CloudFormation のテンプレートに外部ファイルで定義したパラメータを読み込ませる方法

AWS::Include transform を利用することで、CloudFormation のテンプレート内に別で作成したテンプレートの中身を読み込むことができます。

困っていた内容

CloudFormation で コンテナイメージから Lambda 関数を作成したいと考えております。 以下のようなテンプレートを使って、Lambda 関数の環境変数を別ファイルから読み込みたいのですが方法を教えて下さい。

また、CloudFormation のテンプレートは YAML 形式で記述して、読み込む外部ファイルは JSON 形式の様にして、異なるファイル形式でも可能でしょうか。

  • CloudFormation テンプレート(YAML)
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ImageUri: xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/nakano-lambda-python-image:latest
      FunctionName: testFunctionOnDocker
      Role: arn:aws:iam::xxxxxxxx:role/nakano-lambda-basic-role
      PackageType: Image
      Environment:
        !Ref LambdaVariables # <- key-valueで定義した環境変数をセット
  • 外部ファイル(JSON)
{
    "LambdaVariables": {
        "Name":"test",
        "Environment":"dev"
    }
}

どう対応すればいいの?

AWS::Include transform を利用することで、CloudFormation のテンプレート内に別で作成したテンプレートの中身を読み込むことができます。

テンプレートに定型コンテンツを挿入するには、AWS CloudFormation がホストするマクロである AWS::Include transform を使用します。AWS::Include transform により、Amazon S3 バケット内のテンプレートスニペットへのリファレンスを作成することができます。変更セットの作成、または 変更セットを使用したスタックの更新、およびテンプレートが AWS::Include を参照すると、AWS CloudFormation は指定されたファイルの内容をテンプレート内の transform の場所に挿入します。AWS::Include 関数の動作は、プログラミング言語の include、copy、import ディレクティブに似ています。

AWS::Include transform - AWS CloudFormation

また、YAML で記述した CloudFormation のテンプレートに、JSON ファイルで記述した別のテンプレートを読み込んだパラメータを読み込むことは可能です。 その逆も可能です。

テンプレートとスニペットを作成する場合、YAML と JSON テンプレート言語を組み合わせることができます。

AWS::Include transform - AWS CloudFormation

やってみた

Lambda 用の Dockerfile を作成します。

  • Dockerfile
FROM public.ecr.aws/lambda/python:3.8
COPY app.py ./
CMD ["app.lambda_handler"]

Lambda 関数のソースファイルは以下の様にします。

  • app.py
import json

def lambda_handler(event, context):

    return {
        "statusCode": 200,
        "body": json.dumps(
            {
                "message": "hello, nakanosan!",
            }
        ),
    }

以下のディレクトリ構成で配置します。

$ tree
.
├── Dockerfile
└── app.py

上記の内容を、ECR に登録します。 ECR への登録方法は以下を参照ください。

Docker イメージをプッシュする - Amazon ECR

S3 に配置する Lambda 関数の環境変数の定義されたテンプレートを作成します。

  • environment.json
{
    "Variables": {
        "Name": "test",
        "Env": "dev"
    }
}

上記を作成したら、S3 バケットを作成して environment.json をアップロードします。

次に、CloudFormation テンプレートを定義します。 以下のテンプレートを作成します。

ImageUri に ECR に登録したイメージ URI を記載します。 また、Environment の Location 部分で、S3 に配置した外部ファイル(スニペット)を指定します。

  • template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ImageUri: xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/nakano-lambda-python-image:latest
      Environment:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: s3://nakano-cfn-include-bucket/environment.json
      FunctionName: nakanoFunctionOnDocker
      Role: arn:aws:iam::xxxxxxxxxxxx:role/nakano-lambda-basic-role
      PackageType: Image

準備は整ったので、CloudFormation のテンプレートからスタックを作成します。 スタック作成が完了したら、Lambda 関数のコンソールで指定した環境変数が含まれているか確認します。

補足

もちろん、YAML のテンプレートに YAML の外部ファイルで読み込むことも可能です。

  • template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ImageUri: xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/nakano-lambda-python-image:latest
      Environment:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: s3://nakano-cfn-include-bucket/environment.yaml
      FunctionName: nakanoFunctionOnDocker
      Role: arn:aws:iam::xxxxxxxxxxxx:role/nakano-lambda-basic-role
      PackageType: Image
  • environment.yaml
Variables:
  Name: test
  Env: dev

参考資料