本連載では、以下のイメージの構成にあるAWSリソース基盤自動化環境の構築を実践しています。

本連載で構築していく基盤自動化環境のイメージ

前回は、S3およびSQSを構築するテンプレートを実装しました。続く今回は、これまで実装してきたテンプレートの親となるネストテンプレートを作成する方法を紹介します。

なお、実際のソースコードはGitHub上にコミットしています。以降のソースコードでは本質的でない記述を一部省略しているので、実行コードを作成する場合は、必要に応じて適宜GitHub上のソースコードも参照してください。

ネストテンプレートの作成

第21回で、CloudFormationがサポートする機能として、複数のテンプレートでネスト(親子)を構成する「NestedStack」があることを紹介しました。NestedStackはテンプレートを分割したり、複数のテンプレートをまとめて実行したいとき、共通のパラメータや定義を複数のテンプレートで共有したいときに利用します。ただし、制約として参照するテンプレートをS3へアップロードしておく必要があります。

NestedStackを使用する場合、リソースタイプが、AWS::CloudFormation::Stackであるリソース定義を使用します。プロパティとして設定可能な属性は、上記リンク先の通りですが、VPCとサブネット、インターネットゲートウェイ、セキュリティグループについては既に構築済みであることを前提とします。

今回はこれまでの連載で実装してきたRDS、ElastiCache、S3、SQSを構築するテンプレートを子テンプレートとして、開発環境として1つにまとめて実行するための親テンプレートを作成してみます。テンプレートのサンプルは以下の通りです。

AWSTemplateFormatVersion: '2010-09-09'

Description: Sample CloudFormation template with YAML - parent stack for dev environment.

Parameters:
  VPCName:
    Description: Target VPC Stack Name
    Type: String
    MinLength: 1
    MaxLength: 255
    AllowedPattern: ^[a-zA-Z][-a-zA-Z0-9]*$
    Default: mynavi-sample-cloudformation-vpc
  EnvType:                                                               #(A)
    Description: Which environments to deploy your service.
    Type: String
    AllowedValues: ["Dev"]
    Default: Dev
  LocalhostIpAddress:                                                    #(B)
    Description: CiderBlock paramater for VPC
    Type: String
    MinLength: 9
    MaxLength: 18
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    Default: 172.200.2.58/32

Resources:
  RDSDevStack:                                                           #(C)
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./sample-rds-cfn.yml                                  #(D)
      Parameters:                                                        #(E)
        VPCName: !Sub ${VPCName}
        EnvType: !Sub ${EnvType}
        SecurityGroupDev: !Ref SecurityGroupRdsPostgresForDev

  DynamoDBDevStack:                                                      #(F)
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./sample-dynamodb-cfn.yml
      Parameters:
        VPCName: !Sub ${VPCName}
        EnvType: !Sub ${EnvType}

# ElastiCache is not permitted public access, use local redis except dev environmant in vpc.
  ElastiCacheDevStack:                                                   #(G)
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./sample-elasticache-cfn.yml
      Parameters:
        VPCName: !Sub ${VPCName}
        EnvType: !Sub ${EnvType}
        SecurityGroupDev: !Ref SecurityGroupElastiCacheRedisForDev

  S3DevStack:                                                            #(H)
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./sample-s3-cfn.yml
      Parameters:
        EnvType: !Sub ${EnvType}

  SQSDevStack:                                                           #(I)
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./sample-sqs-cfn.yml
      Parameters:
        EnvType: !Sub ${EnvType}

  SecurityGroupRdsPostgresForDev:                                        #(J)
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: SecurityGroupRdsPostgresForDev
      GroupDescription: db access only local machine for dev
      VpcId:
        Fn::ImportValue: !Sub ${VPCName}-VPCID
      Tags:
        - Key: Name
          Value: !Sub ${VPCName}-SecurityGroupRdsPostgresForDev

  SecurityGroupIngressRdsPostgresForDev:                                 #(K)
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref SecurityGroupRdsPostgresForDev
      IpProtocol: tcp
      FromPort: 5432
      ToPort: 5432
      CidrIp: !Ref LocalhostIpAddress

  SecurityGroupElastiCacheRedisForDev:                                   #(L)
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: SecurityGroupElastiCacheForDev
      GroupDescription: elasticache access only local machine for dev
      VpcId:
        Fn::ImportValue: !Sub ${VPCName}-VPCID
      Tags:
        - Key: Name
          Value: !Sub ${VPCName}-SecurityGroupElastiCacheRedisForDev

  SecurityGroupIngressElastiCacheRedisForDev:                            #(M)
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref SecurityGroupElastiCacheRedisForDev
      IpProtocol: tcp
      FromPort: 6379
      ToPort: 6379
      CidrIp: !Ref LocalhostIpAddress

NestedStackとして作成した親テンプレートの記述の基本となるポイントは下表の通りです。

記述 説明
A パラメータとして、EnvTypeを指定します。今回は開発環境向けのテンプレートとするため、デフォルト値”Dev”のみを許容する設定としておきます
B パラメータとして、LocalhostIpAddressを指定します。開発環境の端末のIPアドレスを指定して、RDSおよびElastiCacheへのアクセスを許可するセキュリティグループへ渡す用途で使用します
C RDSを構築するテンプレートを開発環境用スタックとしてリソース定義します
D 以前実装したRDSテンプレートの相対パスを指定します。本来、S3にテンプレートをアップロードしてそのオブジェクトキーを指定しなければなりませんが、後述の手順でパスを置き換えるコマンドを実行します
E 親テンプレートから子テンプレートに渡すパラメータを指定します。子テンプレートに定義したParametersと同じ論理名を指定し、渡すパラメータを設定します
F DynamoDBを構築するテンプレートを開発環境用スタックとしてリソース定義します。子テンプレートに渡すパラメータの定義方法はEと同様です
G ElastiCacheを構築するテンプレートを開発環境用スタックとしてリソース定義します。子テンプレートに渡すパラメータの定義方法はEと同様です。なお、第31回でも記載しましたが、ElastiCacheのアクセスはVPC内にある開発端末にかぎりアクセスが許可されます
H S3を構築するテンプレートを開発環境用スタックとしてリソース定義します。子テンプレートに渡すパラメータの定義方法はEと同様です
I SQSを構築するテンプレートを開発環境用スタックとしてリソース定義します。子テンプレートに渡すパラメータの定義方法はEと同様です
J Eで定義したパラメータとして渡すセキュリティグループを定義します
K Jで定義した(RDSへのアクセスを許可する)セキュリティグループのインバウンド接続ルールを定義します
L Gで定義したパラメータとして渡すセキュリティグループを定義します
M Lで定義した(ElastiCacheへのアクセスを許可する)セキュリティグループのインバウンド接続ルールを定義します

なお、生成するリソースの実行順序を制御するにはDependsOnプロパティを使用します。上記では、RDSやElastiCacheで使用するセキュリティグループを前もって作成する必要がありますが、リソース定義自体でセキュリティグループが参照されているため、自動でCloudFormationが実行順序を判断するので明示的に定義する必要はありません。

また、子テンプレートのパラメータを親テンプレートで参照したい場合は、子テンプレートでOutputs要素にパラメータを定義した後、GetAtt関数で参照します。例えば、子テンプレートに定義したRDSの開発環境のエンドポイントを参照したい場合、”!GetAtt RDSDevStack.Outputs.RDSDevInstanceEndPoint”となります。

テンプレートの生成

作成したテンプレートで指定した子テンプレートのURLは、本来S3にアップロードしてそのオブジェクトキーを指定しなければなりません。AWS CLIの「aws cloudformation package」コマンドで、特定のS3バケットを指定して実行することで、バケットへアップロードし、URLをオブジェクトキーに置き換えたテンプレートを生成できます。

事前にアップロード先のバケットを作成した上で(ここでは連載「AWSで作るクラウドネイティブアプリケーションの基本」の第25回と同様の手順で、「debugroom-mynavi-sample-cloudformation-package」というバケットを事前に作成しておきます)、パッケージを実行するヘルパースクリプトを以下のように作成して実行します。

#!/usr/bin/env bash

template_path="sample-infra-dev-cfn.yml"
output_template="sample-infra-dev-package-cfn.yml"
s3_bucket="debugroom-mynavi-sample-cloudformation-package"

aws cloudformation package --template-file ${template_path} --s3-bucket ${s3_bucket} --output-template-file ${output_template}

実行が正常に終了すると、URLのパスが置き換わったテンプレートが生成されます。

AWSTemplateFormatVersion: '2010-09-09'

#omit

Resources:
  RDSDevStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3.ap-northeast-1.amazonaws.com/debugroom-mynavi-sample-cloudformation-package/85f621985a23d88cd5c0be512e717b3f.template
      Parameters:
        VPCName:
          Fn::Sub: ${VPCName}
        EnvType:
          Fn::Sub: ${EnvType}
        SecurityGroupDev:
          Ref: SecurityGroupRdsPostgresForDev

#omit

生成されたテンプレートを以下のようにヘルパースクリプトを用いて実行すると、定義したリソースがまとめて実行されます。

#!/usr/bin/env bash

stack_name="mynavi-sample-infra-dev"
template_path="sample-infra-dev-package-cfn.yml"

parameters="EnvType=Dev"

aws cloudformation deploy --stack-name ${stack_name} --template-file ${template_path} --parameter-overrides ${parameters} --capabilities CAPABILITY_IAM

定義したリソースがまとめて実行される

以上、今回はNestedStackを使い、複数のリソースをまとめて一括構築するCloudFormationテンプレートを実装しました。次回は、CloudFormationを使って作成したリソースの情報にアクセスするSpringCloudAWSアプリケーションの実装方法について解説を進めていきます。

著者紹介


川畑 光平(KAWABATA Kohei) - NTTデータ 課長代理

金融機関システム業務アプリケーション開発・システム基盤担当を経て、現在はソフトウェア開発自動化関連の研究開発・推進に従事。

Red Hat Certified Engineer、Pivotal Certified Spring Professional、AWS Certified Solutions Architect Professional等の資格を持ち、アプリケーション基盤・クラウドなどさまざまな開発プロジェクト支援にも携わる。2019 APN AWS Top Engineers & Ambassadors選出。

本連載の内容に対するご意見・ご質問は Facebook まで。