【CloudFormation】組み込み関数Fn::Subが意外と便利な件


はじめに

ちょっとした Tips ですが、意外と知らない方が多い(?)ようなので、投稿しておきます。
CloudFormation の組み込み関数 !Sub は、 !Ref!GetAtt など、他の組み込み関数の代わりとしても使用することができます。

!Ref の代わりとして使える

!Ref Resource!Sub ${Resource} は同義です。
ただし、!Ref の方が ${} が不要な分、シンプルなので、単体ではあえて !Sub を使うメリットはありません。

!Ref の場合
Parameters:
  BucketName:
    Type: String

Resources:
  SampleBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName

Outputs:
  SampleBucketName:
    Value: !Ref SampleBucket
!Sub の場合
Parameters:
  BucketName:
    Type: String

Resources:
  SampleBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${BucketName}

Outputs:
  SampleBucketName:
    Value: !Sub ${SampleBucket}

!Join の代わりとして使える

!Sub が有用なのはこのケースです。
以下のように、ARN を !Join で結合している記法を割と見かけますが、!Sub を使うと、一行でシンプルに書けて可読性も良くなります。

!Join の場合
SampleBucketPolicy:
  Type: AWS::S3::BucketPolicy
  Properties:
    Bucket: !Ref SmapleBucket
    PolicyDocument:
      Version: 2012-10-17
      Statement:
        - Effect: Allow
          Action:
            - s3:GetObject
          Principal: "*"
          Resource:
            - !Join
              - ""
              - - arn:aws:s3:::
                - !Ref SmapleBucket
                - /*
!Sub の場合
SampleBucketPolicy:
  Type: AWS::S3::BucketPolicy
  Properties:
    Bucket: !Ref SmapleBucket
    PolicyDocument:
      Version: 2012-10-17
      Statement:
        - Effect: Allow
          Action:
            - s3:GetObject
          Principal: "*"
          Resource:
            - !Sub arn:aws:s3:::${SmapleBucket}/*

!GetAtt の代わりとして使える

!GetAtt Resource.Attribute!Sub ${Resource.Attribute} は同義です。
これも !Ref と同様で、単体で使うメリットはありませんが、文字列結合が必要な場合などに有用です。

!GetAtt の場合
SampleBucketPolicy:
  Type: AWS::S3::BucketPolicy
  Properties:
    Bucket: !Ref SmapleBucket
    PolicyDocument:
      Version: 2012-10-17
      Statement:
        - Effect: Allow
          Action:
            - s3:GetObject
          Principal: "*"
          Resource:
            - !Join
              - ""
              - - !GetAtt SmapleBucket.Arn
                - /*
!Sub の場合
SampleBucketPolicy:
  Type: AWS::S3::BucketPolicy
  Properties:
    Bucket: !Ref SmapleBucket
    PolicyDocument:
      Version: 2012-10-17
      Statement:
        - Effect: Allow
          Action:
            - s3:GetObject
          Principal: "*"
          Resource:
            - !Sub ${SmapleBucket.Arn}/*

リファレンス

これらの仕様は CloudFormation ユーザーガイドにも明記されています。

テンプレートパラメータ名、または ${InstanceTypeParameter} などのリソースの論理 ID を指定すると、CloudFormation は Ref 組み込み関数を使用した場合と同じ値を返します。
${MyInstance.PublicIp} などのリソース属性を指定すると、CloudFormation は Fn::GetAtt 組み込み関数を使用した場合と同じ値を返します。

引用:https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html

おわりに

簡単な Tips でしたが、本記事を書こうと思ったのは、

  Resource:
    - !Join
      - ""
      - - arn:aws:s3:::
        - !Ref SmapleBucket
        - /*

のような記法を意外とよく見かけることがあり、「!Sub を使えば一行で書けるのに…」と思ったことがきっかけでした。

この記法をよく見かけるのはおそらく、AWS の公式ドキュメントにおいても、この記法が使われているためではないかと推測しています。
例えば、以下の Examples など。

CloudFormation テンプレートでも、プログラムでも、シンプルに書けるのであれば、シンプルに書いたほうがよいと思います。

最後まで読んでいただき、ありがとうございます。
本記事が少しでも役に立てば幸いです。