まず、スタックやネストといった言葉の意味がよくわかっていない人は、たぶんCloudFormation
の概要がつかめてないと思いますので、最初はこちらを参照ください。
CloudFormation
で個別に、Aスタック、Bスタック、Cスタックと作成して、これらをまとめて一つのアーキテクチャとして作成する場合は、AスタックからBスタックを参照するためにImport
、Outputs
関数を使うのですが、ある程度の規模のスタックになるのこの参照関係が実に面倒になってきます。
特にスタックが相互に参照している場合です。例えば、AスタックはBスタックの値を参照していてかつBスタックもAスタックの値も参照している。このような場合、wait
などを使って、スタックの作成の順番をこちらで指定する必要があります。
さらに、相互参照のスタックを削除するのも結構面倒です。削除する場合はAスタックの参照を一度、消して、それからBスタックをアップデートして、Aスタックを削除、そして、Bスタックを削除するなんていう面倒なことになります。
ここまでくると流石にまとめてスタックを作りたくなりますよね。そこで使うのがネスト
です。
親スタックの配下に子スタックを作成し、親スタックを作成すればまとめて子スタックが作成され、削除する場合も親スタックを削除すれば子スタックもまとめて削除できるようにします。
まずは、最初にディレクトリの構造を説明します。正直、ディレクトリの構造は人それぞれなので、あくまでも参考程度にしてください。
.
├── Makefile
└── nested-stack
├── architect-bucket
│ └── s3.yml
├── master.yml
├── package
│ └── packaged.yml
├── s3-1.yml
└── s3-2.yml
CloudFormationではaws cliのコマンドを何回も実行することが多いです。コマンド実行の度に、このような長ったらしいコマンドを実行するのは実に面倒です。
aws cloudformation create-stack --stack-name myteststack --template-body file://sampletemplate.json --parameters ParameterKey=KeyPairName,ParameterValue=TestKey ParameterKey=SubnetIDs,ParameterValue=SubnetID1\\,SubnetID2
なので、予めMakefile
に、これらのコマンドをいい感じにまとめておきます。そうすれば、make create
みたいな感じで実行するだけよくなります。
これはCloudFormationで展開するサービスを入れているフォルダです。このフォルダ内に作りたいスタックを入れてます。
ネストしたスタックを作る手順はpackage
→deploy
となるのですが、packageコマンドを実行するとローカルにあるテンプレートをS3にアップロードするので、これはそのS3バケット作成用です。
mbコマンド
でS3を作成してもいいですが、まぁ、これは好みですね。
packageコマンドのきちんとした説明は、公式を参照してください。ここでは、ざっくり説明します。
律儀にネストしたスタックを作成しようする以下のような過程が必要になります。
Properties.TemplateURL
に追加この面倒な手順を簡単にするためにpackageコマンドのオプション
を使います。
コマンドが長くなるのでMakefile
に書きます。make package
でコマンドを実行すると子テンプレートがs3 bucket
にアップされます。ここでは、architect-bucket
ですね。
# Makefile
# package
.PHONY: package
package:
@aws cloudformation package --template-file ./nested-stack/master.yml \
--s3-bucket architect-bucket \
--output-template-file ./nested-stack/package/packaged.yml
そして、親テンプレートであるmaster.yml
のProperties.TemplateURL
が子テンプレートのURLに書き換わったテンプレートpackaged.yml
が作成されます。
# master.yml
AWSTemplateFormatVersion: 2010-09-09
Description: master
Parameters:
Name:
Type: String
Resources:
S3Bucket1:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./s3-1.yml ← ローカルの相対パスがS3のURLに変換される
Parameters:
Name: !Ref Name
S3Bucket2:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./s3-2.yml ← ローカルの相対パスがS3のURLに変換される
Parameters:
S3Name1: !GetAtt S3Bucket1.Outputs.OutputS3Name1
実際に試してないので、こんな感じになります。
# packaged.yml
AWSTemplateFormatVersion: 2010-09-09
Description: master
Parameters:
Name:
Type: String
Resources:
S3Bucket1:
Type: AWS::CloudFormation::Stack
Properties:
https://s3.ap-northeast-1.amazonaws.com/nested-stack/なんちゃらなんちゃら.template
Parameters:
Name: !Ref Name
S3Bucket2:
Type: AWS::CloudFormation::Stack
Properties:
https://s3.ap-northeast-1.amazonaws.com/nested-stack/なんちゃらなんちゃら.template
Parameters:
S3Name1: !GetAtt S3Bucket1.Outputs.OutputS3Name1
これでデプロイする準備はできました。先ほど作成されたpackaged.yml
を使ってmake deploy
を実行してみます。
deploy
コマンドが親スタック作成とネストした子スタックの作成までまるっとやってくれるので大変便利です。
# Makefile
# deploy
.PHONY: deploy
deploy:
@aws cloudformation deploy \
--template-file nested-stack/package/packaged.yml \
--stack-name nested-stack \
親スタックの値をネストした子スタック渡す方法ですが、これも好みだとは思うのですが、僕は親スタックに直接、値をセットするのは後々違うプロジェクトでmaster.yml
自体を編集することになるので、なんかイヤなので、Makefile
に値をセットしてdeployコマンドのオプション--parameter-overrides
で値を渡すようにしています。
# Makefile
Name := bokunonikki
# deploy
.PHONY: deploy
deploy:
@aws cloudformation deploy \
--template-file nested-stack/package/packaged.yml \
--stack-name nested-stack \
--parameter-overrides Name=${Name}
Name
の値をまず最初に親スタックに渡します。master.yml
はこのようにします。
このようにすることで、ネストされた子スタックにs3-1.yml
値を渡すことができます。
# master.yml
AWSTemplateFormatVersion: 2010-09-09
Description: master
Parameters:
Name:
Type: String
Resources:
S3Bucket1:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./s3-1.yml
Parameters:
Name: !Ref Name
s3-1.yml
は以下のようにすれば親スタックの値を受け取ることができます。この場合は、bokunonikki-1
というS3バケットが作成されます。
AWSTemplateFormatVersion: 2010-09-09
Description: nested-stack-s3-1
Parameters:
Name:
Type: String
Resources:
S3Bucket1:
Type: AWS::S3::Bucket
DeletionPolicy: Delete
Properties:
BucketName: !Sub
- ${ Name }-1
- { Name: !Ref Name }
ネストした子スタックどうしで値を渡すにはOutputs関数
を使います。
ここでは、試しにAスタック
の値をBスタック
に渡す場合を考えてみます。
まず、Aスタック
の値をOutputs関数
を使ってBスタック
でも参照できるようにします。この場合は、OutputS3Name1
という名前で参照できるようになってます。
AWSTemplateFormatVersion: 2010-09-09
Description: nested-stack-s3-1
Parameters:
Name:
Type: String
Resources:
S3Bucket1:
Type: AWS::S3::Bucket
DeletionPolicy: Delete
Properties:
BucketName: !Sub
- ${ Name }-1
- { Name: !Ref Name }
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Outputs:
OutputS3Name1:
Value: !Ref S3Bucket1
次に受け取る側のBスタック
です。値を受け取る側はまず、親であるテンプレートに!GetAtt
関数を使って参照できるようにします。この場合は、Parameters
のS3Name1
という値に、S3Bucket1のOutputsのOutputS3Name1をセットするという意味です。
AWSTemplateFormatVersion: 2010-09-09
Description: master
Parameters:
Name:
Type: String
Resources:
S3Bucket1:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./s3-1.yml
Parameters:
Name: !Ref Name
S3Bucket2:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: ./s3-2.yml
Parameters:
S3Name1: !GetAtt S3Bucket1.Outputs.OutputS3Name1 ← これ
Bスタック
自体は、特別何をする必要もありません。普通にParameters
をセットすればいいです。
AWSTemplateFormatVersion: 2010-09-09
Description: nested-stack-s3-2
Parameters:
S3Name1:
Type: String
Resources:
S3Bucket2:
Type: AWS::S3::Bucket
DeletionPolicy: Delete
Properties:
BucketName: !Sub
- ${ S3Name1 }-2
- { S3Name1: !Ref S3Name1 }
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
以上で終わりです。これらのコマンドは実際に実行したわけではないので、コピペ実行はできないのでお気をつけあれ。
ただ、これらの大まかな知識があればあとは公式サイトを見ればだいだいできるようになるはずです。
Makeの本は、これ一冊あればことたりると思います。何かしたくなったら調べるくらいのレベルでも結構役に立ちます。
この本は実際にawsのサービスを実際に手を動かして作っているので、awsの概要を掴むには最適です。