CloudFormation テンプレート(YAML)内の JSON を環境ごとに変更する

AWS

はじめに

CloudFormation テンプレートは YAML、JSON 2種類での記述が可能です。
YAMLで記述する場合も、一部 JSON をそのまま記載することができ、例えば IAM ポリシーについては既存の JSON 定義をそのまま利用できるため JSON で記述した方が簡単です。
CloudFormation テンプレートの再利用性を上げるため、本記事ではテンプレートないの JSON をパラメータで変更する方法を記載します。

サンプルコード

例として、マネジメントコンソールから CodeBuild のポリシーを作成した場合に自動的に作成される IAM ポリシーを作成するCloudFormationテンプレートを示します。
Resources > Policy > Properties > PolicyDocument には作成する IAM ポリシーの定義を JSON で記載しています。
以下がポイントです。

  • IAM ポリシー内の Resource 句には対象のリソースの ARN を記載する必要がありこの値はリージョンや AWS アカウントごとに異なります。
    そのため、置換対象の文字列の前に"Fn::Sub":を加え、それぞれ${AWS::Region}${AWS::AccountId}と記載することで実行される環境に応じて変化させています。
  • JSON 内の値をスタック作成時の入力パラメータ(以下例のTestName)で指定することも可能です。
    リージョンや AWS アカウントと同様に、${TestName}とすることで可変にしています。
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  TestName:
    Type: String

Resources:
  Policy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Path: /
      ManagedPolicyName: !Sub CodeBuildBasePolicy-${TestName}-${AWS::Region}
      PolicyDocument:
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Resource": [
                        "Fn::Sub":"arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${TestName}",
                        "Fn::Sub":"arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${TestName}:*"
                    ],
                    "Action": [
                        "logs:CreateLogGroup",
                        "logs:CreateLogStream",
                        "logs:PutLogEvents"
                    ]
                },
                {
                    "Effect": "Allow",
                    "Resource": [
                        "Fn::Sub":"arn:aws:s3:::codepipeline-${AWS::Region}-*"
                    ],
                    "Action": [
                        "s3:PutObject",
                        "s3:GetObject",
                        "s3:GetObjectVersion",
                        "s3:GetBucketAcl",
                        "s3:GetBucketLocation"
                    ]
                },
                {
                    "Effect": "Allow",
                    "Action": [
                        "codebuild:CreateReportGroup",
                        "codebuild:CreateReport",
                        "codebuild:UpdateReport",
                        "codebuild:BatchPutTestCases",
                        "codebuild:BatchPutCodeCoverages"
                    ],
                    "Resource": [
                        "Fn::Sub":"arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/${TestName}-*"
                    ]
                }
            ]
        }

また、Mappings を使用して文字列を可変にすることも可能です。
以下例では、環境に応じて変わる値を、実行時のパラメータに基づいて可変にしています。

  • Parameters 句で入力時に develop or production のいずれかを Env に設定する
  • 環境にによって変わる値を Mappings で事前に定義している
  • JSON 内では"Fn::FindInMap":[EnvMap, !Ref Env, S3Arn]とすることで、Mappings で定義した値で置換している
### 省略 ###
Parameters:
  Env:
    Type: String
    AllowedValues:
      - develop
      - production

Mappings:
  EnvMap:
    develop:
      S3Arn: arn:aws:s3:::develop-bucket/*
    production:
      S3Arn: arn:aws:s3:::production-bucket/*

Resources:
  Policy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Resource": [
                        "Fn::FindInMap":[EnvMap, !Ref Env, S3Arn]
                    ],
### 省略 ###

おわりに

本記事では、CloudFormation 内の JSON の一部を変数にする方法についてまとめました。
Fn::Sub や Fn::FindInMap を使用することで複数の環境で使用可能な再利用性の高いテンプレートを作成することが可能になります。
この記事がどなたかの参考になれば幸いです。

参考

コメント