インフラやクラウドの専門というわけではないのですがそろそろIaC周りもちゃんと勉強しておきたい・・・という感じなのでAWSのCloudFormationについて入門しつつ復習として記事にまとめておきます。
※とりあえず最初ということでごく基本的なところを中心に記事を書いていきます。
注意事項と前記事までの振り返り
本記事の処理を動かすとEC2関係などで色々と追加になったり起動したりします。その辺はリソースの停止や削除などをしないとお金がかかったりしてくる可能性があるためご注意ください。
また、本記事は5記事目となります。前回までの内容を踏襲していく形となっているのでご注意ください。
1記事目:
2記事目:
3記事目:
4記事目:
本記事で触れること
各種条件分岐設定(Conditions
やCondition
設定など)や分岐設定用の各種関数(Fn::Equals
やFn::If
など)について触れていきます。
条件設定の機能の概要と出来ること
- CloudFormation条件設定ではパラメータなどを参照して条件を満たす場合 / 満たさない場合でテンプレートの挙動を分岐させることができる機能です。
- 特定の条件を満たした場合に一部のリソースを作成するようにしたり、リソースに設定する属性値を変動させたり、出力値を分岐させたりすることができます。
条件設定の基本的な書き方
条件設定の各種条件部分はConditions
セクションに書きます。以下のような書き方になります。
Conditions:
<条件の任意の論理名>:
<条件判定用のFn::Equalsなどの関数指定>
例えばElastic IPアドレスを作成するかどうかという条件を定義したい場合で、条件の論理名をCreateElasticIPCondition
として条件がパラメータに指定された環境が本番環境の場合としたい場合以下のようになります(パラメータ名がEnvironment
、本番の場合のパラメータをPrd
とします)。Fn::Equals
(短縮系は!Equals
)関数とかは後の節で詳しく触れます。
Conditions:
CreateElasticIPCondition:
!Equals [!Ref Environment, Prd]
この作成した条件を参照してリソースを作成するかどうかを指定したい場合には以下のようになります。
Resources:
<リソースの任意の論理名>:
Type: <リソースのタイプ>
Condition: <対象条件の論理名>
...
上記のケースではCondition
部分にはConditions
セクション内で定義した論理名を指定します(前述の例で言うとCreateElasticIPCondition
など)。この場合Condition
で指定した条件を満たした場合にのみこのリソースが作成されます。
例えば条件を満たしたらMyPrdElasticIP
という論理名のElastic IPアドレスのリソースを作成する(条件にはCreateElasticIPCondition
を指定)・・・みたいな形にしたい場合以下のような記述になります。
...
Resources:
...
MyPrdElasticIP:
Type: AWS::EC2::EIP
Condition: CreateElasticIPCondition
Properties:
...
実際に変更セットを作成・反映してみて確認してみます。パラメータには開発環境(Dev
)もしくは本番環境(Prd
)かを選択するEnvironment
という論理名のものを用意し、もしそのパラメータが本番環境のPrd
である場合にElastic IPアドレスのリソースを作成しEC2インスタンスのリソースに設定する・・・という形でYAMLを書きます。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
Environment:
Type: String
AllowedValues:
- Dev
- Prd
Conditions:
CreateElasticIPCondition:
!Equals [!Ref Environment, Prd]
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0590f3a1742b17914
InstanceType: t3.nano
MyElasticIP:
Type: AWS::EC2::EIP
Condition: CreateElasticIPCondition
Properties:
InstanceId: !Ref MyEC2Instance
YAMLをアップロードして変更セットを反映する前にEC2のElastic IP アドレスのページを確認しておきます。現時点では以下のように1つもElastic IP アドレスが存在しない形になっています。
変更セットをアップロードして反映していきます。
パラメータのEnvironment
の値はまずはDev
を選択します。この場合Conditions
のCreateElasticIPConditionの条件を満たさなくなるのでElastic IP アドレス関係のリソースは作成されなくなります。
反映してイベントがUPDATE_COMPLETE
になるまで待った後に確認してみてもElastic IP アドレス関係のイベントは発生していないことが確認できます。
EC2のElastic IP アドレスのページを見てみても特に追加などはされていないことが確認できます。
続いて同じYAMLテンプレートで、Environment
のパラメータだけPrd
に変更してみます。
変更セットの確認画面でもElastic IP アドレス関係のアクションがAddになっていることを確認できます。
反映後のイベント画面でも各種Elastic IP アドレス関係のものが実行されていることが確認できます。
EC2のElastic IP アドレスのページを見てみると正常に追加がされていることが確認できます。
EC2インスタンスのページで対象のインスタンスを確認してみても追加となったElastic IP アドレスが反映されていることが確認できます。
このように同じYAMLテンプレートでありながらConditions
の設定による分岐でリソースを生成を分岐させることができました。
分岐条件によってリソースが作成されなくなった場合の挙動
既に作成済みのリソースが選択したパラメータなどによって作成されなくなった場合にどうなるのかを見てみます。
前節で使ったYAMLテンプレートとスタックをそのまま使用していきます。再度Environment
のパラメータにDev
を選択します。前回はPrd
でElastic IP アドレスが作成する条件になっていたものが反映されていますが今度はそのリソースが作成されなくなります。
変更セットを作成して確認してみると対象のElastic IP アドレスが削除(Remove)される形になりました。どうやら作成済みのリソースが分岐条件によって作成されなくなった場合にはそのまま残る形ではなく削除されるようです。
反映してみるとElastic IP アドレスなどが削除されていることが確認できます。
EC2のElastic IP アドレスのページを確認してみても一覧から消えていることが確認できます。
Fn::Equals
関数
前節までで使ってきましたがFn::Equals
関数はリスト内の2つの値が等しいかどうかの真偽値を取得する関数です。一致していればtrue、一致していなければfalseの真偽値を返します。主にConditions
セクションなどで使います。
値は2つのインデックスを持つリストで指定して、比較用の各値を2つのインデックス位置に設定します。
例えばFn::Equals [<1つ目の値>, <2つ目の値>]
といったように書きます。短縮系では!Equals [<1つ目の値>, <2つ目の値>]
といった感じになります。
前節までで!Equals
関数を使って変更セットの作成などまで対応しているのでこの節ではその辺は省略しますが、例えばEnvironment
というパラメータがPrd
かどうかという条件であれば以下のような記述となります。
Conditions:
CreateElasticIPCondition:
!Equals [!Ref Environment, Prd]
Fn::And
関数
Fn::And
関数はリスト内の各インデックスの条件(Condition
)の真偽値が全てtrueであればtrueとなる関数です。3つ以上の条件を指定することもできます。基本的に真偽値を得るためにFn::Equals
関数などをリスト内に入れる形となります。また、Conditions
セクションで定義されている条件単体(Condition
)の値もドキュメントを見ている感じ!Condition
という記述で指定できるようです(Fn::Condition
という関数があるのだろうか?と軽く検索してみたのですがヒットしなかったのでこの場合は!Condition
の短縮系のみある感じでしょうか・・・?)。
例えば以下のような記述になります。
Fn::And:
- Fn::Equals: [<1つ目の値>, <2つ目の値>]
- !Condition <定義されている条件の論理名>
短縮系では!And [<各条件の値>]
という形です。以下のような記述となります。
!And
- !Equals [<1つ目の値>, <2つ目の値>]
- !Condition <定義されている条件の論理名>
試しとして雑ですが以下のYAMLでUserName
というユーザー名選択用のパラメータと環境選択用のEnvironment
という2つのパラメータを用意しました。また、環境が本番(Prd
)且つ対象ユーザーがB(UserB
)の場合にElastic IP アドレスのリソースを設定する(リソースでその辺を作成する)という条件を指定しています。
お試しなので!And
関数内では!Equals
と!Condition
の関数を両方使う形で設定してみます。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
Environment:
Type: String
AllowedValues:
- Dev
- Prd
UserName:
Type: String
AllowedValues:
- UserA
- UserB
Conditions:
IsPrdEnv:
!Equals [!Ref Environment, Prd]
IsUserBAndPrdEnv:
!And
- !Equals [!Ref UserName, UserB]
- !Condition IsPrdEnv
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0590f3a1742b17914
InstanceType: t3.nano
MyElasticIP:
Type: AWS::EC2::EIP
Condition: IsUserBAndPrdEnv
Properties:
InstanceId: !Ref MyEC2Instance
変更セットの作成や反映をしていきます。試しにまずは環境のパラメータはPrd
、ユーザー名のパラメータはUserB
を選択します。環境とユーザー名両方の条件で!And
関数の条件を満たすためElastic IP アドレスのリソースが作成される想定です。
変更セットの変更内容を見てみるとElastic IP アドレス関係が追加される表示になっていることを確認できます。反映前に!And
の条件の結果がどうなるかとかが見れるのは良いですね。
反映後のイベントもElastic IP アドレス関係の更新が走っていることが確認できます。
今度は同じテンプレートで再度変更セットを作成して環境のパラメータはDev
の方を選択します。!And
関数の条件を満たさなくなるためElastic IP アドレスのリソースが作成されず削除される想定です。
変更セットの変更内容の確認画面では想定通りElastic IP アドレスのアクションがRemoveになっていることが確認できます(そのまま変更セットを反映しておきます)。
Fn::Or
関数
Fn::And
関数があるならOr
関係の関数もあります・・・ということでFn::Or
関数について触れていきます。
書き方的にはFn::And
関数と同じような形でリストで複数の条件(Condition
として定義したものやFn::Equals
関数など)を以下のように指定します。
Fn::Or:
- Fn::Equals: [<1つ目の値>, <2つ目の値>]
- !Condition <定義されている条件の論理名>
短縮系では!Or [<各条件>]
といった具合で以下のようになります。
!Or
- Fn::Equals: [<1つ目の値>, <2つ目の値>]
- !Condition <定義されている条件の論理名>
試しに以下のようにYAMLテンプレートを調整してみます。
- 環境のパラメータ(
Environment
)にステージングとしてStg
の選択肢を追加します。 -
Conditions
にIsPrdOrStgEnv
という名前の条件で選択されパラメータがPrd
もしくはStg
かどうかという条件を追加しました。 - もし
IsPrdOrStgEnv
の条件が満たされていればElastic IP アドレスのリソースが生成・設定されるようにしています。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
Environment:
Type: String
AllowedValues:
- Dev
- Stg
- Prd
Conditions:
IsPrdOrStgEnv:
!Or
- !Equals [!Ref Environment, Prd]
- !Equals [!Ref Environment, Stg]
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0590f3a1742b17914
InstanceType: t3.nano
MyElasticIP:
Type: AWS::EC2::EIP
Condition: IsPrdOrStgEnv
Properties:
InstanceId: !Ref MyEC2Instance
変更セットを作成し、まずはパラメータでPrd
を選択してみます。Elastic IP アドレスのリソースが作成・設定される想定です。
変更セットの変更内容の確認画面では想定通りElastic IP アドレスが設定される形になっています(このまま反映します)。
続いてYAMLテンプレートはそのままで今度は環境のパラメータでDev
を選択します。Elastic IP アドレスが削除される想定です。
変更確認画面でElastic IPアドレスのアクションが想定通りRemoveになっていることが確認できます(このまま反映します)。
最後に環境のパラメータでStg
を選択します。!Or
関数的にこの場合もElastic IP アドレスのリソースが作成・設定される想定です。
確認画面で想定通りElastic IP アドレスのアクションがAddになっていることが確認できました(このまま反映しておきます)。
Fn::Not
関数
Fn::Not
関数では指定された条件(Condition
)の逆の真偽値を取得します。例えばtrueとなる条件であればfalse、falseであればtrueの真偽値を取得できます。
書き方としてはFn::Not: [<条件の指定>]
といった具合になります。短縮系では!Not [<条件の指定>]
となります。
条件部分にはFn::Equals
関数やConditions
のセクションに定義した条件などを指定します。例えば!Not [!Equals [!Ref Environment, Prd]]
といった記述となります。
以下の例では環境のパラメータ(Environment
)で開発環境(Dev
)以外を選択したかどうかという条件でIsNotDevEnv
という名前でConditions
セクションに条件を追加しています。条件は!Not
関数を使って!Not [!Equals [!Ref Environment, Dev]]
としています。
また、もし該当の条件を満たす場合にはElastic IP アドレスの設定がEC2に設定されるようにしています。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
Environment:
Type: String
AllowedValues:
- Dev
- Stg
- Prd
Conditions:
IsNotDevEnv:
!Not [!Equals [!Ref Environment, Dev]]
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0590f3a1742b17914
InstanceType: t3.nano
MyElasticIP:
Type: AWS::EC2::EIP
Condition: IsNotDevEnv
Properties:
InstanceId: !Ref MyEC2Instance
変更セットの作成と反映を行っていきます。まずはパラメータでDev
を選択し、条件がfalseとなってElastic IP アドレスが反映されない想定で進めます。
変更セットの変更内容を見ると、想定通りElastic IP アドレスがRemoveになっていることが確認できます(そのまま反映します)。
続いてもう一度同じYAMLテンプレートを追加して、環境のパラメータにPrd
を選択します。Dev
以外が選択されたので条件がtrueとなるのでElastic IP アドレスが追加される想定です。
変更確認画面で想定通り条件がtrueとなりElastic IP アドレスが追加になっていることが確認できました(そのまま反映します)。
Fn::If
関数
Fn::If
関数は条件を満たした場合と満たさない場合のそれぞれの値の分岐を設定できる関数です。特定の属性部分などで使用します。
書き方としてはFn::If: [<条件の論理名>, <真の時に設定する値>, <偽の時に設定する値>]
といったように値はリストで指定し、最初のインデックスにはConditions
セクションで定義した条件の論理名、2番目のインデックスには条件を満たす場合に設定する値、3番目のインデックスには条件を満たさない場合に設定する値の3つを指定します。
短縮系では!If [<条件の論理名>, <真の時に設定する値>, <偽の時に設定する値>]
という書き方になります。
以下の例では環境選択用のEnvironment
というパラメータを設けて、選択されたパラメータが開発環境(Dev
)かどうかの条件(IsDevEnv
)をConditions
セクションに設けています。
さらに、そのIsDevEnv
の条件を満たした場合にはEC2インスタンスのインスタンスタイプをt3.nano
、満たさない場合にはt3.micro
に値を分岐するようにしています。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
Environment:
Type: String
AllowedValues:
- Dev
- Prd
Conditions:
IsDevEnv:
!Equals [!Ref Environment, Dev]
Resources:
MyEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0590f3a1742b17914
InstanceType: !If [IsDevEnv, t3.nano, t3.micro]
変更セットを作って試してみます。まずはパラメータにはPrd
を選択します(条件を満たさないのでインスタンスタイプがt3.micro
になる想定)。
反映後のイベントが完了してからEC2インスタンスのページに遷移してみるとインスタンスタイプが想定通りt3.micro
になっていることを確認できます。
続いて同じYAMLテンプレートでパラメータでDev
を選択してみます。条件を満たすのでインスタンスタイプがt3.nano
になる想定です。
再度EC2インスタンスのページを開いて見ると想定通りインスタンスタイプがt3.nano
になっていることが確認できます。
参考文献・参考サイト・参考講座まとめ