こんにちは!エンタープライズクラウド部技術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 / Japan AWS Jr. Champions of the Year 2024
EC部クラウドコンサルティング1課所属。2022年IT未経験でSWXへ新卒入社。
記事に関するお問い合わせや修正依頼⇒ hidaka@serverworks.co.jp