CloudFormation テンプレート書いていると、1枚のテンプレートが大きくなり数百行は優に超えてくるので分割したくなります。 分割するとテンプレートの枚数だけデプロイコマンドを叩く必要があるので シェルスクリプトや Makefile を書かないと簡単にデプロイはしにくいです。 テンプレート間の依存関係も考慮した上でデプロイする必要もあります。 そこで Nested Stack でテンプレートを構築し、スタックを作成する方法を紹介します。
Nested Stack とは
分割された CloudFormation Template からスタックを作成・管理するには2通りの方法があります。
- Cross Stack Reference - チュートリアル: 別の AWS CloudFormation スタックのリソース出力を参照する - AWS CloudFormation
- Nested Stack - ネストされたスタックの操作 - AWS CloudFormation
今回紹介する Nested Stack とは何でしょうか。 公式ドキュメントより抜粋します。
ネストされたスタックは、他のスタックの一部として作成されたスタックです。ネストされたスタックは、AWS::CloudFormation::Stack リソースを使用して別のスタック内に作成します。
少し分かりづらいですが、親スタックが子スタックを作成するということです。
実際に CloudFormation Nested Stack でスタックを構築してみましょう。
親テンプレートとネットワーク用テンプレート(VPC, Subnet)の2枚を使用します。
テンプレート
. ├── network.yml └── parent.yml
parent.yml
AWSTemplateFormatVersion: "2010-09-09" Resources: NetworkStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: ./network.yml
network.yml
AWSTemplateFormatVersion: "2010-09-09" Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: 172.16.0.0/16 EnableDnsSupport: "false" EnableDnsHostnames: "false" InstanceTenancy: dedicated PublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: "" CidrBlock: 172.16.0.0/24 VpcId: !Ref VPC PublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 1 - Fn::GetAZs: "" CidrBlock: 172.16.1.0/24 VpcId: !Ref VPC
スタックの作り方
本来は親テンプレートの Properties.TemplateURL
に S3 へ upload した子テンプレートの URL を指定します。
しかし、子テンプレートを S3 に upload してからさらに親テンプレートも変更するとなると大変です。
そこで package サブコマンド を使います。
親テンプレートの Properties.TemplateURL
に子テンプレートの相対パスを指定し、
このコマンドオプションに親テンプレートと upload する S3 bucket name を指定し実行すると、
- 子テンプレートを S3 bucket へ upload
- 親テンプレートの
Properties.TemplateURL
を upload した子テンプレートの URL に書き換え
となります。親スタックを作成するには、生成された 2. のテンプレートを使用します。
Nested Stack 作成
実際に Nested Stack を作成してみましょう。
. ├── network.yml └── parent.yml
先と同じこれらのファイルを使います。
$ aws cloudformation package --template-file ./parent.yml --s3-bucket cfn-nested-stack-demo --output-template-file /tmp/response.yml Uploading to e4821e88e99c0568aa79739fbaa80f2c.template 631 / 631.0 (100.00%) Successfully packaged artifacts and wrote output template to file /tmp/response.yml. Execute the following command to deploy the packaged template aws cloudformation deploy --template-file /tmp/response.yml --stack-name <YOUR STACK NAME>
package
サブコマンドを実行すると、子テンプレートが S3 bucket へ upload されます。
$ aws s3 ls s3://cfn-nested-stack-demo/ 2020-04-21 12:39:13 631 e4821e88e99c0568aa79739fbaa80f2c.template $ aws s3 cp s3://cfn-nested-stack-demo/e4821e88e99c0568aa79739fbaa80f2c.template - AWSTemplateFormatVersion: '2010-09-09' Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: 172.16.0.0/16 EnableDnsSupport: 'false' EnableDnsHostnames: 'false' InstanceTenancy: dedicated PublicSubnet1: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: '' CidrBlock: 172.16.0.0/24 VpcId: Ref: VPC PublicSubnet2: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 1 - Fn::GetAZs: '' CidrBlock: 172.16.1.0/24 VpcId: Ref: VPC
作成された親テンプレート /tmp/response.yml
は以下のようになります。
AWSTemplateFormatVersion: '2010-09-09' Resources: NetworkStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.ap-northeast-1.amazonaws.com/cfn-nested-stack-demo/e4821e88e99c0568aa79739fbaa80f2c.template
子テンプレートを指定する TemplateURL
が変わっていることを確認できました。
ではこのテンプレートを使って Nested Stack を作成しましょう。
$ aws cloudformation deploy --template-file /tmp/response.yml --stack-name cfn-nested-stack-demo Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - cfn-nested-stack-demo
作成されました。
Nested Stack が作成されると、子スタックは以下のようになります。
親スタック名-子スタックリソース名-ランダム文字列
というスタック名になる- AWS Management Console では子テンプレートに
ネストされた
やNESTED
というタグが確認できる
Nested Stack 更新
Nested Stack を更新するには、親スタックを操作します。
ここでは新たに PublicSubnet3
を追加しましょう。
network.yml
diff --git a/network.yml b/network.yml index 9a3b5a6..5b32343 100644 --- a/network.yml +++ b/network.yml @@ -28,3 +28,13 @@ Resources: - Fn::GetAZs: "" CidrBlock: 172.16.1.0/24 VpcId: !Ref VPC + + PublicSubnet3: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: + Fn::Select: + - 2 + - Fn::GetAZs: "" + CidrBlock: 172.16.2.0/24 + VpcId: !Ref VPC
同じように package
, deploy
サブコマンドを実行します。
$ aws cloudformation package --template-file ./parent.yml --s3-bucket cfn-nested-stack-demo --output-template-file /tmp/response.yml Uploading to 7051d70fce833c9fca4fa573b9cfc96d.template 833 / 833.0 (100.00%) Successfully packaged artifacts and wrote output template to file /tmp/response.yml. Execute the following command to deploy the packaged template aws cloudformation deploy --template-file /tmp/response.yml --stack-name <YOUR STACK NAME> $ aws cloudformation deploy --template-file /tmp/response.yml --stack-name cfn-nested-stack-demo Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - cfn-nested-stack-demo
更新できました。
AWS 公式では Nested Stack を更新・削除する場合親スタックを操作することが推奨されています。 子スタックを操作するとどうなるのでしょうか。
マネコンから削除します
子スタックのみ削除され、親スタックが残った状態になりました。 また、子スタックで管理されていた VPC, Subnet も同様削除されました。
ここでは省略しますが、この状態で親スタックのみを削除することもできました。
さて、この半端に残ったスタックを更新するとどうなるのでしょうか。
今度は PublicSubnet2, PublicSubnet3 を削除してみます。
diff --git a/network.yml b/network.yml index 5b32343..addc850 100644 --- a/network.yml +++ b/network.yml @@ -18,23 +18,3 @@ Resources: - Fn::GetAZs: "" CidrBlock: 172.16.0.0/24 VpcId: !Ref VPC - - PublicSubnet2: - Type: AWS::EC2::Subnet - Properties: - AvailabilityZone: - Fn::Select: - - 1 - - Fn::GetAZs: "" - CidrBlock: 172.16.1.0/24 - VpcId: !Ref VPC - - PublicSubnet3: - Type: AWS::EC2::Subnet - Properties: - AvailabilityZone: - Fn::Select: - - 2 - - Fn::GetAZs: "" - CidrBlock: 172.16.2.0/24 - VpcId: !Ref VPC
$ aws cloudformation package --template-file ./parent.yml --s3-bucket cfn-nested-stack-demo --output-template-file /tmp/response.yml Uploading to c1d0483d57bc44d918b4e545b37ee3f6.template 429 / 429.0 (100.00%) Successfully packaged artifacts and wrote output template to file /tmp/response.yml. Execute the following command to deploy the packaged template aws cloudformation deploy --template-file /tmp/response.yml --stack-name <YOUR STACK NAME> $ aws cloudformation deploy --template-file /tmp/response.yml --stack-name cfn-nested-stack-demo Waiting for changeset to be created.. Waiting for stack create/update to complete Failed to create/update the stack. Run the following command to fetch the list of events leading up to the failure aws cloudformation describe-stack-events --stack-name cfn-nested-stack-demo
スタック更新に失敗しました。
子スタックを削除した場合は Nested Stack 自体削除するしかなさそうです。 Nested Stack を用いて AWS リソースの作成・変更等を運用していくのであれば、公式推奨通りに親スタックのみを操作するのがよいでしょう
Nested Stack の使いどころ
Cross Stack Reference に比べると分割されたスタックをまとめて管理できデプロイも簡単になるので、 Nested Stack を進んで使っていこう!とも思ったのですがデメリットもあります。
更新時の影響範囲が広い
基本的に Nested Stack 更新時は Nested 管理下の子スタックにも更新が掛かります
子スタックの Change Set が見れない
子スタック(
AWS::CloudFormation::Stack
リソースタイプ)が Modify されるのはわかるのですが、 詳細までは見れません
たくさんのリソースを管理したい、CI/CD Pipeline に CloudFormation Stack 更新を乗せたい、 ということあれば、Nested Stack で管理しないことをお勧めします (個人的には変更差分の詳細が見れないのが辛い…)。
上記デメリットを許容できるのであれば Nested Stack で構築するのもありだと思います。
さいごに
今回は CloudFormation Nested Stack の作り方・デメリット等紹介しました。
package
, deploy
サブコマンドを使用して CloudFormation Nested Stack を作成・更新したり、
子スタックのみを削除した際の挙動を確認しました。
CloudFormation Nested Stack を使用する際には、まずは運用まで回せるのかを検証することをお勧めします。