CloudFormationのスタック間でリソースを参照する
ども、大瀧です。 昨日Cloudformationの大規模アップデートが有り、YAMLサポートや新しい関数などと共に異なるスタックのリソース参照機能が追加されました。 今回は異なるスタックのリソース参照機能について試してみた様子をレポートします。
これまでのスタック間参照
これまでCloudFormationには、独立したCloudFormationスタック(単一のCloudFormationテンプレートから作成されたリソース一式)同士でリソースを参照する方法はなく、リソース名やリソースIDをパラメータに入力するか、テンプレートにハードコードしていました *1。
シェルスクリプトで複数のCloudFormationスタック作成を自動化するときは、aws cloudformation describe-stack-resources
のレスポンスをパースして次のスタック作成のパラメータに代入などしていました。
スタック間参照
今回追加されたスタック間参照は、被参照テンプレートのエクスポート定義と参照するテンプレートのインポートで実現します。
エクスポート
被参照側のテンプレートでは、参照させるリソースないしアトリビュートをOutputs
セクションに指定し、Export
属性の下に参照時の変数名をName
属性で定義します。
例えば、VPC、サブネット、セキュリティグループのIDをそれぞれSampleVPC
、SampleSubnet
、SampleSG
という変数名でエクスポートします。
"Outputs" : { "VPCId" : { "Value" : { "Ref" : "VPC" }, "Export" : { "Name" : "SampleVPC" } }, "PublicSubnet" : { "Value" : { "Ref" : "PublicSubnet" }, "Export" : { "Name" : "SampleSubnet" } }, "WebServerSecurityGroup" : { "Value" : { "Fn::GetAtt" : ["WebServerSecurityGroup", "GroupId"] }, "Export" : { "Name" : "SampleSG" } } }
こんな感じです。
インポート
エクスポートした変数を参照するテンプレートでは、組み込み関数Fn::ImportValue
で変数名を指定します。Ref
と同じような感覚で利用できます。
"Resources" : { "WebServerInstance": { "Type": "AWS::EC2::Instance", "Properties": { "InstanceType" : "t2.micro", "NetworkInterfaces" : [{ "SubnetId" : { "Fn::ImportValue" : "SampleSubnet" }, "GroupSet" : [ { "Fn::ImportValue" : "SampleSG" } } ], :(略)
結構お手軽ですね。
制約と対策
上の例を見て気づいた方もいるかも知れませんが、何も考えずに変数を定義するとテンプレートにハードコードすることになります。つまり、同一テンプレートから複数のスタックを作成しようとすると、変数名の重複でエラーになります。CloudformationでCloud Design Patternのスタンプパターンのようなことをやろうとするとハマるので注意が必要です。
変数名を可変にするために、以下の方法を考えてみました。
エクスポートの改良
文字列決め打ちではなく、Ref
による参照もしくは新しく追加された文字列代入のFn::Sub
を利用します。今回はスタック名と文字列の組み合わせにしてみました。
"Outputs" : { "VPCId" : { "Value" : { "Ref" : "VPC" }, "Export" : { "Name" : { "Fn::Sub" : "${AWS::StackName}-VPC" } } }, "PublicSubnet" : { "Value" : { "Ref" : "PublicSubnet" }, "Export" : { "Name" : { "Fn::Sub" : "${AWS::StackName}-Subnet" } } }, "WebServerSecurityGroup" : { "Value" : { "Fn::GetAtt" : ["WebServerSecurityGroup", "GroupId"] }, "Export" : { "Name" : { "Fn::Sub" : "${AWS::StackName}-SecGroup" } } } }
なおFn::Sub
と似た、組み込み関数のFn::Join
はエラーで使えませんでした。戻り値が純粋な文字列では無いためのようです。
インポートの改良
インポート側も同様に可変になるような仕掛けを考えました。エクスポートで試したFn::Sub
とFn::Join
はエラーになってしまいましたがRef
が通ったので、Parameters
を経由してRef
で参照させることで可変にすることができました。
"Parameters": { "NetworkStackSubnet": { "Type": "String", "Default" : "SampleNetworkCrossStack-Subnet" }, "NetworkStackSecGroup": { "Type": "String", "Default" : "SampleNetworkCrossStack-SecGroup" } }, "Resources" : { "WebServerInstance": { "Type": "AWS::EC2::Instance", "Properties": { "InstanceType" : "t2.micro", "NetworkInterfaces" : [{ "SubnetId" : { "Fn::ImportValue" : { "Ref" : "NetworkStackSubnet" } }, "GroupSet" : [ { "Fn::ImportValue" : { "Ref" : "NetworkStackSecGroup" } } ], :(略)
これで、複数スタックを作ったときも重複せずにそれぞれのスタック名から変数名を指定し、参照することができます。
まとめ
CloudFormationのスタック間でリソースを参照する方法について解説しました。若干クセのある機能ですが、上手く活用してCloudFormationによるAWS環境構築に役立ててください!
なお、現時点ではエクスポートした変数一覧を確認する機能が見当たらないので、どんな変数が定義されているのかは、作成したスタックの[Template]タブからテンプレートのOutputs
セクションを片っ端から確認していくしかなさそうです。。。
参考URL
脚注
- テンプレートのリソースに別のテンプレートを指定する、いわゆるテンプレートの親子関係はこの限りではなく、子テンプレートから親テンプレートのリソースを参照することが可能です。 ↩