はじめに
本記事でも何度か紹介しているAWS Black Belt Online Seminar
ですが、また新しいセミナーが公開されたようです。
3か月くらい前にもCloudFormationのセミナーが公開されていましたが、それだけCloudFormation
の需要があるということなんでしょうね。
その6ではTransform
を使用した他テンプレートのインクルードを試しましたが、今回はスタックをネスト構成
にしてテンプレートを分割してみようと思います。
- 【前】CloudFormationをゼロから勉強する。(その7:変更セットとドリフト検出)
- 【次】CloudFormationをゼロから勉強する。(その9:クロススタックリファレンス)
- 【番外編】CloudFormationをゼロから勉強する。(番外編:AWS Perspective)
- 【番外編】CloudFormationをゼロから勉強する。(番外編:Former2)
ネストとは
イメージとしてはその6のTransform
でのテンプレート分割と同じような方法となりますが、Transform
のインクルードはベーステンプレートの一部を別ファイルにして呼び出すイメージなので、実行単位であるスタックとしては1スタックであるのに対して、ネスト
は他のスタックを呼び出すイメージとなるので、実行時の見え方は複数スタックとなります。
共通コンポーネント用スタックを作成して、個々のスタックから呼び出すようにするのがAWSのベストプラクティスとのことです。
・・・とはいえTransform
のインクルードでも書き方次第でネスト
した場合と同じことができそうなのでいまいち区別がし辛いですが、ネスト
の場合はその6で書いたようなyamlの書き方の制約は無いので、Transform
のインクルードはリソースの一部パラメータ(例えばタグ)を分割したりするのに使い、ネスト
はリソース自体を分割したりするのに使うといった区別が良いかと思います。
ネストの書式
Resources
セクションにAWS::CloudFormation::Stack
タイプのリソースを作成することでネストしたスタックを指定できます。
ネストしたファイル名を指定する必要があるため、Properties
でTemplateURL
の指定が必須となります。
また、S3バケット
へのアップロードが必要となるため、Transform
でのインクルードと同様、実行する前にpackage
コマンドでURLの変換、S3バケット
へのアップロードを行う必要があります。
Resources:
NestStack1:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./vpc.yaml
ネストしたスタックへの入出力
ネストのスタックは親子関係となるため、親スタック
から子スタック
を呼び出す構成になります。
入出力の関係は子スタック
をプログラミング言語の関数として考えるとイメージが湧きやすいと思いますが、親スタック
で子スタック
からの入出力を受ける場合、子スタック
(という関数)に引数を渡すのが入力で、子スタック
(という関数)からの戻り値が出力となります。
子スタック側での入出力書式
これはネストしていない場合と同じくParameters
セクションで上位からの入力を受けて、Outputs
で結果を上位へ渡します。
親スタック側での入力書式
親スタック
から子スタック
へ値を入力するためにはネストするスタックのProperties
にParameters
を記載して子スタック
に渡す値を記載する必要があります。
上述の例で言うと、親スタック
から子スタック
(という関数)へ引数を渡す方法が、Properties
のParameters
となります。
Resources:
NestStack1: ★子スタック名
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./vpc.yaml
Parameters:
NestVPCRange: !Ref VPCRange
NestSubnetRange1: !Ref SubnetRange1
親スタック側での出力書式
子スタック
(という関数)からの戻り値は!GetAtt組み込み関数
で以下の書式で記載することで取得できます。
!GetAtt [子スタック名].Outputs.[子スタック側のOutputリソース名]
例えば親スタック
で前項目の子スタックへの入力値指定例の設定、子スタック
で以下のような出力設定を行ったとします。
Outputs:
NestSubnet1Id: ★Outputリソース名
Value: !Ref EC2Subnet1
その場合、子スタック
の出力値(戻り値)を取得するためには以下の指定を行うことで取得できます。
!GetAtt NestStack1.Outputs.NestSubnet1Id
ネストスタック間の入出力まとめ
ネストスタック間の入出力について、AWSマネジメントコンソール画面からの入出力も含めたイメージ図を描いてみました。
青が子スタック
とのやり取り、赤が自スタック
でのやり取りとなります。
基本のイメージが分かれば、図のような複雑なスタック構成でも悩むことはないかと思いますが、注意点として、各スタック間の値の入出力は単純なネストの記述ではスタックを飛び越してやり取りできません。
そのため、例えば図中のNestStack2
の出力値をNestStack3
で使用したい場合は、まずNestStack1
のOutput
でNestStack2
の戻り値をRoot Stack
に渡し、次にRoot Stack
からNestStack3
のParameters
でNestStack3
に値を引き渡す必要があります。
もしスタックを飛び越してやり取りしたい場合は、Output
セクションで値をExport
すればやり取りすることが可能なようです。
※Export
する方法(Cross Stack Reference
)は次回試そうと思います。
今回作成する構成
今回は以下のような構成を作ってみようと思います。
ルートスタック用テンプレートの作成
各子スタック
で使用するVPCRange
、SubnetRange1
、SubnetRange2
を読み込み、各スタックの入力に引き渡します。
また、vpc.yaml
で作成するVPC ID
とsubnet1.yaml
で作成するSubnet1のID
はsubnet2.yaml
とec2.yaml
で使用するため、vpc.yaml
の出力値をsubnet2.yaml
とec2.yaml
の入力値として!GetAtt
で指定するようにします。
AWSTemplateFormatVersion: 2010-09-09
Resources:
NestStack1:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./vpc.yaml
Parameters:
NestVPCRange: !Ref VPCRange
NestSubnetRange1: !Ref SubnetRange1
NestStack3:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./subnet2.yaml
Parameters:
NestVpcId: !GetAtt NestStack1.Outputs.NestVpcId
NestSubnetRange2: !Ref SubnetRange2
NestStack4:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./ec2.yaml
Parameters:
NestSubnetId: !GetAtt NestStack1.Outputs.NestSubnet1Id
Parameters:
VPCRange:
Type: String
Description: "VPC Subnet Range"
SubnetRange1:
Type: String
Description: "Subnet Range1"
SubnetRange2:
Type: String
Description: "Subnet Range2"
VPCスタック用テンプレートの作成
VPC Range
と、子スタック
となるsubnet1.yaml
のSubnet1 Range
の情報をParameters
セクションで受け取り、subnet1.yaml
にSubnet1 Range
の値を渡します。
また、Subnet1のID
は、ec2.yaml
でも使用するため、subnet1.yaml
の出力値をvpc.yaml
の出力値としてルートスタック
となるcf.yaml
に渡すようにします。
AWSTemplateFormatVersion: 2010-09-09
Parameters:
NestVPCRange:
Type: String
NestSubnetRange1:
Type: String
Resources:
EC2VPC1:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: !Ref NestVPCRange
Tags:
- Key: "Name"
Value: "cf_VPC1"
NestStack2:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./subnet1.yaml
Parameters:
NestVpcId: !Ref EC2VPC1
NestSubnetRange1: !Ref NestSubnetRange1
Outputs:
NestVpcId:
Value: !Ref EC2VPC1
NestSubnet1Id:
Value: !GetAtt NestStack2.Outputs.NestSubnet1Id
Subnet1スタック用テンプレートの作成
親スタック
となるvpc.yaml
からの値をParameters
で受け、Subnet1のID
をOutputs
で出力するようにします。
AWSTemplateFormatVersion: 2010-09-09
Parameters:
NestVpcId:
Type: 'AWS::EC2::VPC::Id'
NestSubnetRange1:
Type: String
Resources:
EC2Subnet1:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref NestVpcId
CidrBlock: !Ref NestSubnetRange1
Tags:
- Key: "Name"
Value: "cf_Subnet1"
Outputs:
NestSubnet1Id:
Value: !Ref EC2Subnet1
Subnet2スタック用テンプレートの作成
subnet2.yaml
は他スタックで使用する値は無いので、親スタック
となるcf.yaml
からの値をParameters
で受けるだけになります。
AWSTemplateFormatVersion: 2010-09-09
Parameters:
NestVpcId:
Type: 'AWS::EC2::VPC::Id'
NestSubnetRange2:
Type: String
Resources:
EC2Subnet2:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref NestVpcId
CidrBlock: !Ref NestSubnetRange2
Tags:
- Key: "Name"
Value: "cf_Subnet2"
EC2スタック用テンプレートの作成
subnet1.yaml
で出力したSubnet1のID
をParameters
で読み込み、EC2インスタンス
を作成します。
AWSTemplateFormatVersion: 2010-09-09
Parameters:
NestSubnetId:
Type: 'AWS::EC2::Subnet::Id'
Resources:
EC2Instance:
Type: 'AWS::EC2::Instance'
Properties:
ImageId: ami-0cc75a8978fbbc969
InstanceType: t2.micro
KeyName: staging_key
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
SubnetId: !Ref NestSubnetId
DeviceIndex: 0
テンプレートの実行
ネスト化
により、テンプレートが複数に分かれるため、その6と同様、package
とdeploy
コマンドでテンプレートの変換と実行を行います。
URLなどのテンプレートの変換は、ルートスタック
で行えば、子スタック
も自動的に変換とS3への転送が行われるため、ルートスタック用テンプレート
のみ実行します。
aws cloudformation package --template-file cf.yaml --s3-bucket [S3バケット名] --output-template-file output.yaml
package
コマンド実行後、deploy
コマンドでS3バケット
に転送したテンプレートを実行します。
aws cloudformation deploy --template-file output.yaml --stack-name stack-test --parameter-overrides VPCRange=172.24.0.0/16 SubnetRange1=172.24.0.0/24 SubnetRange2=172.24.1.0/24
CloudFormation
のスタック
画面で以下の様になれば成功です。
おわりに
スタック
のネスト
は親子関係となるため、構造が分かりやすく、書き方とイメージを覚えてしまえば分かりやすいと感じました。
次も同じようなテンプレート分割方法となるクロススタック参照
(Cross Stack Reference
)を勉強してみようと思います。