のべラボ.blog

Tech Blog | AWS や サーバーレスやコンテナ などなど

AWS CloudFormation の組込み関数 Ref と Sub について

仕事柄、様々な AWS CloudFormation テンプレートを参照することがありますが、あるテンプレートを見ていた時、ふと、「あれ?ここで使っているは 組込み関数は Subでなくて Refでもいいんじゃない?」と気づくことがありました。

その時の経験から、組込み関数 Ref と Sub についてまとめてみたいと思います。

AWS CloudFormation の組込み関数については、次のドキュメントもあるので、あわせてご参照ください。

組み込み関数リファレンス | AWS CloudFormation ユーザーガイド


まず、次のCloudFormation テンプレートのサンプルをみてみましょう。 これは、Amazon S3 バケットをパラメータで指定した名前で 作成するテンプレートです。

Parameters:
  BucketName1:
    Description: 'Bucket Name #1'
    Type: String
    Default: 'tnobe-sample1-bucket-1'
  BucketName2:
    Description: 'Bucket Name #2'
    Type: String
    Default: 'tnobe-sample1-bucket-2'
Resources:
  MyBucket1:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: !Ref BucketName1   # Ref関数で参照
  MyBucket2:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: !Sub ${BucketName2} # Sub関数で参照

このテンプレートでは、Parameters セクションのパラメータ名を Ref 関数と Sub 関数で参照して、S3のバケット名に指定しています。 Ref 関数では、そのままパラメータ名 (BucketName1) を指定していますが、Sub 関数では、${BucketName2}と、$ { }で囲んでいます。

記述方法は異なりますが、どちらもパラメータの値を参照することができます。


次に、2つ目のサンプルをみてみましょう。

Parameters:
  BucketName1:
    Description: 'Bucket Name #1'
    Type: String
    Default: 'tnobe-sample2-bucket-1'
  BucketName2:
    Description: 'Bucket Name #2'
    Type: String
    Default: 'tnobe-sample2-bucket-2'
Resources:
  MyBucket1:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: !Ref BucketName1
        Tags:
        - Key: Region
          Value: !Ref 'AWS::Region'   # 疑似パラメータをRef関数で参照
  MyBucket2:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: !Sub ${BucketName2}
        Tags:
        - Key: Region
          Value: !Sub ${AWS::Region}  # 疑似パラメータをSub関数で参照

2つ目のテンプレートでは、AWS::Region という疑似パラメータを Ref 関数と Sub 関数で参照して、S3バケットのタグの値を設定しています。

疑似パラメータとは、事前に定義されているパラメータで、例えば AWS::Region であれば、そのテンプレートでCloudFormationスタックを作成するAWSのリージョンのIDが自動的に設定されます。

次のドキュメントに他の疑似パラメータの記載もあるので、参考にしてください。

擬似パラメータ参照 | AWS CloudFormation ユーザーガイド

この2つ目のサンプルでも、Ref 関数では、そのまま疑似パラメータ (AWS::Region) をそのまま指定していますが、Sub 関数では、${AWS::Region}と、$ { }で囲んでいます。

記述方法は異なりますが、疑似パラメータであっても、Ref 関数でも Sub 関数でも値を参照することができます。


では、3つ目のサンプルをみてみましょう。

Parameters:
  BucketId1:
    Description: 'Bucket Id #1'
    Type: String
    Default: '1'
  BucketId2:
    Description: 'Bucket Id #2'
    Type: String
    Default: '2'
Resources:
  MyBucket1:
      Type: AWS::S3::Bucket
      Properties:
        # Ref関数で参照した値を他の文字列と連結したい場合
        BucketName: !Join 
                      - ''
                      - - 'tnobe-sample3-bucket-'
                        - !Ref BucketId1
  MyBucket2:
      Type: AWS::S3::Bucket
      Properties:
        # Sub関数で参照した値を他の文字列と連結したい場合
        BucketName: !Sub 'tnobe-sample3-bucket-${BucketId2}'

3つ目のサンプルでは、文字列 tnobe-sample3-bucket- と、パラメータの値を連結して、バケット名に設定したいという意図があります。

この場合、Ref 関数を使う方法と Sub 関数を使う方法では、記述方法が異なることがわかります。

Ref 関数の場合は、他の文字列と連携つするために、 Join という関数を使っています。

一方、Sub 関数の場合は、文字列の中に、${BucketId2} が埋め込ている形で記述します。

この場合だと、Ref関数 + Join 関数の記述より、Sub 関数の方がシンプルに記述できますね。


まとめ

  • AWS CloudFormation の組込み関数 Ref と Sub では、単一の値を参照するという面では、どちらも同じように使用できます。
  • AWS CloudFormation のドキュメントにも記載はありますが、もともと Ref 関数は、パラメータやテンプレートのリソースの論理IDを参照する用途で使用します。 また、Ref 関数であれば、${ } で囲む必要がないので、単一の値を参照するだけであれば、Ref 関数の使用が適切でしょう。
  • ただし、文字列を連結したい場合は、Sub 関数の方がシンプルに記述できるということは、覚えておいた方が良さそうですね!
/* -----codeの行番号----- */