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

# はじめに

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

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

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

## 例

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

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

Outputs:
  SampleBucketName:
    Value: !Ref SampleBucket
```

```yaml:!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``` を使うと、一行でシンプルに書けて可読性も良くなります。

## 例

```yaml:!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
                - /*
```

```yaml:!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``` と同様で、単体で使うメリットはありませんが、文字列結合が必要な場合などに有用です。


## 例

```yaml:!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
                - /*
```

```yaml:!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 でしたが、本記事を書こうと思ったのは、

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

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

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

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-policy.html#aws-properties-s3-policy--examples

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


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