こうこく
作 ▸

AWS CDKで生成するテンプレートからMetadataとCDKMetadataとBootstrapVersionを除去

要するに手書きと同じようなテンプレートを直接出力する方法です

  • Metadata を消す … CLIコマンドで --path-metadata false を指定
  • CDKMetadata を消す … CLIコマンドで --no-version-reporting を指定、または cdk.json で versionReporting: false を指定
  • BootstrapVersion を消す … スタックをnewするとき、オプション synthesizergenerateBootstrapVersionRule: false をセットした DefaultStackSynthesizer インスタンスを指定する

あとおまけです

  • AWSTemplateFormatVersion を追加する … スタック内で this.templateOptions.templateFormatVersion をセット
  • Description を追加する … スタック内で this.templateOptions.description をセット
aws-cdk 2.50.0

例えば以下のコードは、イベント内容をそのままレスポンスするLambda関数URLを含む単純なスタックをCDKで作成するためのもの。ここではリソースはすべて Cfn* で記載してる。

cdk.ts
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';

class MyStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // パラメータ
    const paramLambdaTimeout = new cdk.CfnParameter(this, 'LambdaTimeout', {
      type: 'Number',
      default: 3,
    });

    // IAM: Lambda実行ロール
    const executionRole = new iam.CfnRole(this, 'LambdaExecutionRole', {
      assumeRolePolicyDocument: {
        Version: '2012-10-17',
        Statement: [
          {
            Effect: 'Allow',
            Principal: { Service: ['lambda.amazonaws.com'] },
            Action: ['sts:AssumeRole'],
          },
        ],
      },
      policies: [
        {
          policyName: 'LoggingPolicy',
          policyDocument: {
            Version: '2012-10-17',
            Statement: [
              {
                Effect: 'Allow',
                Action: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
                Resource: `arn:aws:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`,
              },
            ],
          },
        },
      ],
    });

    // Lambda: 適当な関数
    const lambdaFunction = new lambda.CfnFunction(this, 'MyFunction', {
      runtime: 'nodejs16.x',
      handler: 'index.handler',
      timeout: paramLambdaTimeout.valueAsNumber,
      role: executionRole.attrArn,
      code: {
        zipFile: `exports.handler = async (event) => ({ statusCode: 200, body: JSON.stringify(event) });`,
      },
    });

    // 関数URL
    const functionUrl = new lambda.CfnUrl(this, 'MyFunctionURL', {
      targetFunctionArn: lambdaFunction.attrArn,
      authType: 'NONE',
    });
    new lambda.CfnPermission(this, 'PermissionForMyFunctionURL', {
      functionName: lambdaFunction.ref,
      action: 'lambda:InvokeFunctionUrl',
      functionUrlAuthType: 'NONE',
      principal: '*',
    });

    // Output
    new cdk.CfnOutput(this, 'FunctionURL', {
      value: functionUrl.attrFunctionUrl,
    });
  }
}

const app = new cdk.App();

new MyStack(app, 'MyStack', {
  env: { account: '999999999999', region: 'ap-northeast-1' },
});

このコードを普通に cdk synth した場合…

コマンド
npx cdk synth --app "npx ts-node path/to/cdk.ts"

標準出力されるyaml形式のテンプレートは以下の通り。

出力されるテンプレート
Parameters:
  LambdaTimeout:
    Type: Number
    Default: 3
  BootstrapVersion:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /cdk-bootstrap/hnb659fds/version
    Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]
Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource:
                  Fn::Join:
                    - ""
                    - - "arn:aws:logs:"
                      - Ref: AWS::Region
                      - ":"
                      - Ref: AWS::AccountId
                      - :*
          PolicyName: LoggingPolicy
    Metadata:
      aws:cdk:path: MyStack/LambdaExecutionRole
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: "exports.handler = async (event) => ({ statusCode: 200, body: JSON.stringify(event) });"
      Role:
        Fn::GetAtt:
          - LambdaExecutionRole
          - Arn
      Handler: index.handler
      Runtime: nodejs16.x
      Timeout:
        Ref: LambdaTimeout
    Metadata:
      aws:cdk:path: MyStack/MyFunction
  MyFunctionURL:
    Type: AWS::Lambda::Url
    Properties:
      AuthType: NONE
      TargetFunctionArn:
        Fn::GetAtt:
          - MyFunction
          - Arn
    Metadata:
      aws:cdk:path: MyStack/MyFunctionURL
  PermissionForMyFunctionURL:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunctionUrl
      FunctionName:
        Ref: MyFunction
      Principal: "*"
      FunctionUrlAuthType: NONE
    Metadata:
      aws:cdk:path: MyStack/PermissionForMyFunctionURL
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:H4sIAAAAAAAA/yWOSwoCMRBEz+I+aWdEvYDgVom4lp6khZ7JR/LRRcjdzeiqql5RUDs4DDBs8JOkNou0PEG9ZdSLOD39FSM6yhRF7x+V0UGnKlgSFt1kEGrP5+J15uDXxT3a35Ci45Q6bG3Nl5JfJa9OUQolamrCB0Mwp+17PMK47x/mxCxj8ZkdgfrrF4feEAifAAAA
    Metadata:
      aws:cdk:path: MyStack/CDKMetadata/Default
Outputs:
  FunctionURL:
    Value:
      Fn::GetAtt:
        - MyFunctionURL
        - FunctionUrl
Rules:
  CheckBootstrapVersion:
    Assertions:
      - Assert:
          Fn::Not:
            - Fn::Contains:
                - - "1"
                  - "2"
                  - "3"
                  - "4"
                  - "5"
                - Ref: BootstrapVersion
        AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.

これでもいいんだけど、これだとCloudFormationテンプレートを普通に手書きした場合とはちょっと雰囲気が違う。

  • Parameters セクションに BootstrapVersion が追加されている。また、その入力値をバリデーションするための CheckBootstrapVersion ルールが Rules セクションに追加されている。
  • Resources セクション以下の各リソースに Metadata (aws:cdk:path) が追加されている。
  • Resources セクション以下に CDKMetadata リソースが追加されている。
  • AWSTemplateFormatVersion セクションが無い。(なくてもいいけど)
  • Description が無い。

なので、以下の通りにCDKのコードを変更する。コメントに★がついてるところ付近が変更点。

cdk.ts
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';

class MyStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // ★AWSTemplateFormatVersionをセット
    this.templateOptions.templateFormatVersion = '2010-09-09';

    // ★Descriptionをセット
    this.templateOptions.description = 'my happy stack';

    // パラメータ
    const paramLambdaTimeout = new cdk.CfnParameter(this, 'LambdaTimeout', {
      type: 'Number',
      default: 3,
    });

    // IAM: Lambda実行ロール
    const executionRole = new iam.CfnRole(this, 'LambdaExecutionRole', {
      assumeRolePolicyDocument: {
        Version: '2012-10-17',
        Statement: [
          {
            Effect: 'Allow',
            Principal: { Service: ['lambda.amazonaws.com'] },
            Action: ['sts:AssumeRole'],
          },
        ],
      },
      policies: [
        {
          policyName: 'LoggingPolicy',
          policyDocument: {
            Version: '2012-10-17',
            Statement: [
              {
                Effect: 'Allow',
                Action: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
                Resource: `arn:aws:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`,
              },
            ],
          },
        },
      ],
    });

    // Lambda: 適当な関数
    const lambdaFunction = new lambda.CfnFunction(this, 'MyFunction', {
      runtime: 'nodejs16.x',
      handler: 'index.handler',
      timeout: paramLambdaTimeout.valueAsNumber,
      role: executionRole.attrArn,
      code: {
        zipFile: `exports.handler = async (event) => ({ statusCode: 200, body: JSON.stringify(event) });`,
      },
    });

    // 関数URL
    const functionUrl = new lambda.CfnUrl(this, 'MyFunctionURL', {
      targetFunctionArn: lambdaFunction.attrArn,
      authType: 'NONE',
    });
    new lambda.CfnPermission(this, 'PermissionForMyFunctionURL', {
      functionName: lambdaFunction.ref,
      action: 'lambda:InvokeFunctionUrl',
      functionUrlAuthType: 'NONE',
      principal: '*',
    });

    // Output
    new cdk.CfnOutput(this, 'FunctionURL', {
      value: functionUrl.attrFunctionUrl,
    });
  }
}

const app = new cdk.App();

new MyStack(app, 'MyStack', {
  env: { account: '999999999999', region: 'ap-northeast-1' },
  synthesizer: new cdk.DefaultStackSynthesizer({
    generateBootstrapVersionRule: false, // ★BootstrapVersionおよびそのバリデーションルールを出力しない
  }),
});

スタック内で AWSTemplateFormatVersionDescription の値をセットしている。また、スタックを new するときにオプションで synthesizer を指定し、BootstrapVersion の出力をOFFにしている。

さらに、このコードを以下のコマンドで cdk synth する。

コマンド
npx cdk synth --app "npx ts-node path/to/cdk.ts" --no-version-reporting --path-metadata false

オプション --no-version-reporting により CDKMetadata リソースの、オプション --path-metadata false により Metadata (aws:cdk:path) の出力をOFFにしている。これで出力されるテンプレートは以下の通り。

出力されるテンプレート
Description: my happy stack
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  LambdaTimeout:
    Type: Number
    Default: 3
Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource:
                  Fn::Join:
                    - ""
                    - - "arn:aws:logs:"
                      - Ref: AWS::Region
                      - ":"
                      - Ref: AWS::AccountId
                      - :*
          PolicyName: LoggingPolicy
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: "exports.handler = async (event) => ({ statusCode: 200, body: JSON.stringify(event) });"
      Role:
        Fn::GetAtt:
          - LambdaExecutionRole
          - Arn
      Handler: index.handler
      Runtime: nodejs16.x
      Timeout:
        Ref: LambdaTimeout
  MyFunctionURL:
    Type: AWS::Lambda::Url
    Properties:
      AuthType: NONE
      TargetFunctionArn:
        Fn::GetAtt:
          - MyFunction
          - Arn
  PermissionForMyFunctionURL:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunctionUrl
      FunctionName:
        Ref: MyFunction
      Principal: "*"
      FunctionUrlAuthType: NONE
Outputs:
  FunctionURL:
    Value:
      Fn::GetAtt:
        - MyFunctionURL
        - FunctionUrl

これでCDK特有の属性が除去されて、手書きした場合と同じようなテンプレートになる。

なおCLIで指定するオプション --no-version-reporting は、 cdk.json に以下のように記載しても可。

cdk.json
{
  "versionReporting": false
}

以上。

この記事に何かあればこちらまで (非公開)