CloudFormation マクロを使ったら DeletionPolicy も制御できた!
久々に Python でなんか書こうと思ったらさっぱり書けなくなっていました。。那須です。
昨日、CloudFormation で DeletionPolicy をパラメータで自由に制御できないよーという記事を書きました。
スタックポリシーや削除保護でなんとかなるのはなるのですが、やっぱり同じリソース作るのに別のテンプレートを用意するのはなんか違うなーと思いまして。 で、ふと思い出したのが CloudFormation マクロの存在です。
AWS CloudFormation Macros について
なんとなくなんでもできそうですよね。
CloudFormation マクロの仕組み
CloudFormation マクロの仕組みを図で描くとこうなります(間違ってたらごめん。。
最初にマクロの Lambda 関数とマクロを呼び出す CloudFormation スタックを作成しておいて、リソース作成スタックを作成する際にマクロを呼び出すようにすることで動的に内容を取得して結果に応じてリソースを作成する、という流れです。
マクロである Lambda 関数のインプットとアウトプットは、このドキュメントに書いてあります。
2時間かけてドキュメントを読んで理解したので、さっそく昨日の希望を叶えるべくやってみましょう!
テンプレートのパラメータを使って DeletionPolicy を変えられるようにしてみた
DeletionPolicy をマクロで自由に変えられるようにしてみます。
マクロ(Lambda)の作成
マクロ自体は Lambda 関数なので、下記の Python3 スクリプトを使って関数を作ります。 関数名は SpecifyDeletionPolicy_macro にしました。 CloudFormation テンプレートの DeletionPolicyParam パラメータを基にしてレスポンスを返します。 単純に決められたレスポンスの形の通りに情報を入れて返してるだけのものなので、これなら何やってるかだいたいわかりますよね?
def lambda_handler(event, context): response = { "requestId": event["requestId"], "status": "success", "fragment": event["templateParameterValues"]["DeletionPolicyParam"] } return response
マクロの指定
次に作成したマクロを呼び出すための CloudFormation テンプレートを作成します。 マクロを呼び出すリソースは、Type を AWS::CloudFormation::Macro にします。 AWS::CloudFormation::Macro のドキュメントもあります。 マクロ名と呼び出す Lambda 関数を指定してるだけです。
{ "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "Transform": { "Type": "AWS::CloudFormation::Macro", "Properties": { "Description": "Specify DeletionPolicy settings", "FunctionName": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:SpecifyDeletionPolicy_macro", "Name": "SpecifyDeletionPolicy" } } } }
これをもとにスタックを作成します。 スタックができると、マクロを呼び出す準備は完了です。 物理 ID のところにある SpecifyDeletionPolicy がマクロ名で、これを指定すると先ほど作成した Lambda 関数が実行される形ですね。
リソース作成テンプレート
いよいよ VPC とサブネット 1 つを作成するテンプレートです。 パラメータで DeletionPolicy を [Delete,Retain] から選ぶようにし、VPC と サブネットのところの DeletionPolicy に Fn::Transform でマクロ SpecifyDeletionPolicy を呼び出すようにします。
{ "AWSTemplateFormatVersion": "2010-09-09", "Parameters": { "DeletionPolicyParam": { "Type": "String", "Default": "Delete", "AllowedValues": [ "Delete", "Retain" ] } }, "Resources": { "Vpc": { "Type": "AWS::EC2::VPC", "DeletionPolicy": { "Fn::Transform": { "Name": "SpecifyDeletionPolicy" } }, "Properties": { "CidrBlock": "172.16.255.0/24", "Tags": [ { "Key": "Name", "Value": "macro-test-vpc" } ] } }, "PublicSubnet1": { "Type": "AWS::EC2::Subnet", "DeletionPolicy": { "Fn::Transform": { "Name": "SpecifyDeletionPolicy" } }, "Properties": { "VpcId": { "Ref": "Vpc" }, "CidrBlock": "172.16.255.0/25" "Tags": [ { "Key": "Name", "Value": "macro-test-subnet" } ] } } } }
これでスタックを作成すると、DeletionPolicyParam パラメータで Delete を選ぶと "DeletionPolicy": "Delete" に、Retain を選ぶと "DeletionPolicy": "Retain" としてリソースが作成されます。
このボタンを押すとマクロが実行されます。
マクロを実行した結果、どんなリソースが作成されるかがここに表示されます。
VPC とサブネットが作成されましたね。
テンプレートタブを見ると、何やら選択できるようになっています。「元のテンプレートを表示」だと↑で書いたテンプレートそのままです。
「処理されたテンプレートを表示」を選択すると、マクロ実行した結果のテンプレートが見えます。今回の例で言うと、"DeletionPolicy": "Retain" となっていますね。
作成されたサブネットがこちらです。それでは、このサブネット ID がCloudFormation スタックを削除しても消えないことを確認してみましょう。
「スタックの削除」を実行します。
スタックは消えましたね。ではサブネットがどうなっているか見てみましょう。
"DeletionPolicy": "Retain" と指定されたリソースなので、ちゃんと削除されずに残ってますね。無事に昨日の希望が叶いました!
まとめ
これまで CloudFormation の制約でできなかったことが、マクロを使えば何でもできそうな気がします。Lambda の実行分だけ課金されてしまいますが、使うことで要件を満たせるならガンガン使っていった方がよさそうですね。