はじめに
今回はまだ試していなかったTransform
のうち、他テンプレートのインクルード機能を使ってみようと思います。
Transformセクションについて
CloudFormation
からLambda
関数を呼び出す場合や、他のテンプレートをインクルードする場合に使用します。
今回使う他テンプレートのインクルードは、テンプレート内のどこでも呼び出すことができ、呼び出す位置によって若干構文が変わってきます。
ただ、インクルードで使う場合はトップレベルで使う場合でもFn::Transform
だけ覚えておけばよいと思います。(理由は後述)
Transform:
Name: 'AWS::Include'
Parameters:
Location: 's3://MyAmazonS3BucketName/MyFileName.yaml'
Fn::Transform:
Name: 'AWS::Include'
Parameters:
Location: 's3://MyAmazonS3BucketName/MyFileName.yaml'
Transform設定があるテンプレートの実行方法
Transform
を含むテンプレートは今までのようにaws cloudformation create-stack
コマンドでは実行できず、aws cloudformation package
コマンドでS3のバケットにインクルードするファイルをアップロードする必要があります。
aws cloudformation package
コマンドでアップロード後は、aws cloudformation deploy
コマンドでスタックを作成・実行します。
create-stack
もdeploy
もどちらもスタックを作成できますが、変換を伴うテンプレートの実行(今回の場合、Transform
によるLocation
の変換)を行う場合はdeploy
で行う必要があるようです。
Transformでインクルードする際の注意点
メインのテンプレートから呼び出すファイルですが、基本的にはコードの一部分をカット&ペーストして別ファイルとしてインクルードすることができますがいくつか注意しなければいけない点があるので以下記載します。
1.インクルードファイルの書式
インクルードファイルでは!Ref
などの組み込み関数や!FindInMap
のような短縮形の記載は使えないようで、Ref:
やFn::FindInMap:
のような記載方法に書き直す必要があります。
また、{}
や[]
を使った記載もフォーマットエラーとなります。
YAML
の書式についてあまり詳しくありませんが、いわゆるブロックスタイル
の書式でないとインクルードファイルは読み込んでくれないようです。
そのため、上記の書式が使われている部分を抜き出してインクルードファイルとする場合は、別の記載方法に変換する必要があります。
2.同じインデントレベルでの複数インクルードファイルの指定
同じ構造内で同じインデントレベルでの複数Transform
定義の指定はできません。
例えば、Resources
セクションでEC2インスタンス
の定義とサブネット
の定義をTransform
でインクルードしようとしても最後に記載したインクルードファイルの定義しか反映されません。
また、Location
に2つ書いた場合は、実行時に失敗します。
そのため、同じインデントレベルで複数インクルードする場合は、1つのファイルにまとめるか、構成を変更する必要があります。
Resources:
Fn::Transform: ★実行されない
Name: AWS::Include
Parameters:
Location:
- s3://[S3バケット名]/c102fec7b84d7dc46b57e4ea6c3ecc85
Fn::Transform: ★実行される
Name: AWS::Include
Parameters:
Location:
- s3://[S3バケット名]/1a887907495d757aa66496d127a9d9ae
Resources:
Fn::Transform: ★以下の書き方はエラーで失敗
Name: AWS::Include
Parameters:
Location:
- s3://[S3バケット名]/c102fec7b84d7dc46b57e4ea6c3ecc85
- s3://[S3バケット名]/1a887907495d757aa66496d127a9d9ae
3.トップレベルでTransform指定した場合の変換
Transform
でインクルードファイルを指定した場合、前述のpackage
コマンドを実行してインクルードファイルをアップロードすると、Location
のファイル名がアップロード先のS3URL
に変換されます。
但し、トップレベルでの記載方法でTransform
を設定すると、package
コマンドでLocation
部分が変換されないようです。
Fn::Transform
の記載でもトップレベルで設定できるので、インクルードでTransform
を使う場合はFn::Transform
だけ使えばよいかと思います。
AWSTemplateFormatVersion: 2010-09-09
Transform:
Name: AWS::Include
Parameters:
Location: description.yaml ★変換されない
AWSTemplateFormatVersion: 2010-09-09
Fn::Transform:
Name: AWS::Include
Parameters:
Location: s3://[S3バケット名]/b7a2955ca33cb3908c36b49284fd487a ★変換された
今回作成する構成
今回も今までと同様、前回のテンプレートを基に作成していきます。
前回からの追加要素として以下を追加します。
- デザイナー画面からのパラメータ入力項目を分かりやすく整える。
- EC2インスタンスの記述を
Transform
で別ファイルからインクルードする。
パラメータ入力画面の整理
大体コマンドで実行しているのであまり関係ないのですが、Metadata
セクションにパラメータ入力の順番等を記載することでパラメータ入力画面の項目を整えることができるようなので実装してみようと思います。
MetaData
セクションにAWS::CloudFormation::Interface
を設定することで画面整理できます。
Metadata:
'AWS::CloudFormation::Interface':
ParameterGroups:
- Label:
default: "Network Configuration"
Parameters:
- VPCRange
- SubnetRange
- Label:
default: "EC2 Configuration"
Parameters:
- EnvType
ParameterLabels:
VPCRange:
default: "Input VPC Range"
SubnetRange:
default: "Input Subnet Range"
EnvType:
default: "Select Environment"
尚、上記のParameterLabels
は入力しないとMetadata記載なし
の場合と同様、Parameters
セクションの項目名が表示されます。
今まで入力項目の順番がバラバラになるのが気になっていましたが、これで運用者がGUI画面上から入力する場合でも入力間違いが減らせそうです。
インクルードファイルの作成
前回のテンプレートからEC2インスタンス
部分をカット&ペーストします。
ただ、前述の通り、{}
や[]
などは使用できません。
そのため、短縮系や{}
等使われている箇所を変換していきます。
Ref短縮形の変換
前回のテンプレートでは、!Ref
と!FinfInMap
を短縮形で使用しているため、通常の構文で記載した場合は以下となります。(!FindInMapは後述)
SubnetId: !Ref EC2SJOWQ
SubnetId:
- Ref: EC2SJOWQ
括弧とFindInMap短縮形の変換
前回のテンプレートではKeyName
の指定で括弧や短縮形を多用しています。
括弧や短縮形が多くて変換が難しいですが、括弧の部分をそれぞれ1つのグループとして、グループ内は同列の要素として変換した結果が以下となります。
KeyName: {Fn::If: [EnvSelect, !FindInMap [ Keypair, productionkey, KEYPAIR ], !FindInMap [ Keypair, stagingkey, KEYPAIR ]]}
KeyName:
Fn::If:
- EnvSelect
- Fn::FindInMap:
- Keypair
- productionkey
- KEYPAIR
- Fn::FindInMap:
- Keypair
- stagingkey
- KEYPAIR
完成したインクルードファイル
以下の内容をec2.yaml
としてローカルに保存しておきます。
ブロックスタイルに変換後のインクルードファイル(展開して下さい)
EC2I3Q8S8:
Type: 'AWS::EC2::Instance'
Properties:
ImageId: ami-0cc75a8978fbbc969
InstanceType: t2.micro
KeyName:
Fn::If:
- EnvSelect
- Fn::FindInMap:
- Keypair
- productionkey
- KEYPAIR
- Fn::FindInMap:
- Keypair
- stagingkey
- KEYPAIR
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
SubnetId:
Ref: EC2SJOWQ
DeviceIndex: 0
EC2Instance:
Type: 'AWS::EC2::Instance'
Properties:
ImageId: ami-0cc75a8978fbbc969
InstanceType: t2.micro
KeyName:
Fn::If:
- EnvSelect
- Fn::FindInMap:
- Keypair
- productionkey
- KEYPAIR
- Fn::FindInMap:
- Keypair
- stagingkey
- KEYPAIR
NetworkInterfaces:
- SubnetId:
Ref: EC2SJOWQ
DeviceIndex: 0
Condition: EnvSelect
テンプレートファイルの修正
先ほどカット&ペーストで切り出したEC2インスタンス
のリソースが記載されていた箇所にTransform
を記載します。
Resources:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: "ec2.yaml"
完成したテンプレートの変換
完成したテンプレートファイルは以下となります。
この時点ではInclude
のLocation
で指定しているファイル名はローカルで作成したファイル名(ec2.yaml
)で問題ありません。
完成したテンプレートファイル(展開して下さい)
AWSTemplateFormatVersion: 2010-09-09
Metadata:
'AWS::CloudFormation::Interface':
ParameterGroups:
- Label:
default: "Network Configuration"
Parameters:
- VPCRange
- SubnetRange
- Label:
default: "EC2 Configuration"
Parameters:
- EnvType
ParameterLabels:
VPCRange:
default: "Input VPC Range"
SubnetRange:
default: "Input Subnet Range"
EnvType:
default: "Select Environment"
'AWS::CloudFormation::Designer':
e1390660-013a-4523-9893-ccd6074e430f:
size:
width: 190
height: 190
position:
x: 390
'y': 90
z: 0
embeds:
- e6cc612e-5a0f-459b-8329-3a5852613031
e6cc612e-5a0f-459b-8329-3a5852613031:
size:
width: 140
height: 140
position:
x: 410
'y': 130
z: 1
parent: e1390660-013a-4523-9893-ccd6074e430f
iscontainedinside:
- e1390660-013a-4523-9893-ccd6074e430f
Resources:
EC2VPC1WR8B:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: !Ref VPCRange
Metadata:
'AWS::CloudFormation::Designer':
id: e1390660-013a-4523-9893-ccd6074e430f
EC2SJOWQ:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref EC2VPC1WR8B
CidrBlock: !Ref SubnetRange
Metadata:
'AWS::CloudFormation::Designer':
id: e6cc612e-5a0f-459b-8329-3a5852613031
Fn::Transform:
Name: AWS::Include
Parameters:
Location: "ec2.yaml"
Parameters:
VPCRange:
Type: String
Description: "VPC Subnet Range"
SubnetRange:
Type: String
Description: "Subnet Range"
EnvType:
Type: String
Description: "Production or Staging"
AllowedValues: ["Production", "Staging"]
Outputs:
VPCRegion:
Description: "Region Name"
Value: !Ref AWS::Region
Mappings:
Keypair:
productionkey:
"KEYPAIR": "production_key"
stagingkey:
"KEYPAIR": "staging_key"
Conditions:
EnvSelect: {"Fn::Equals": [{"Ref" : "EnvType"}, "Production"]}
以下package
コマンドを実行することで、テンプレートファイルの変換とインクルードファイルのS3バケットへのアップロードが行われます。
また、テンプレートファイルの変換は、テンプレートファイルとして指定したファイルが直接変換されるのではなく、--output-template-file
で指定したファイルに変換後の内容が出力されます。(今回は出力後のテンプレートファイルをcf.yaml
とします。)
尚、インクルードファイルがアップロードされるS3バケットは予め作成しておいてください。
aws cloudformation package --template-file cloudformation.yaml --s3-bucket [S3バケット名] --output-template-file cf.yaml
完成したテンプレートの実行
変換後のテンプレートファイルは以下となります。
Transform
のLocation
だけではなく、yamlのブロックスタイル
への変更が行われるようです。
変換されたテンプレートファイル(展開して下さい)
AWSTemplateFormatVersion: 2010-09-09
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Network Configuration
Parameters:
- VPCRange
- SubnetRange
- Label:
default: EC2 Configuration
Parameters:
- EnvType
ParameterLabels:
VPCRange:
default: Input VPC Range
SubnetRange:
default: Input Subnet Range
EnvType:
default: Select Environment
AWS::CloudFormation::Designer:
e1390660-013a-4523-9893-ccd6074e430f:
size:
width: 190
height: 190
position:
x: 390
y: 90
z: 0
embeds:
- e6cc612e-5a0f-459b-8329-3a5852613031
e6cc612e-5a0f-459b-8329-3a5852613031:
size:
width: 140
height: 140
position:
x: 410
y: 130
z: 1
parent: e1390660-013a-4523-9893-ccd6074e430f
iscontainedinside:
- e1390660-013a-4523-9893-ccd6074e430f
Resources:
EC2VPC1WR8B:
Type: AWS::EC2::VPC
Properties:
CidrBlock:
Ref: VPCRange
Metadata:
AWS::CloudFormation::Designer:
id: e1390660-013a-4523-9893-ccd6074e430f
EC2SJOWQ:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: EC2VPC1WR8B
CidrBlock:
Ref: SubnetRange
Metadata:
AWS::CloudFormation::Designer:
id: e6cc612e-5a0f-459b-8329-3a5852613031
Fn::Transform:
Name: AWS::Include
Parameters:
Location: s3://[S3バケット名]/[アップロードされたインクルードファイル名]
Parameters:
VPCRange:
Type: String
Description: VPC Subnet Range
SubnetRange:
Type: String
Description: Subnet Range
EnvType:
Type: String
Description: Production or Staging
AllowedValues:
- Production
- Staging
Outputs:
VPCRegion:
Description: Region Name
Value:
Ref: AWS::Region
Mappings:
Keypair:
productionkey:
KEYPAIR: production_key
stagingkey:
KEYPAIR: staging_key
Conditions:
EnvSelect:
Fn::Equals:
- Ref: EnvType
- Production
このテンプレートを指定してdeploy
コマンドでスタックを作成していきますが、deploy
コマンドはcreate-stack
のコマンドのようにパラメータファイルを外部ファイルとして読み込むことができないため、パラメータの入力は--parameter-overrides
コマンドで指定します。
aws cloudformation deploy --template-file cf.yaml --stack-name stack-test --parameter-overrides SubnetRange=172.24.0.0/24 VPCRange=172.24.0.0/16 EnvType=Production
おわりに
今回はTransform
によるファイルのインクルードを試しましたが、色々と制約があるためちょっと使いづらいと感じました。
今回の方法でTransform
をバリバリ使うのであれば、普段から短縮形や括弧などを使うフロースタイル
での構文は使わないよう心がければよさそうではありますが、複数のテンプレートを使用するやり方はTransform
ではなく他の方法がいくつかあるようなので、そもそもTransform
を使うべきではないのかもしれません。
次回は変更セット
とドリフト検出
について勉強してみようと思います。