概要
本記事ではEC2をCloudFormationで作成する際に、様々なオプションに柔軟に対応する方法をご紹介します。
GUIでEC2を作成する場合、アタッチするEBSの数は簡単に増減出来ますよね?他にもVPCやSecurityGroupをリストから選択出来たり、EC2の作成ウィザードは非常に多くの項目を簡単に設定することが出来ます。
一方で組織のポリシーとしてEC2にプリセットしたい設定がある場合(例:特定タグの付与やリソース名称の指定etc)などは、あらかじめ値を指定したCloudFormation Templateを用意・利用して作成することで実現することが出来ます。
マルチアカウント環境においてもAWS Service Catalogを使えば、メンバーアカウントに対してCloudFormation Templateを配布することが出来ますので、EC2の作成ウィザードではなくService Catalogのメニューから配布されたCloudFormation Templateを使ってEC2を作成することで組織のポリシーを満たしたリソースの作成が可能です。
では、EC2の作成ウィザードのような設定の柔軟性を持つCloudFormation Templateを作るにはどうすればよいでしょうか。
以下ではその一例として、設定の種類毎のパラメータの書き方や条件に応じてEC2に複数のEBSをアタッチする方法をご説明します。
ユースケース
- 設定の種類(Boolean型,複数項目から単一選択,複数項目から複数選択)に合わせたCloudFormation Templateでの記載方法を確認したい
- 複数の条件分岐を持たせたEC2作成用のCloudFormation Templateを用意したい
設定内容
全体
CloudFormation Templateの全文を記載します。
*かなり行数があるためいくつか設定値をカットしています。
*パラメータの整理のため、パラメータグループの利用をオススメします。
AWSTemplateFormatVersion: "2010-09-09"
Description: ""
Parameters:
Ec2ImageId:
Type: AWS::EC2::Image::Id
Ec2InstanceType:
Description: Select EC2 InstanceType
Type: String
AllowedValues: ["t3.micro", "t3.medium", "t3.large"]
Ec2KeyName:
Type: AWS::EC2::KeyPair::KeyName
SubnetID:
Type: AWS::EC2::Subnet::Id
SecurityGroups:
Type: "List<AWS::EC2::SecurityGroup::Id>"
EbsVolume00Size:
Type: Number
Default: 10
EbsVolume00Type:
Type: String
Default: gp2
AllowedValues: [gp2, gp3, io1, io2, standard]
EbsVolume01Size:
Type: Number
Default: 0
EbsVolume01Type:
Type: String
Default: gp2
AllowedValues: [gp2, gp3, io1, io2, standard]
EbsVolume02Size:
Type: Number
Default: 0
EbsVolume02Type:
Type: String
Default: gp2
AllowedValues: [gp2, gp3, io1, io2, standard]
InstanceProfile:
Type: String
DisableApiTermination:
Type: String
Default: false
AllowedValues: [true, false]
Tenancy:
Type: String
Default: default
AllowedValues: [default, dedicated, host]
EBSEncryption:
Type: String
Default: true
AllowedValues: [true, false]
EBSOptimized:
Type: String
Default: false
AllowedValues: [true, false]
EBSDeleteOnTermination:
Type: String
Default: true
AllowedValues: [true, false]
Conditions:
AttachVolume01: !Not
- !Equals [!Ref EbsVolume01Size, "0"]
AttachVolume02: !Not
- !Equals [!Ref EbsVolume02Size, "0"]
Resources:
CreateEC2InstanceAmazonLinux2:
Type: "AWS::EC2::Instance"
Properties:
ImageId: !Ref Ec2ImageId
InstanceType: !Ref Ec2InstanceType
KeyName: !Ref Ec2KeyName
Tenancy: !Ref Tenancy
EbsOptimized: !Ref EBSOptimized
DisableApiTermination: !Ref DisableApiTermination
BlockDeviceMappings:
- DeviceName: "/dev/xvda"
Ebs:
Encrypted: !Ref EBSEncryption
VolumeSize: !Ref EbsVolume00Size
VolumeType: !Ref EbsVolume00Type
DeleteOnTermination: !Ref EBSDeleteOnTermination
- DeviceName: "/dev/xvdb"
NoDevice: !If [AttachVolume01,!Ref "AWS::NoValue", {}]
Ebs:
!If
- AttachVolume01
- Encrypted: !Ref EBSEncryption
VolumeSize: !Ref EbsVolume01Size
VolumeType: !Ref EbsVolume01Type
DeleteOnTermination: !Ref EBSDeleteOnTermination
- !Ref "AWS::NoValue"
- DeviceName: "/dev/xvdc"
NoDevice: !If [AttachVolume02,!Ref "AWS::NoValue", {}]
Ebs:
!If
- AttachVolume02
- Encrypted: !Ref EBSEncryption
VolumeSize: !Ref EbsVolume02Size
VolumeType: !Ref EbsVolume02Type
DeleteOnTermination: !Ref EBSDeleteOnTermination
- !Ref "AWS::NoValue"
NetworkInterfaces:
- AssociatePublicIpAddress: "false"
DeviceIndex: "0"
SubnetId: !Ref SubnetID
GroupSet:
- {"Fn::Join" : [ ",", { "Ref" : "SecurityGroups" }] }
IamInstanceProfile: !Ref InstanceProfile
AWS固有のパラメータタイプ
一部のリソースの値はAWS固有のパラメータとして、アカウント内の既存の設定値を参照することが出ます。
このテンプレートの場合、KeyPair・Subnet・SecurityGroupはこのパラメータを利用しています。
Ec2KeyName:
Type: AWS::EC2::KeyPair::KeyName
SubnetID:
Type: AWS::EC2::Subnet::Id
SecurityGroups:
Type: "List<AWS::EC2::SecurityGroup::Id>"
CloudFormationのスタック作成画面から見ると以下のようになります。これはEC2の作成ウィザードにかなり近いですね。
この値が参照されるのは以下の箇所のSubnetIdの部分、SubnetIdを調べるのは面倒なのでリストから選択出来るのはありがたいです。
NetworkInterfaces:
- AssociatePublicIpAddress: "false"
DeviceIndex: "0"
SubnetId: !Ref SubnetID
サポートされるAWSの固有パラメータタイプの一覧については以下のドキュメントをご確認ください。
Boolean型の設定
EC2作成に関して言えば、このタイプのオプションが一番多いと思われます。
パラメータとしてはtrue/falseの二択として、どちらかを選択する形とします。
DisableApiTermination:
Type: String
Default: false
AllowedValues: [true, false]
リソース作成の箇所では!Refで参照します。
DisableApiTermination: !Ref DisableApiTermination
複数項目から単一選択を行う設定
上記の派生でtrue/falseではなく、許可された複数の項目から一つを選択する場合のオプションです。
例えば、上記で紹介したSubnetIdやInstanceTypeなどがこれに当たります。
SubnetIdは固有パラメータタイプを利用していましたが、AllowedValuesを利用することでリストから選択するように設定出来ます。
Ec2InstanceType:
Description: Select EC2 InstanceType
Type: String
AllowedValues: ["t3.micro", "t3.medium", "t3.large"]
リソース作成の箇所では!Refで参照します。
InstanceType: !Ref Ec2InstanceType
複数項目から複数選択を行う設定
今回ではSecurityGroupが該当します。SecurityGroupは固有パラメータタイプを利用していますが、固有パラメータタイプ以外を複数選択する場合はTypeにList<Number>
またはCommaDelimitedList
を利用します。
SecurityGroups:
Type: "List<AWS::EC2::SecurityGroup::Id>"
CloudFormationのコンソールだと以下のように表示され、チェックボックスにチェックする形式になっているのが分かります。
リソース作成の箇所での指定は以下の通りです。複数の値を選択出来る場合はカンマ区切りが必要となりますので、","区切りになるようJoin関数を活用しましょう。
GroupSet:
- {"Fn::Join" : [ ",", { "Ref" : "SecurityGroups" }] }
任意の文字列・数字を入力させる設定
ここではEBSのボリュームサイズやインスタンスプロファイルの指定に利用しています。TypeにNumber
もしくはString
を利用します。
EbsVolume00Size:
Type: Number
Default: 10
...
InstanceProfile:
Type: String
複数EBSのアタッチ
Conditionの設定
Conditionは設定した条件に当てはまっていればtrue
、当てはまらなければfalse
を返す設定で、これを利用することで特定の条件の場合のみリソースを作成するよう設定出来ます。
ここでは、VolumeSizeとして0
以外が入力されたときはtrue
・0
が入力されたときはfalse
となるようなAttachVolume01(02)というConditionを用意します。
Conditions:
AttachVolume01: !Not
- !Equals [!Ref EbsVolume01Size, "0"]
AttachVolume02: !Not
- !Equals [!Ref EbsVolume02Size, "0"]
Conditionの詳細な説明については下記のドキュメントをご確認ください。
EBSリソース作成の条件分岐
ここが非常にややこしいです。
全体(EBS)
- DeviceName: "/dev/xvdb"
NoDevice: !If [AttachVolume01,!Ref "AWS::NoValue", {}]
Ebs:
!If
- AttachVolume01
- Encrypted: !Ref EBSEncryption
VolumeSize: !Ref EbsVolume01Size
VolumeType: !Ref EbsVolume01Type
DeleteOnTermination: !Ref EBSDeleteOnTermination
- !Ref "AWS::NoValue"
NoDevice設定
まずEC2インスタンス作成時に合わせてEBSを作成するにはAWS::EC2::Instance BlockDeviceMapping
を使用します。
このマッピング設定でNoDevice: {}
を使用することでEBSが作成されない状態になります。
NoDeviceとIfの組み合わせ
次にCloudFormationのIf関数を利用します。If関数は"Fn::If": [condition_name, value_if_true, value_if_false]
の形で利用されます。
今回はNoDevice: !If [AttachVolume01,!Ref "AWS::NoValue", {}]
としていますので、
・AttachVolume01がtrueの場合
NoDevice: !Ref "AWS::NoValue"
・AttachVolume01がfalseの場合
NoDevice: {}
となります。
ここで登場する!Ref "AWS::NoValue
は疑似パラメータ参照と呼ばれるものの一つで、!Ifの組み合わせることで対応するリソースプロパティを削除します。つまり今回の場合はNoDevice
自体を削除するよう設定出来ます。
AWS::NoValue
Fn::If 組み込み関数の戻り値として指定すると、対応するリソースプロパティを削除します。
EBSリソースの作成
EBSの作成についてもIf関数を利用します。
・AttachVolume01がtrueの場合
指定した設定値でリソースを作成します。
Ebs:
Encrypted: !Ref EBSEncryption
VolumeSize: !Ref EbsVolume01Size
VolumeType: !Ref EbsVolume01Type
DeleteOnTermination: !Ref EBSDeleteOnTermination
NoDevice: !Ref "AWS::NoValue"
・AttachVolume01がfalseの場合
AWS::NoValue
を利用してリソースプロパティ自体を削除します。
EBS: !Ref "AWS::NoValue"
まとめ
本記事ではEC2を作成するCloudFormation Templateを作成する場合を例として、設定の種類ごとのCloudFormationのパラメータの記載方法をご紹介しました。記事内で紹介している複数EBSのアタッチように関数等を組み合わせることで、CloudFormationでも柔軟な設定を実現することが出来ます。
一方、過度な条件分岐やパラメータ化を行うとテンプレート自体の可読性が下がってしまう可能性がありますので、複雑すぎる設定作業については無理にCloudFormationのみで実現しようとせず、適切なレベル感にとどめておくことをオススメします。