AWS CloudFormation で組み込み関数を併用


概要

AWS CloudFormation でテンプレートを作成していると、
ParametersMappings を連結したくなったり、
!If の中で 組み込み関数 を使いたくなったりと、
組み込み関数 を併用して使いたくなることがたまにある。

使い方を忘れがちなので具体的な使用例を備忘録として記録します。
間違ってるところがあったら教えてください!

併用でなく単体で組み込み関数を使いたい場合は公式のドキュメント見た方がいいかも
組み込み関数リファレンス - AWS CloudFormaion

  • 使用例の見方と注意点
    • YAML 形式で書いてます(一部 JSON を併用)
    • Key: function の Value と直後の Key: result の Value は同じことを表しています。

使用例

!If の中で組み込み関数を使いたい!

!If, !FindInMap, !Ref, !Sub の併用

使用例
Parameters:
  Env:
    Type: String
    Default: "Prod"
    AllowedValues: [ "Prod", "Stg" ]

Conditions:
  IsProd: !Equals [ !Ref Env, "Prod" ]   # 条件式 Env = Prod の意

Mappings:
  Tags:
    japanese:
      Prod: "honban"
      Stg: "sute-jingu"

Resources:
# ~省略~
      Tags:
        # 普通に !If
        - Key: function
          Value: !If
            - IsProd        # Conditions で設定した条件式 (この例では Parameters の Env が Prod なので 真)
            - "Production"  # 真の場合 (この例ではコッチ)
            - "Staging"     # 偽の場合
        - Key: result
          Value: "Production"

        # 普通に !FindInMap
        - Key: function
          Value: !FindInMap [Tags, Japanese, Prod]
        - Key: result
          Value: "honban"

        # If の中で !FindInMap を使う
        - Key: function
          Value: !If
            - IsProd
            - !FindInMap [Tags, Japanese, Prod]
            - "Staging"
        - Key: result
          Value: "honban"

        # !If の中で !FindInMap を使う(別パターン)
        - Key: function
          Value: !If
            - IsProd
            - !FindInMap
              - Tags
              - Japanese
              - Prod
            - "Staging"
        - Key: result
          Value: "honban"

        # !If の中で !FindInMap を使って !FindInMap の中で !Ref を使う
        - Key: function
          Value: !If
            - IsProd
            - !FindInMap
              - Tags
              - Japanese
              - !Ref Env
            - "Staging"
        - Key: result
          Value: "honban"

        # !If の中で !Sub    
        - Key: function
          Value: !If
            - IsProd
            - !Sub ${Env}-hoge
            - "Staging"
        - Key: result
          Value: "Prod-hoge"

ParametersMappings を連結したい!

!Join を使う

!Join!FindInMap (と!Ref) の併用

使用例
Parameters:
  Env:
    Type: String
    Default: "Prod"
Mappings:
  Tags:
    Service:
      Prod: "Business"
      Stg: "Game"
Resources:
# ~省略~
        # !Join の中で !FindInMap を使って、FindInMap の中で !Ref を使う
        - Key: function
          Value: !Join [ "-", [ !FindInMap [ Tags, Service, !Ref Env ], '1' ] ]
        - Key: result
          Value: "Business-1"

        # ↑の値を !Join で !Ref と連結 (Join の中の Join の中で FindInMap と !Refを使う)
        - Key: function
          Value: {'Fn::Join': [ ".", [ !Ref Env, !Join [ "-", [ !FindInMap [Tags, Service, !Ref Env ], '1' ] ] ] ] }
        - Key: result
          Value: "Prod.Business-1"

!Join わかりずらいので嫌い
!Sub の方が良い

!Sub 使う (※こっちのがオススメ)

!SubFindInMap (と !Ref)を併用

使用例
Parameters:
  Env:
    Type: String
    Default: "Prod"
Mappings:
  Tags:
    Service:
      Prod: "Business"
      Stg: "Game"
Resources:
# ~省略~
      Tags:
        - Key: function
          Value: !Sub
            - ${Env}.${hoge}-1
            - { hoge: !FindInMap [Tags, Service, !Ref Env] }
        - Key: result
          Value: "Prod.Bussiness-1"

!Join よりはマシだけどまだわかりずらい
いい方法ないかな。。。

!ImportValue の呼び出し値をパラメーター化したい!

!ImportValue!Sub の併用

スタック名:prod-vpc-stack(参照元)
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16

Outputs:
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub ${AWS::StackName}-VpcId
スタック名:prod-ec2-stack(参照先)
Parameters:
  Env:
    Type: String
    Default: "prod"
  ImportStackName:
    Type: String
    Default: "prod-vpc-stack"

Mappings:
  ImportValue:
    VpcId:
      prod: prod-vpc-stack-VpcId
      stg: stg-vpc-stack-VpcId

Resources:
#  ~省略~
        # 普通に !ImportValue
        - Key: function
          Value: ImportValue: prod-vpc-stack-VpcId

        # !ImportValue と !Sub を併用
        - Key: function
          Value: {'Fn::ImportValue': !Sub '${ImportStackName}-VpcId'}

        - Key: function
          Value: {'Fn::ImportValue': !Sub '${Env}-vpc-stack-VpcId'}

        # !ImportValue と !FindInMap を併用
        - Key: function
          Value: { "Fn::ImportValue" : { "Fn::FindInMap" : [ "ImportValue", "VpcId", !Ref Env ] } }

# いずれも prod-vpc-stack(参照元) の VPCID を取得できる

!GetAZs を1行にしたい

アベイラビリティゾーンをベタ書きしてしまうと汎用性に欠けるし
cfn-python-lint で怒られてしまうので !GetAZs を使う

こんなエラー
W3010 Don't hardcode ap-northeast-1a for AvailabilityZones

使用例(ap-northeast-1の場合)

Resources:
# ~省略~
      tags:
        # 公式に書いてある方法だとこんな感じ
        - Key: function
          Value: !Select
          - 0
          - Fn::GetAZs: !Ref 'AWS::Region'
        # JSON 使って1行にしてみる
        - Key: function
          Value: { "Fn::Select" : ["0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" } } ] }
        - Key: result
          Value: "ap-northeast-1a"

        - Key: function
          Value: { "Fn::Select" : ["1", { "Fn::GetAZs" : { "Ref" : "AWS::Region" } } ] }
        - Key: result
          Value: "ap-northeast-1c"

        - Key: function
          Value: { "Fn::Select" : ["2", { "Fn::GetAZs" : { "Ref" : "AWS::Region" } } ] }
        - Key: result
          Value: "ap-northeast-1d"

{ "Fn::GetAZs" : { "Ref" : "AWS::Region" } }
[ap-northeast-1a, ap-northeast-1c, ap-northeast-1d] のような配列になっているイメージ。 ※ ap-northeast-1 の場合
その配列から、Selectで0,1,2番目を抜き出している。