こんにちは!エンタープライズクラウド部技術2課の日高です。
先日、私の1年後輩がインタビュー記事を書いてくれましたので、私についてもっと知りたいと思ってくださる方は、ぜひブログをご覧ください!
今回は、CloudFormationを利用してネストされたスタックの作成方法と、JSONファイルを用いたパラメータの設定、さらにAWS CLIでの実行手順について詳しく解説していきます。
イメージが湧きづらい方は、「はじめに」をご覧ください。
題名等で今回やることが理解できた方は、「はじめに」は読み飛ばしていただいて問題ありません。
はじめに
ネストされたスタックとは
AWS CloudFormationを使用すると、インフラストラクチャーをコードとして管理することができます。
この中で「ネストされたスタック」という概念があります。
ネストされたスタックとは、スタックを分割して階層化する特性があります。
上記の図のようにネストされたスタックは、1つの「メインスタック(親スタック)」と1つ以上の「子スタック」から構成されます。
- メインスタック(親スタックやルートスタックとも呼ばれます。): ネストされたスタックの親となるスタックです。このスタックから他の子スタックを参照します。
- 子スタック: メインスタックによって管理されるスタックです。これらは個別にリソースを持ち、個々のスタックとしてデプロイされます。
こちらを図としてまとめると、下記のようなイメージになります。
上記の図の場合、通常「vpc.yml」「ec2-sg.yml」「ec2.yml」を個別に実行する必要があります。
しかし、ネストされたスタックのメインスタックを実行すると、メインスタックが子スタックである「vpc.yml」「ec2-sg.yml」「ec2.yml」を呼び出し実行してくれます。
実際のマネジメントコンソールでネストされたスタックを実行した画面は下記のようになります。
赤枠で囲まれているのがメインスタックです。青枠で囲まれているのが、メインスタックから呼び出された子スタックになります。
上記の図からも分かるように、メインスタックから呼び出された子スタックには「ネストされた」という表記がされています。
本ブログで記載すること
このブログでは、「AWS CLIと事前に設定されたパラメータを含むJsonファイルを使用して、ユーザーがメインスタック(親スタック)をどのように実行するか」について詳しく解説します。
この図に示されている「①ユーザーがメインスタック(親スタック)を実行する」方法には、下記の2つの方法があります。
- マネジメントコンソールを使用する方法
- AWS CLIを使用する方法
マネジメントコンソールを通じて実行する場合、以下のような画面でパラメータを指定する必要があります。
ただ、今回このブログで解説するのはAWS CLIで実行する方法になります。 具体的には、Jsonファイルにパラメータを事前に設定し、それを読み込んでネストされたスタックを実行する方法です。
このプロセスの概念図は以下の通りです。
Jsonファイルをあらかじめ設定することで得られるメリットは以下の通りです。
- マネジメントコンソールで入力する手間が省ける。
- 人間による入力ミスを防ぐことができる(Jsonファイルの設定値が正しい場合)。
- AWS CLIコマンドの実行だけで、迅速に同じ環境を構築できる。
準備(CloudFormationテンプレートとJsonファイル)
下記に記載されているメインスタック(root.yml)、各子スタック(vpc.yml、ec2-sg.yml、ec2.yml)、Jsonファイル(ParameterFile.json)を同一フォルダ内に保存してください。
各子スタックは、独立して実行しやすいように、幅広い用途に適応できるよう汎用的に、また、他のスタックとの密接な依存関係を避けるために疎結合で設計されています。
今回作る構成
上記の構成図の構成を作成します。 EC2にはSSMのセッションマネージャーを利用してログインする想定なので、EC2のセキュリティグループにはインバウンドルールは追加しません。
テンプレート格納用のS3バケットの作成
CloudFormationテンプレートを格納するためのS3バケットを作成してください。
私は「hidaka-cloudformation-template」で作成しました。
詳しくは後述しますが、AWS CLIを実行すると下記の動作をします。
- 子スタックを指定したS3バケットに格納する
- root.ymlに記載している
Properties
のTemplateURL
の値を、格納したS3バケットのオブジェクトURIに書き換える
そのため、前もってS3の準備が必要になります。
メインスタック(root.yml)
AWSTemplateFormatVersion: "2010-09-09" Description: ParentStack Main Parameters: #vpc.ymlのパラメータ指定 VpcCidr: Type: String Description: CIDR block for the VPC PublicSubnetACidr: Type: String Description: CIDR block for the public subnet in AZ A PublicSubnetCCidr: Type: String Description: CIDR block for the public subnet in AZ C ProtectedSubnetACidr: Type: String Description: CIDR block for the protected subnet in AZ A ProtectedSubnetCCidr: Type: String Description: CIDR block for the protected subnet in AZ C PrivateSubnetACidr: Type: String Description: CIDR block for the private subnet in AZ A PrivateSubnetCCidr: Type: String Description: CIDR block for the private subnet in AZ C FlowLogsBucketName: Type: String Description: <System Name>-flow-logs-<Account Number> VpcTagName: Type: String Description: <System Name>-<Environment> VpcApplicationTag: Type: String Description: Application tag for all resources <System Name> #下記よりec2-sg.ymlパラメータを指定。(インバウンドルールを追加する場合はこことec2-sg.ymlのコメントアウト解除し、InfraParameterFile.jsonに各値を追記してください。) Ec2SecurityGroupName: Description: "Enter the name for the Security Group." Type: String Ec2SecurityGroupDescription: Description: "Enter a description for the Security Group." Type: String Ec2SecurityGroupTagValue: Description: "Enter a tag value for the Security Group." Type: String #Ec2IngressCidrIp: #Description: "CIDR block for inbound rule." #Type: String #Default: "0.0.0.0/0" # この値は適切なものに変更してください #Ec2IngressFromPort: #Description: "Start range of the TCP port for inbound rule." #Type: Number #Default: 80 # この値は適切なものに変更してください #Ec2IngressToPort: #Description: "End range of the TCP port for inbound rule." #Type: Number #Default: 80 # この値は適切なものに変更してください #Ec2IngressProtocol: #Description: "Protocol for inbound rule." #Type: String #Default: "tcp" # "tcp"、"udp"、"icmp"、または"-1" (すべてのプロトコルを指定) # 下記よりec2.ymlのパラメータを指定。(EBS複数作成する場合はこことec2.ymlのコメントアウト解除し、InfraParameterFile.jsonにEc2DiskSize2の値を追記してください。) ImageId : Type : "AWS::EC2::Image::Id" Description: Enter Amazon Machine Image ID for Windows. Ec2InstanceTypeParameter: Type: String Default: t3.micro Ec2IamInstanceProfileName: Type: String Ec2InstanceName: Type: String Description: (Required) MaxLength 15.Enter EC2 Instance Name. #Windows の制約上、ホスト名は16文字未満となるため。 MaxLength: 15 MinLength: 1 Ec2DiskSize1: Type: String Description: (Required) Enter the disk size. Default: "30" #Ec2DiskSize2: # Type: String # Description: (Required) Enter the disk size. # Default: "8" Ec2KeyName: Type: String Description: (Required) Enter the Ec2KeyName. Default: "XXXXX" Ec2ApplicationTag: Type: String Description: Application tag for all resources <System Name> ############################################################################################### ####### ここから下Resourcesセクション ######################################################### ############################################################################################### Resources: #vpc.ymlのスタック実行 VpcStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: ./vpc.yml Parameters: VpcCidr: !Ref VpcCidr PublicSubnetACidr: !Ref PublicSubnetACidr PublicSubnetCCidr: !Ref PublicSubnetCCidr ProtectedSubnetACidr: !Ref ProtectedSubnetACidr ProtectedSubnetCCidr: !Ref ProtectedSubnetCCidr PrivateSubnetACidr: !Ref PrivateSubnetACidr PrivateSubnetCCidr: !Ref PrivateSubnetCCidr FlowLogsBucketName: !Ref FlowLogsBucketName VpcTagName: !Ref VpcTagName VpcApplicationTag: !Ref VpcApplicationTag #下記よりec2-sg.ymlのスタック実行(インバウンドルールを追加する場合はこことec2-sg.ymlのコメントアウト解除し、InfraParameterFile.jsonに値を追記してください。) Ec2SgStack: Type: AWS::CloudFormation::Stack DependsOn: - VpcStack Properties: TemplateURL: ./ec2-sg.yml Parameters: VpcId: !GetAtt VpcStack.Outputs.VpcId Ec2SecurityGroupName: !Ref Ec2SecurityGroupName Ec2SecurityGroupDescription: !Ref Ec2SecurityGroupDescription Ec2SecurityGroupTagValue: !Ref Ec2SecurityGroupTagValue #Ec2IngressCidrIp: !Ref Ec2IngressCidrIp #Ec2IngressFromPort: !Ref Ec2IngressFromPort #Ec2IngressToPort: !Ref Ec2IngressToPort #Ec2IngressProtocol: !Ref Ec2IngressProtocol #下記よりec2.ymlのスタック実行 Ec2Stack: Type: AWS::CloudFormation::Stack DependsOn: - VpcStack - Ec2SgStack Properties: TemplateURL: ./ec2.yml Parameters: ImageId : !Ref ImageId Ec2InstanceTypeParameter: !Ref Ec2InstanceTypeParameter Ec2IamInstanceProfileName: !Ref Ec2IamInstanceProfileName Ec2InstanceName: !Ref Ec2InstanceName Ec2DiskSize1: !Ref Ec2DiskSize1 #Ec2DiskSize2: !Ref Ec2DiskSize2 Ec2SubnetIdForEth0: !GetAtt VpcStack.Outputs.SubnetProtectedAId Ec2SecurityGroupIdsForEth0: !GetAtt Ec2SgStack.Outputs.SecurityGroupId Ec2KeyName: !Ref Ec2KeyName Ec2ApplicationTag: !Ref Ec2ApplicationTag
子スタック①(vpc.yml)
AWSTemplateFormatVersion: '2010-09-09' Description: NestedStack For root.yml Parameters: VpcCidr: Type: String Description: CIDR block for the VPC PublicSubnetACidr: Type: String Description: CIDR block for the public subnet in AZ A PublicSubnetCCidr: Type: String Description: CIDR block for the public subnet in AZ C ProtectedSubnetACidr: Type: String Description: CIDR block for the protected subnet in AZ A ProtectedSubnetCCidr: Type: String Description: CIDR block for the protected subnet in AZ C PrivateSubnetACidr: Type: String Description: CIDR block for the private subnet in AZ A PrivateSubnetCCidr: Type: String Description: CIDR block for the private subnet in AZ C FlowLogsBucketName: Type: String Description: <System Name>-flow-logs-<Account Number> VpcTagName: Type: String Description: <System Name>-<Environment> VpcApplicationTag: Type: String Description: Application tag for all resources <System Name> Resources: # ここからVPCとVPCフローログの定義。 Vpc: Type: 'AWS::EC2::VPC' DeletionPolicy: Delete Properties: CidrBlock: !Ref VpcCidr EnableDnsHostnames: 'true' EnableDnsSupport: 'true' Tags: - Key: Name Value: !Sub '${VpcTagName}-vpc' - Key: Application Value: !Ref VpcApplicationTag S3BucketForFlowLogs: Type: 'AWS::S3::Bucket' DeletionPolicy: Delete Properties: BucketName: !Ref FlowLogsBucketName AccessControl: Private Tags: - Key: Application Value: !Ref VpcApplicationTag VPCFlowLog: Type: 'AWS::EC2::FlowLog' DeletionPolicy: Delete Properties: LogDestinationType: s3 LogDestination: !Sub 'arn:aws:s3:::${S3BucketForFlowLogs}' ResourceId: !Ref Vpc ResourceType: VPC TrafficType: ALL Tags: - Key: Name Value: !Sub '${VpcTagName}-log' - Key: Application Value: !Ref VpcApplicationTag # ここからサブネットの定義。各サブネットはパラメータ化されたCIDRブロックを使用 SubnetPublicA: Type: 'AWS::EC2::Subnet' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc AvailabilityZone: ap-northeast-1a CidrBlock: !Ref PublicSubnetACidr MapPublicIpOnLaunch: 'false' Tags: - Key: Name Value: !Sub '${VpcTagName}-public-1a' - Key: Application Value: !Ref VpcApplicationTag SubnetPublicC: Type: 'AWS::EC2::Subnet' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc AvailabilityZone: ap-northeast-1c CidrBlock: !Ref PublicSubnetCCidr MapPublicIpOnLaunch: 'false' Tags: - Key: Name Value: !Sub '${VpcTagName}-public-1c' - Key: Application Value: !Ref VpcApplicationTag SubnetProtectedA: Type: 'AWS::EC2::Subnet' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc AvailabilityZone: ap-northeast-1a CidrBlock: !Ref ProtectedSubnetACidr MapPublicIpOnLaunch: 'false' Tags: - Key: Name Value: !Sub '${VpcTagName}-protected-1a' - Key: Application Value: !Ref VpcApplicationTag SubnetProtectedC: Type: 'AWS::EC2::Subnet' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc AvailabilityZone: ap-northeast-1c CidrBlock: !Ref ProtectedSubnetCCidr MapPublicIpOnLaunch: 'false' Tags: - Key: Name Value: !Sub '${VpcTagName}-protected-1c' - Key: Application Value: !Ref VpcApplicationTag SubnetPrivateA: Type: 'AWS::EC2::Subnet' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc AvailabilityZone: ap-northeast-1a CidrBlock: !Ref PrivateSubnetACidr MapPublicIpOnLaunch: 'false' Tags: - Key: Name Value: !Sub '${VpcTagName}-private-1a' - Key: Application Value: !Ref VpcApplicationTag SubnetPrivateC: Type: 'AWS::EC2::Subnet' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc AvailabilityZone: ap-northeast-1c CidrBlock: !Ref PrivateSubnetCCidr MapPublicIpOnLaunch: 'false' Tags: - Key: Name Value: !Sub '${VpcTagName}-private-1c' - Key: Application Value: !Ref VpcApplicationTag # ここからIGWとNGWの定義 Igw: Type: 'AWS::EC2::InternetGateway' DeletionPolicy: Delete Properties: Tags: - Key: Name Value: !Sub '${VpcTagName}-igw' - Key: Application Value: !Ref VpcApplicationTag IgwAttach: Type: 'AWS::EC2::VPCGatewayAttachment' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc InternetGatewayId: !Ref Igw NgwA: Type: 'AWS::EC2::NatGateway' DeletionPolicy: Delete Properties: AllocationId: !GetAtt EipNgwA.AllocationId SubnetId: !Ref SubnetPublicA Tags: - Key: Name Value: !Sub '${VpcTagName}-ngw-a' - Key: Application Value: !Ref VpcApplicationTag EipNgwA: Type: 'AWS::EC2::EIP' DeletionPolicy: Delete Properties: Domain: vpc Tags: - Key: Name Value: !Sub '${VpcTagName}-ngw-a-eip' - Key: Application Value: !Ref VpcApplicationTag # 2つNGWを作る場合は下記のコメントアウトを解除。 # NgwC: # Type: 'AWS::EC2::NatGateway' # DeletionPolicy: Delete # Properties: # AllocationId: !GetAtt EipNgwC.AllocationId # SubnetId: !Ref SubnetPublicC # Tags: # - Key: Name # Value: !Sub '${VpcTagName}-ngw-c' # - Key: Application # Value: !Ref VpcApplicationTag # EipNgwC: # Type: 'AWS::EC2::EIP' # DeletionPolicy: Delete # Properties: # Domain: vpc # Tags: # - Key: Name # Value: !Sub '${VpcTagName}-ngw-c-eip' # - Key: Application # Value: !Ref VpcApplicationTag #ここからRtbの定義 RtbPublic: Type: 'AWS::EC2::RouteTable' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub '${VpcTagName}-public-rtb' - Key: Application Value: !Ref VpcApplicationTag RtbPublicRoute0: Type: 'AWS::EC2::Route' DeletionPolicy: Delete DependsOn: IgwAttach Properties: RouteTableId: !Ref RtbPublic DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref Igw RtbPrivate: Type: 'AWS::EC2::RouteTable' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub '${VpcTagName}-private-rtb' - Key: Application Value: !Ref VpcApplicationTag RtbProtectedA: Type: 'AWS::EC2::RouteTable' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub '${VpcTagName}-protected-a-rtb' - Key: Application Value: !Ref VpcApplicationTag RtbProtectedARoute0: Type: 'AWS::EC2::Route' DeletionPolicy: Delete Properties: RouteTableId: !Ref RtbProtectedA DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NgwA RtbProtectedC: Type: 'AWS::EC2::RouteTable' DeletionPolicy: Delete Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub '${VpcTagName}-protected-c-rtb' - Key: Application Value: !Ref VpcApplicationTag RtbProtectedCRoute0: Type: 'AWS::EC2::Route' DeletionPolicy: Delete Properties: RouteTableId: !Ref RtbProtectedC DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NgwA #NgwCを作成する場合、NgwCに変更する。 # サブネットとルートテーブルの関連付け SubnetRouteTableAssociationSubnetPublicA: Type: 'AWS::EC2::SubnetRouteTableAssociation' DeletionPolicy: Delete Properties: SubnetId: !Ref SubnetPublicA RouteTableId: !Ref RtbPublic SubnetRouteTableAssociationSubnetPublicC: Type: 'AWS::EC2::SubnetRouteTableAssociation' DeletionPolicy: Delete Properties: SubnetId: !Ref SubnetPublicC RouteTableId: !Ref RtbPublic SubnetRouteTableAssociationSubnetPrivateA: Type: 'AWS::EC2::SubnetRouteTableAssociation' DeletionPolicy: Delete Properties: SubnetId: !Ref SubnetPrivateA RouteTableId: !Ref RtbPrivate SubnetRouteTableAssociationSubnetPrivateC: Type: 'AWS::EC2::SubnetRouteTableAssociation' DeletionPolicy: Delete Properties: SubnetId: !Ref SubnetPrivateC RouteTableId: !Ref RtbPrivate SubnetRouteTableAssociationSubnetProtectedA: Type: 'AWS::EC2::SubnetRouteTableAssociation' DeletionPolicy: Delete Properties: SubnetId: !Ref SubnetProtectedA RouteTableId: !Ref RtbProtectedA SubnetRouteTableAssociationSubnetProtectedC: Type: 'AWS::EC2::SubnetRouteTableAssociation' DeletionPolicy: Delete Properties: SubnetId: !Ref SubnetProtectedC RouteTableId: !Ref RtbProtectedC Outputs: VpcId: Description: The ID of the created VPC Value: !Ref Vpc Export: Name: !Sub "${AWS::StackName}-VpcId" SubnetPublicAId: Description: The ID of the public subnet in AZ A Value: !Ref SubnetPublicA Export: Name: !Sub "${AWS::StackName}-SubnetPublicAId" SubnetPublicCId: Description: The ID of the public subnet in AZ C Value: !Ref SubnetPublicC Export: Name: !Sub "${AWS::StackName}-SubnetPublicCId" SubnetProtectedAId: Description: The ID of the protected subnet in AZ A Value: !Ref SubnetProtectedA Export: Name: !Sub "${AWS::StackName}-SubnetProtectedAId" SubnetProtectedCId: Description: The ID of the protected subnet in AZ C Value: !Ref SubnetProtectedC Export: Name: !Sub "${AWS::StackName}-SubnetProtectedCId" SubnetPrivateAId: Description: The ID of the private subnet in AZ A Value: !Ref SubnetPrivateA Export: Name: !Sub "${AWS::StackName}-SubnetPrivateAId" SubnetPrivateCId: Description: The ID of the private subnet in AZ C Value: !Ref SubnetPrivateC Export: Name: !Sub "${AWS::StackName}-SubnetPrivateCId"
子スタック②(ec2-sg.yml)
AWSTemplateFormatVersion: "2010-09-09" Description: NestedStack For root.yml Parameters: VpcId: Description: "Required. Select VPC ID" Type: String Ec2SecurityGroupName: Description: "Enter the name for the Security Group." Type: String Ec2SecurityGroupDescription: Description: "Enter a description for the Security Group." Type: String Ec2SecurityGroupTagValue: Description: "Enter a tag value for the Security Group." Type: String #Ec2IngressCidrIp: #インバウンド通信を許可したい場合コメント解除してください。 #Description: "CIDR block for inbound rule." #Type: String #Default: "0.0.0.0/0" # この値は適切なものに変更してください #Ec2IngressFromPort: #Description: "Start range of the TCP port for inbound rule." #Type: Number #Default: 80 # この値は適切なものに変更してください #Ec2IngressToPort: #Description: "End range of the TCP port for inbound rule." #Type: Number #Default: 80 # この値は適切なものに変更してください #Ec2IngressProtocol: #Description: "Protocol for inbound rule." #Type: String #Default: "tcp" # "tcp"、"udp"、"icmp"、または"-1" (すべてのプロトコルを指定) Resources: Ec2Sg: Type: "AWS::EC2::SecurityGroup" DeletionPolicy: "Delete" Properties: GroupDescription: !Ref Ec2SecurityGroupDescription GroupName: !Ref Ec2SecurityGroupName Tags: - Key: "Name" Value: !Ref Ec2SecurityGroupTagValue VpcId: !Ref VpcId #Ec2SgIngress0: #Type: "AWS::EC2::SecurityGroupIngress" #DeletionPolicy: "Delete" #Properties: #CidrIp: !Ref Ec2IngressCidrIp #Description: "Inbound access" #FromPort: !Ref Ec2IngressFromPort #GroupId: !Ref Ec2Sg #IpProtocol: !Ref Ec2IngressProtocol #ToPort: !Ref Ec2IngressToPort Ec2SgEgress0: Type: "AWS::EC2::SecurityGroupEgress" DeletionPolicy: "Delete" Properties: CidrIp: "0.0.0.0/0" Description: "No limit access to anywhere" FromPort: 0 GroupId: !Ref Ec2Sg IpProtocol: "-1" ToPort: 65535 Outputs: SecurityGroupId: Description: "The ID of the created Security Group" Value: !Ref Ec2Sg Export: Name: Ec2SecurityGroupIdExport
子スタック③(ec2.yml)
AWSTemplateFormatVersion: '2010-09-09' Description: NestedStack Forroot.yml Parameters: ImageId : Type : "AWS::EC2::Image::Id" Description: Enter Amazon Machine Image ID for Windows. Ec2InstanceTypeParameter: Type: String Default: t3.micro Ec2IamInstanceProfileName: Type: String Ec2InstanceName: Type: String Description: (Required) MaxLength 15.Enter EC2 Instance Name. #Windows の制約上、ホスト名は16文字未満となるため。 MaxLength: 15 MinLength: 1 Ec2DiskSize1: Type: String Description: (Required) Enter the disk size. Default: "30" #Ec2DiskSize2: # Type: String # Description: (Required) Enter the disk size. # Default: "8" Ec2SubnetIdForEth0: Type: "AWS::EC2::Subnet::Id" Description: "Required. Select SubnetId in execution environment!" Ec2SecurityGroupIdsForEth0: Type: "List<AWS::EC2::SecurityGroup::Id>" Description: "Required. Select SecurityGroup in execution environment!" Ec2KeyName: Type: String Description: (Required) Enter the Ec2KeyName. Default: "XXXXX" Ec2ApplicationTag: Type: String Description: Application tag for all resources <System Name> Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "Basic Configuration" Parameters: - "Ec2InstanceName" - "Ec2InstanceTypeParameter" - "InstanceType" - "ImageId" - "Ec2IamInstanceProfileName" - "Ec2DiskSize1" #- "Ec2DiskSize2" - "Ec2KeyName" - Label: default: "NetworkInterface Configuration For Eth0" Parameters: - "Ec2SubnetIdForEth0" - "Ec2SecurityGroupIdsForEth0" Resources: ## 既存キーペア利用時は以下KeyPairは不要のためコメントアウトする。 KeyPair: Type: "AWS::EC2::KeyPair" Properties: KeyName: Ref: "Ec2KeyName" KeyType: rsa DeletionPolicy: Retain UpdateReplacePolicy: Retain EC2Instance: Type: "AWS::EC2::Instance" DeletionPolicy: "Delete" Properties: BlockDeviceMappings: - DeviceName: "/dev/sda1" Ebs: DeleteOnTermination: true Encrypted: false VolumeSize: Ref: "Ec2DiskSize1" VolumeType: "gp3" ## EBSを複数使用する場合は以下2つ目のDeviceNameを使用。 #- DeviceName: "/dev/sdf" # Ebs: # DeleteOnTermination: true # Encrypted: false # VolumeSize: # Ref: "Ec2DiskSize2" # VolumeType: "gp3" #CreditSpecification: # バーストパフォーマンスUnlimited無効化。T系インスタンスタイプ以外はサポートしない項目。 #CPUCredits: standard DisableApiTermination: true EbsOptimized: true # EBS最適化 IamInstanceProfile: Ref: "Ec2IamInstanceProfileName" ImageId: Ref: "ImageId" InstanceInitiatedShutdownBehavior: "stop" InstanceType: Ref: "Ec2InstanceTypeParameter" Monitoring: false KeyName: !Ref KeyPair NetworkInterfaces: - AssociatePublicIpAddress: false DeleteOnTermination: true #Eth0は、DeleteOnTermination:trueでなければローンチできないため、強制的にtrueを設定。falseの場合、"A network interface without a network interface ID must have delete on termination as true"となるため。 DeviceIndex: 0 GroupSet: Ref: "Ec2SecurityGroupIdsForEth0" SubnetId: Ref: "Ec2SubnetIdForEth0" SourceDestCheck: true Tags: - Key: Name Value: Ref: Ec2InstanceName - Key: Application Value: !Ref Ec2ApplicationTag Tenancy: "default" LaunchTemplate: LaunchTemplateId: !Ref LaunchTemplate01 Version: 1 LaunchTemplate01: ## EBS,ENIにタグ付与 Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateData: MetadataOptions: # セキュリティ強化のためIMDSv2のみ有効を標準とする HttpTokens: required TagSpecifications: - ResourceType: volume Tags: - Key: Name Value: Ref: Ec2InstanceName - Key: Application Value: !Ref Ec2ApplicationTag - ResourceType: network-interface Tags: - Key: Name Value: Ref: Ec2InstanceName - Key: Application Value: !Ref Ec2ApplicationTag Outputs: EC2InstanceId: Description: The ID of the created EC2 Instance Value: !Ref EC2Instance Export: Name: Ec2InstanceIdExport
Jsonファイル(ParameterFile.json)
[ { "ParameterKey": "VpcCidr", "ParameterValue": "10.0.0.0/16" }, { "ParameterKey": "PublicSubnetACidr", "ParameterValue": "10.0.1.0/24" }, { "ParameterKey": "PublicSubnetCCidr", "ParameterValue": "10.0.2.0/24" }, { "ParameterKey": "ProtectedSubnetACidr", "ParameterValue": "10.0.3.0/24" }, { "ParameterKey": "ProtectedSubnetCCidr", "ParameterValue": "10.0.4.0/24" }, { "ParameterKey": "PrivateSubnetACidr", "ParameterValue": "10.0.5.0/24" }, { "ParameterKey": "PrivateSubnetCCidr", "ParameterValue": "10.0.6.0/24" }, { "ParameterKey": "FlowLogsBucketName", "ParameterValue": "<実際に起動したいVPCフローログを保存するS3バケット名に書き換えてください>" }, { "ParameterKey": "VpcTagName", "ParameterValue": "<実際に起動したいVPC名に書き換えてください>" }, { "ParameterKey": "VpcApplicationTag", "ParameterValue": "<アプリケーションタグに指定したい値に書き換えてください>" }, { "ParameterKey": "Ec2SecurityGroupName", "ParameterValue": "<実際に起動したいセキュリティグループ名に書き換えてください>" }, { "ParameterKey": "Ec2SecurityGroupDescription", "ParameterValue": "<実際に起動したいセキュリティグループ名の説明に書き換えてください>" }, { "ParameterKey": "Ec2SecurityGroupTagValue", "ParameterValue": "<実際に起動したいセキュリティグループ名に書き換えてください>" }, { "ParameterKey": "ImageId", "ParameterValue": "<実際に起動するAMI IDに書き換えてください>" }, { "ParameterKey": "Ec2InstanceTypeParameter", "ParameterValue": "<実際に起動したいインスタンスタイプに書き換えてください>" }, { "ParameterKey": "Ec2IamInstanceProfileName", "ParameterValue": "<実際に紐づけるIAMロール名に書き換えてください>" }, { "ParameterKey": "Ec2InstanceName", "ParameterValue": "<実際に起動したいインスタンス名に書き換えてください>" }, { "ParameterKey": "Ec2DiskSize1", "ParameterValue": "65" }, { "ParameterKey": "Ec2KeyName", "ParameterValue": "<実際に起動したいキーペア名に書き換えてください>" }, { "ParameterKey": "Ec2ApplicationTag", "ParameterValue": "<アプリケーションタグに指定したい値に書き換えてください>" } ]
実行コマンド
前提
「準備」にて記載した内容を全て保存していると下記のようなフォルダ構成になっているはずです。
<フォルダ名> - root.yml - vpc.yml - ec2-sg.yml - ec2.yml - ParameterFile.json
①テンプレートを保存したディレクトリ配下に移動します。
cd <ディレクトリ名>
私はblogディレクトリ配下にテンプレートを保存したので、下記のように移動します。
②子スタックをパッケージングする。
子スタックをパッケージングします。
この段階でTemplateURLが置き換わって作成されます。
aws cloudformation package --template-file <メインスタック名> --output-template-file packaged-<メインスタック名> --s3-bucket <子スタックを保存するS3バケット名> <コマンド例> aws cloudformation package --template-file root.yml --output-template-file packaged-root.yml --s3-bucket hidaka-cloudformation-template
※aws cloudformation packageについて詳しくは以下をご覧ください。
コマンドを実行すると、--output-template-file
で指定した「packaged-root.yml」という名前のテンプレートができています。
「packaged-root.yml」の中を見てみると、下記のようにTemplateURL
が置き換わっていました。
- packaged-root.yml
- root.yml
③メインスタックを即時実行させる。
②で作成したメインスタックを実行させます。
aws cloudformation deploy --template-file packaged-<メインスタック名> --stack-name <スタック名> --capabilities CAPABILITY_NAMED_IAM --parameter-overrides file://<パラメータを指定しているJsonファイル名> <コマンド例> aws cloudformation deploy --template-file packaged-root.yml --stack-name test --capabilities CAPABILITY_NAMED_IAM --parameter-overrides file://ParameterFile.json
※aws cloudformation deployについて詳しくは以下をご覧ください。
実際にコマンドを実行すると下記のような表示がされます。
CloudFormationのマネジメントコンソールに移動するとネストされたスタックが実行されています。
では①〜③のコマンドを実行した後、どのように動作しているのか。
「コマンド実行後イメージ」について記載していきます。
コマンド実行後イメージ
AWS CLIコマンドで実行すると上記の図のように実行されます。
※あくまでイメージなので厳密な実行プロセスは違う可能性があります。
- メインスタックが、ParameterFile.jsonから設定するパラメータを読み込む。
- メインスタック内のResources句に、ParameterFile.jsonlから受け取った値を渡す。
- メインスタックのProperties句が実行する子スタックを呼び出す。
- 子スタックのParameters句にParameterFile.jsonlから受け取った値を渡す。
あとは、メインスタックで指定している子スタック分③、④が繰り返されるイメージです。
下記の画面のようになっていたら無事成功しています。
まとめ
CloudFormationを使用してネストされたスタックを作成し、JSONファイルでパラメータを設定してAWS CLIで実行する方法についてブログを記載しました。
かなりややこしく理解に時間がかかったので、何回も読み返していただけると幸いです。
本記事が誰かの助けになっていれば幸いです。
日高 僚太(執筆記事の一覧)
2024 Japan AWS Jr. Champions / 2024 Japan AWS All Certifications Engineers
EC部クラウドコンサルティング課所属。2022年IT未経験でSWXへ新卒入社。
記事に関するお問い合わせや修正依頼⇒ hidaka@serverworks.co.jp