はじめに

2023年10月にAWS Cloud Development Kit (以後CDK)の 移行コマンドが利用可能となりました。

CDK移行コマンドとは、ユーザーは以前にデプロイされたAWS CloudFormation (以後CloudFormation)テンプレートや スタックで作成されたリソースを CDK アプリケーションに移行することができる機能となります。

CloudFormationからCDKに移行を考えている方も多いのではないでしょうか。

今回は既存のCloudFormationスタックをCDKへ移行する際に、私がハマったエラーや注意点を共有していきたいと思います。

本記事の想定読者としてはCloudFormationのコード開発をした経験がある方向けとなります。

 

概要

本記事では既存のCloudFormationスタックをCDKに置き換えてみます。

流れは以下の通りです。最後にcdk-nagの取り込みや、GitHub Actionsとの連携をおまけとして記載しています。

①既存CloudFormationスタックをcdk migrateコマンドでCDKに変換

②出力したCDKコードのエラーを修正

③既存スタックへcdk deployコマンドでスタックを上書き

 

前提事項

◆バージョン

root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# cdk --version
2.126.0 (build fb74c41)
root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# 
root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# node -v
v20.12.0
root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# 

 

◆CDK変換を行う既存のスタック

今回変換を行うスタックは以下を作成しているスタックとなります。

・ログ用バケット
・コード配置用バケット(Lambdaのzipファイル等の配置先)
・Amazon kinesis Data FireforceでCloudWatchLogからAmazon S3にログを送付する事前の定義
・他スタックへバケット名などを連携するためのAWS Systems Manager Parameter Store
CloudFormationのOutPutsは利用していません。

 

 

◆既存スタックのCloudFormationコード
折り畳んでいます。

CloudFormationコード
CloudFormationコード
AWSTemplateFormatVersion: 2010-09-09

## ------------------------------------------------------------
## Description Section
## ------------------------------------------------------------
Description: FINTANTEST
## ------------------------------------------------------------
## Parameters Section
## ------------------------------------------------------------
Parameters:
  SYSTEMNAME:
    Description: |
      (Require) Enter System Name (lowercase). *** Only lowercase English ***
    Type: String
    AllowedPattern: "[a-z,-,_]+"
    Default: systemname
  env:
    Description: |
      (Require) Select Environment.
    Type: String
    AllowedValues:
      - dev
      - stg
      - prd
    Default: dev
  MakeVgaScript1Bucket:
    Description: |
      (Require) Select "true" to create VgaScript1 Bucket.
    Type: String
    AllowedValues:
      - true
      - false
  ExpirationDaysApplog:
    Description: |
      (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***
    Type: String
    Default: 3650
  ExpirationDaysPdlog:
    Description: |
      (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***
    Type: String
    Default: 90
  ExpirationDaysPerflog:
    Description: |
      (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***
    Type: String
    Default: 450
  LogExpirationDaysVersioning:
    Description: |
      (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***
    Type: String
    Default: 3650
  LogRetentionPeriod:
    Description: |
      Put the number of log retention period
    Type: String
    AllowedValues:
      - 1
      - 3
      - 5
      - 7
      - 14
      - 30
      - 60
      - 90
      - 120
      - 150
      - 180
      - 365
      - 400
      - 545
      - 731
      - 1827
      - 3653
    Default: 90
  ScriptExpirationDaysVersioning:
    Description: |
      (Require) Enter Expiration In Days for versioning. If not Deleted, Leave blank. *** Only Number ***
    Type: String
    Default: 90
## ------------------------------------------------------------
## Conditions Section
## ------------------------------------------------------------
Conditions:
  MakeVgaScript1Bucket: !Equals [!Ref MakeVgaScript1Bucket, "true"]
  SelectApplog: !Equals [!Ref ExpirationDaysApplog, ""]
  SelectPdlog: !Equals [!Ref ExpirationDaysPdlog, ""]
  SelectPerflog: !Equals [!Ref ExpirationDaysPerflog, ""]
  SelectLogExpirationDaysVersioning:
    !Equals [!Ref LogExpirationDaysVersioning, ""]
  SelectScriptExpirationDaysVersioning:
    !Equals [!Ref ScriptExpirationDaysVersioning, ""]

## ------------------------------------------------------------
## Resources Section
## ------------------------------------------------------------
Resources:
  # ------------------------------------------------------------#
  #  IAM Role for Service
  # ------------------------------------------------------------#
  KinesisCloudwatchlogsToS3Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: kinesis-cloudwatchlogstos3-role
      Path: "/"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - firehose.amazonaws.com
                - logs.us-east-1.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: kinesis-cloudwatchlogstos3-policy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - s3:AbortMultipartUpload
                  - s3:GetBucketLocation
                  - s3:GetObject
                  - s3:ListBucket
                  - s3:ListBucketMultipartUploads
                  - s3:PutObject
                  - s3:PutObjectAcl
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - kinesis:DescribeStream
                  - kinesis:GetShardIterator
                  - kinesis:GetRecords
                  - kinesis:ListShards
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - firehose:*
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - iam:PassRole
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - logs:PutLogEvents
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - lambda:InvokeFunction
                  - lambda:GetFunctionConfiguration
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - kms:Decrypt
                Resource: !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLDER%
                Condition:
                  StringEquals:
                    "kms:ViaService": !Sub kinesis.${AWS::Region}.amazonaws.com
                  StringLike:
                    "kms:EncryptionContext:aws:kinesis:arn": !Sub arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLDER%
  # ------------------------------------------------------------#
  #  S3 Bucket   *** ResourceName + Bucket ***
  # ------------------------------------------------------------#
  VgaLogBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      BucketName: !Sub ${env}-${AWS::AccountId}-vga-log-1
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      OwnershipControls:
        Rules:
          - ObjectOwnership: ObjectWriter
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LifecycleConfiguration:
        Rules:
          - !If
            - SelectApplog
            - !Ref AWS::NoValue
            - Id: AWSLogs_delete_applog
              Status: Enabled
              Prefix: applog/
              ExpirationInDays: !Sub ${ExpirationDaysApplog}
          - !If
            - SelectPdlog
            - !Ref AWS::NoValue
            - Id: AWSLogs_delete_pdlog
              Status: Enabled
              Prefix: pdlog/
              ExpirationInDays: !Sub ${ExpirationDaysPdlog}
          - !If
            - SelectPerflog
            - !Ref AWS::NoValue
            - Id: AWSLogs_delete_perflog
              Status: Enabled
              Prefix: perflog/
              ExpirationInDays: !Sub ${ExpirationDaysPerflog}
          - !If
            - SelectLogExpirationDaysVersioning
            - !Ref AWS::NoValue
            - Id: NoncurrentVersionExpiration
              Status: Enabled
              NoncurrentVersionExpiration:
                NoncurrentDays: !Sub ${LogExpirationDaysVersioning}
      MetricsConfigurations:
        - Id: EntireBucket
      VersioningConfiguration:
        Status: Enabled
      Tags:
        - Key: "Name"
          Value: !Sub "${env}-${AWS::AccountId}-vga-log-1"
        - Key: "System"
          Value: !Sub "${SYSTEMNAME}"

  VgaScriptBucket:
    Type: AWS::S3::Bucket
    Condition: MakeVgaScript1Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Properties:
      BucketName: !Sub ${env}-${AWS::AccountId}-vga-script-1
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      OwnershipControls:
        Rules:
          - ObjectOwnership: ObjectWriter
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LifecycleConfiguration: !If
        - SelectScriptExpirationDaysVersioning
        - !Ref AWS::NoValue
        - Rules:
            - Id: NoncurrentVersionExpiration
              Status: Enabled
              NoncurrentVersionExpiration:
                NoncurrentDays: !Sub ${ScriptExpirationDaysVersioning}
      Tags:
        - Key: "Name"
          Value: !Sub "${env}-${AWS::AccountId}-vga-script-1"
        - Key: "System"
          Value: !Sub "${SYSTEMNAME}"
  # ------------------------------------------------------------#
  #  S3 Bucket Policy   *** ResourceName + BucketPolicy ***
  # ------------------------------------------------------------#
  VgaLogBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref VgaLogBucket
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: "AllowSSLRequestsOnly"
            Effect: Deny
            Principal: "*"
            Action: "s3:*"
            Resource: 
              - !Sub "arn:aws:s3:::${env}-${AWS::AccountId}-vga-log-1"
              - !Sub "arn:aws:s3:::${env}-${AWS::AccountId}-vga-log-1/*"
            Condition:
              Bool:
                aws:SecureTransport: false
          - Sid: "AclCheck"
            Effect: Allow
            Principal:
              Service:
                - "config.amazonaws.com"
                - "cloudtrail.amazonaws.com"
                - !Sub "logs.${AWS::Region}.amazonaws.com"
                - "delivery.logs.amazonaws.com"
            Action: s3:GetBucketAcl
            Resource: !Sub "arn:aws:s3:::${env}-${AWS::AccountId}-vga-log-1"
          - Sid: Write
            Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
                - cloudtrail.amazonaws.com
                - !Sub logs.${AWS::Region}.amazonaws.com
                - guardduty.amazonaws.com
                - delivery.logs.amazonaws.com
            Action: s3:PutObject
            Resource: !Sub "arn:aws:s3:::${env}-${AWS::AccountId}-vga-log-1/*"
            Condition:
              StringEquals:
                s3:x-amz-acl: "bucket-owner-full-control"
          - Sid: "LocationCheck & List"
            Effect: Allow
            Principal: 
              Service: 
                - "config.amazonaws.com"
                - "guardduty.amazonaws.com"
            Action:
              - "s3:GetBucketLocation"
              - "s3:ListBucket"
            Resource: !Sub "arn:aws:s3:::${env}-${AWS::AccountId}-vga-log-1"
          - Sid: ELBLogWrite
            Effect: Allow
            Principal:
              AWS: "arn:aws:iam::127311923021:root"
            Action: "s3:PutObject"
            Resource: !Sub "arn:aws:s3:::${env}-${AWS::AccountId}-vga-log-1/*"

  VgaScriptBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Condition: MakeVgaScript1Bucket
    Properties:
      Bucket: !Ref VgaScriptBucket
      PolicyDocument: !Sub |
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "AllowSSLRequestsOnly",
                    "Effect": "Deny",
                    "Principal": "*",
                    "Action": "s3:*",
                    "Resource": [
                        "arn:aws:s3:::${env}-${AWS::AccountId}-vga-script-1",
                        "arn:aws:s3:::${env}-${AWS::AccountId}-vga-script-1/*"
                    ],
                    "Condition": {
                        "Bool": {
                            "aws:SecureTransport": false
                        }
                    }
                }
            ]
        }

  # ------------------------------------------------------------#
  #  CloudWatch Logs
  # ------------------------------------------------------------#
  KinesisLog:
    Type: AWS::Logs::LogGroup
    DeletionPolicy: Retain
    UpdateReplacePolicy: Delete
    Properties:
      LogGroupName: !Sub ${env}-kinesis-logs
      RetentionInDays: !Ref LogRetentionPeriod

  KinesisErrorDeliveryS3DeliveryStreamError:
    Type: AWS::Logs::LogStream
    Properties:
      LogGroupName: !Ref KinesisLog
      LogStreamName: !Sub ${env}-${SYSTEMNAME}-kinesis-error-logs-s3-deliverystream-error

  # ------------------------------------------------------------#
  # CloudWatch Logs SubscriptionFilter
  # ------------------------------------------------------------#
  KinesisErrorSubscriptionFilter:
    Type: AWS::Logs::SubscriptionFilter
    Properties:
      RoleArn:
        Fn::GetAtt:
          - "KinesisCloudwatchlogsToS3Role"
          - "Arn"
      LogGroupName: !Ref KinesisLog
      FilterPattern: ""
      DestinationArn:
        Fn::GetAtt:
          - "KinesisErrorS3DeliveryStream"
          - "Arn"

  # ------------------------------------------------------------#
  # Kinesis Data Firehose
  # ------------------------------------------------------------#
  KinesisErrorS3DeliveryStream:
    Type: AWS::KinesisFirehose::DeliveryStream
    Properties:
      DeliveryStreamName: !Sub ${env}-${SYSTEMNAME}-kinesis-error-logs-s3-deliverystream
      DeliveryStreamType: DirectPut
      DeliveryStreamEncryptionConfigurationInput:
        KeyType: AWS_OWNED_CMK
      ExtendedS3DestinationConfiguration:
        BucketARN: !Sub arn:aws:s3:::${env}-${AWS::AccountId}-vga-log-1
        BufferingHints:
          IntervalInSeconds: 60
          SizeInMBs: 5
        CompressionFormat: UNCOMPRESSED
        Prefix: !Sub pdlog/AWSLogs/${AWS::AccountId}/KinesisDataFirehose/
        RoleARN: !GetAtt KinesisCloudwatchlogsToS3Role.Arn
        ErrorOutputPrefix: !Sub pdlog/AWSLogs/${AWS::AccountId}/KinesisDataFirehose/error/
        CloudWatchLoggingOptions:
          Enabled: true
          LogGroupName: !Ref KinesisLog
          LogStreamName: !Ref KinesisErrorDeliveryS3DeliveryStreamError

  ## ------------------------------------------------------------
  ## SSMParameter
  ## ------------------------------------------------------------
  SSMParameterKinesisLog:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Sub /${env}/infra/${SYSTEMNAME}/CloudWatch/kinesis-logs/name
      Type: String
      Value: !Ref KinesisLog

  SSMParameterKinesisCloudwatchlogsToS3Role:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Sub /${env}/infra/${SYSTEMNAME}/IAMRole/KinesisTransferCloudwatchlogstos3Role/arn
      Type: String
      Value: !GetAtt KinesisCloudwatchlogsToS3Role.Arn

  SSMParameterVgaLogBucketName:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Sub /${env}/infra/${SYSTEMNAME}/S3/VgaLog1Bucket/name
      Type: String
      Value: !Ref VgaLogBucket

  SSMParameterVgaScriptBucketName:
    Type: AWS::SSM::Parameter
    Condition: MakeVgaScript1Bucket
    Properties:
      Name: !Sub /${env}/infra/${SYSTEMNAME}/S3/VgaScript1Bucket/name
      Type: String
      Value: !Ref VgaScriptBucket

 

CloudFormationパラメータ

様々なAWSアカウントに流用できるようにパラメータもいくつか作りこんでいます。

パラメータキー
説明
env AWSリソースに付与する環境識別子(dev/stg/prd)
ExpirationDaysXXXX 各種ログのファイル保管期限を行うS3ライフサイクルルール
LogExpirationDaysVersioning ログバケットバージョニングの期限切れ期間のS3ライフサイクルルール
LogRetentionPeriod CloudWatchLogのログ保管期限
MakeVgaScript1Bucket コード配置先バケットの作成有無。(true or false)
ScriptExpirationDaysVersioning コード配置先バケットバージョニングの期限切れ期間のS3ライフサイクルルール
SYSTEMNAME AWSリソースに付与するシステム識別子(システム名称)

作業の流れ

既存CloudFormationスタックのYAMLコードは様々なAWSアカウントに転用することを意識して作成されているので、

そのコンセプトを守りつつ、概要でも記載した順序で対応していきます。

①cdk migrateコマンドによる既存スタックのCDKコード化

②コード化したCDKのエラー解析とコード修正

③cdk deployで既存スタックの上書き

 

①cdk migrateコマンドによる既存スタックのCDKコード化

cdk migrateコマンドを使い、CloudFormationをCDKに変換して、ローカルにコードを生成します。

今回は同じプロジェクトメンバーが使っている言語と合わせるためTypeScriptで変換してみます。

All done!が出力すれば変換完了です。

◆実行結果

root@f5bd1aaccbee:/infra/fintan# mkdir cdk && cd cdk
root@f5bd1aaccbee:/infra/fintan/cdk# cdk migrate --from-stack --stack-name fintan-cdk --account <アカウントID> --region us-east-1 --language typescript
This is an experimental feature and development on it is still in progress. We make no guarantees about the outcome or stability of the functionality.
 ⏳  Generating CDK app for fintan-cdk...
Applying project template app for typescript
Initializing a new git repository...
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint:   git config --global init.defaultBranch 
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint:   git branch -m 
Executing npm install...
✅ All done!

root@f5bd1aaccbee:/infra/fintan/cdk# 

◆作成ファイル

root@f5bd1aaccbee:/infra/fintan/cdk# tree ./fintan-cdk -L 2 -I node_modules
./fintan-cdk
|-- README.md
|-- bin
| `-- fintan-cdk.ts
|-- cdk.json
|-- jest.config.js
|-- lib
| `-- fintan-cdk-stack.ts
|-- migrate.json
|-- package-lock.json
|-- package.json
|-- test 
| `-- fintan-cdk.test.ts
`-- tsconfig.json

3 directories, 10 files
root@f5bd1aaccbee:/infra/fintan/cdk#

②コード化したCDKのエラー解析とコード修正

この状態で使えるのか、cdk synthコマンドを使いCloudFormationテンプレートを出力してみましょう。

勿論このままでは現状使えないことがわかります。いくつかエラーが出ているので解消していきます。

root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# cdk synth
/infra/fintan/cdk/fintan-cdk/node_modules/ts-node/src/index.ts:859
    return new TSError(diagnosticText, diagnosticCodes, diagnostics);
           ^
TSError: ⨯ Unable to compile TypeScript:
bin/fintan-cdk.ts:7:39 - error TS2345: Argument of type '{}' is not assignable to parameter of type 'FintanCdkStackProps'.
  Property 'makeVgaScript1Bucket' is missing in type '{}' but required in type 'FintanCdkStackProps'.

  7 new FintanCdkStack(app, 'fintan-cdk', {
                                          ~
  8   /* If you don't specify 'env', this stack will be environment-agnostic.
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
... 
 20   /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 21 });
    ~

  lib/fintan-cdk-stack.ts:25:12
    25   readonly makeVgaScript1Bucket: boolean;
                  ~~~~~~~~~~~~~~~~~~~~
    'makeVgaScript1Bucket' is declared here.

    at createTSError (/infra/fintan/cdk/fintan-cdk/node_modules/ts-node/src/index.ts:859:12)
    at reportTSError (/infra/fintan/cdk/fintan-cdk/node_modules/ts-node/src/index.ts:863:19)
    at getOutput (/infra/fintan/cdk/fintan-cdk/node_modules/ts-node/src/index.ts:1077:36)
    at Object.compile (/infra/fintan/cdk/fintan-cdk/node_modules/ts-node/src/index.ts:1433:41)
    at Module.m._compile (/infra/fintan/cdk/fintan-cdk/node_modules/ts-node/src/index.ts:1617:30)
    at Module._extensions..js (node:internal/modules/cjs/loader:1414:10)
    at Object.require.extensions. [as .ts] (/infra/fintan/cdk/fintan-cdk/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1197:32)
    at Function.Module._load (node:internal/modules/cjs/loader:1013:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:128:12) {
  diagnosticCodes: [ 2345 ]
}

Subprocess exited with error 1
root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# 

②-1.CDK環境の設定

./bin/fintan-cdk.tsファイルを修正していきます。

コメントで丁寧にアンコメントすべき箇所を定義してくれています。

今回は AWS CLI プロファイルを使用するため、[env: { account:prosess.env~]の部分を解除します。

./bin/fintan-cdk.tsファイル
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { FintanCdkStack } from '../lib/fintan-cdk-stack';

const app = new cdk.App();
new FintanCdkStack(app, 'fintan-cdk', {
  /* If you don't specify 'env', this stack will be environment-agnostic.
   * Account/Region-dependent features and context lookups will not work,
   * but a single synthesized template can be deployed anywhere. */

  /* Uncomment the next line to specialize this stack for the AWS Account
   * and Region that are implied by the current CLI configuration. */
  env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },  // アンコメント

  /* Uncomment the next line if you know exactly what Account and Region you
   * want to deploy the stack to. */
  // env: { account: '123456789012', region: 'us-east-1' },

  /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
});

②-2.CloudFormationのパラメータ設定

CloudFormationのパラメータを定義している場合、同じように環境変数を外出ししてあげる必要があります。

各環境毎にパラメータをファイルで管理して、差分や履歴を追えるようにしたいため、環境毎のファイルを作りそこへパラメータを外出しします。

配置先用にconfigディレクトリを新たに作り、新たなパラメータ用のファイルを作成します。

開発環境用:./config/dev.ts

本番環境用:./config/prd.ts

STG環境用:./config/stg.ts

root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# mkdir config && touch ./config/dev.ts && touch ./config/stg.ts && touch ./config/prd.ts

./config/dev.tsファイルに元々あったパラメータを転記していきます。

文字タイプが重要なため、CloudFormationパラメータで指定していたbooleanやnumberの場合はダブルクォートで括らないように注意しましょう。

また、env変数は既にCDKの環境設定で使われているため、元々CloudFormationで利用していた「env」は「environment」に変更します。

./config/dev.tsファイル
export const config = {
    "makeVgaScript1Bucket": true,
    "systemname": "fintan",
    // "env": "dev"
    "environment": "dev",
    "expirationDaysApplog": 3650,
    "expirationDaysPdlog": 90,
    "expirationDaysPerflog": 450,
    "logExpirationDaysVersioning": 3650,
    "logRetentionPeriod": 90,
    "scriptExpirationDaysVersioning": 90
  };

ファイルを各環境毎にパラメータを分けるため以下のように本番環境用、STG環境用に用意します。

今回はAWSリソース名に付与する環境識別子[environment]を各環境毎に変更していきます。

./config/prd.ts

    “environment”: “prd”,

./config/stg.ts

 ”environment”:“stg”,

②-3.パラメータをCDKに読み込ませる

./bin/fintan-cdk.tsに②で記載した各種ファイルを読み込ませる定義を入れます。

今回はコンテキストを利用して、cdk コマンドの引数で環境を変更できるように改良してみます。

追加した箇所はコメント分を確認ください。

./bin/fintan-cdk.tsファイル
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import 'source-map-support/register';
import { FintanCdkStack } from '../lib/fintan-cdk-stack';
// CDKにconfig以下のファイルを読み込みさせるため、importで作成したconfigを指定。
import { config as devProperties } from '../config/dev';
import { config as prdProperties } from '../config/prd';
import { config as stgProperties } from '../config/stg';


const app = new cdk.App();

// envPrefixというコンテキストを作りdev,stg,prdの引数を受け付ける。
const argContext = 'envPrefix';
const envPrefix= app.node.tryGetContext(argContext);
if (envPrefix== undefined)
throw new Error(`Please specify environment with context option. ex) cdk deploy -c envPrefix=dev [-c awsAccount=XXXXXXXX ] [-c awsRegion=us-east-1 ]`);

//AWSアカウント設定
const awsAccount =
  app.node.tryGetContext('awsAccount') || process.env.CDK_DEFAULT_ACCOUNT
console.log(`awsAccount is "${awsAccount}".`)

//REGION設定
const awsRegion =
  app.node.tryGetContext('awsRegion') || process.env.CDK_DEFAULT_REGION
console.log(`awsRegion is "${awsRegion}".`)

// 読み込むconfigファイルを指定
const properties = getProperties(envPrefix)

new FintanCdkStack(app, 'fintan-cdk', {
  /* If you don't specify 'env', this stack will be environment-agnostic.
   * Account/Region-dependent features and context lookups will not work,
   * but a single synthesized template can be deployed anywhere. */

  /* Uncomment the next line to specialize this stack for the AWS Account
   * and Region that are implied by the current CLI configuration. */
  env: { account: awsAccount, region: awsRegion },

  // configファイル読込み
  ...properties
  
  /* Uncomment the next line if you know exactly what Account and Region you
   * want to deploy the stack to. */
  // env: { account: '123456789012', region: 'us-east-1' },

  /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
});

//コンテキストの設定値で読み込むconfig以下のファイルを切り替える
function getProperties(envKey: String) {
  if (envKey === "dev") {
    return devProperties
  } else if (envKey === "prd") {
    return prdProperties
  } else if (envKey === "stg") {
    return stgProperties
  }  else {
    throw new Error("No Support environment")
  }
}

②-4.CDKのエラーを解消

./lib/fintan-cdk-stack.tsを修正していきます。発生しているエラーは以下の通り。

cdk synthコマンドが成功するまで、エラーを解消していきます。

項番 エラータイプ エラー内容 原因 解決策
Problem_1 TS2322 Type ‘{ account: string | undefined; region: string | undefined; }’ is not assignable to type ‘string’. envという変数を利用しているため、CDK環境設定と変数のバッティング。 既に利用しているenv変数を利用しない
Problem_2 TS2345 Argument of type ‘{ makeVgaScript1Bucket: boolean; systemname: string; environment: string; expirationDaysApplog: number; expirationDaysPdlog: number; expirationDaysPerflog: number; logExpirationDaysVersioning: number; logRetentionPeriod: numbeor; scriptExpirationDaysVersioning: number; env: { …; }; }’ is not assignable to parameter of type ‘FintanCdkStackProps’.
Types of property ‘logExpirationDaysVersioning’ are incompatible.
Type ‘number’ is not assignable to type ‘string’.
CloudFormationパラメータを入れている変数の型があっていない。 ./lib/fintan-cdk-stack.tsの型を修正

数字をnumberに、true/falseをbooleanに

Problem_3 TS2322 Type ‘string | number’ is not assignable to type ‘number | undefined’.

Type ‘string | undefined’ is not assignable to type ‘string’.

./lib/fintan-cdk-stack.tsの変数定義で定義されていない場合(undefined)の処理がない undefinedの場合の処理を追加
Problem_4 TS2367 This comparison appears to be unintentional because the types ‘boolean’ and ‘string’ have no overlap.

This comparison appears to be unintentional because the types ‘number’ and ‘string’ have no overlap.

CloudFormationパラメータを入れている変数の型があっていない。 数字をnumberに、true/falseをbooleanに

◆変更箇所の比較
修正した場所は上表の「項番」「Problem_X」のコメントをつけています。

長いので折り畳みしておきます。

fintan-cdk-stack.tsのsdiff結果
root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# sdiff -w 200  ./lib/fintan-cdk-stack.ts ./lib/fintan-cdk-stack.ts_old
import * as cdk from 'aws-cdk-lib';                                                                     import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';                                                             import * as iam from 'aws-cdk-lib/aws-iam';
import * as kinesisfirehose from 'aws-cdk-lib/aws-kinesisfirehose';                                     import * as kinesisfirehose from 'aws-cdk-lib/aws-kinesisfirehose';
import * as logs from 'aws-cdk-lib/aws-logs';                                                           import * as logs from 'aws-cdk-lib/aws-logs';
import * as s3 from 'aws-cdk-lib/aws-s3';                                                               import * as s3 from 'aws-cdk-lib/aws-s3';
import * as ssm from 'aws-cdk-lib/aws-ssm';                                                             import * as ssm from 'aws-cdk-lib/aws-ssm';

export interface FintanCdkStackProps extends cdk.StackProps {                                           export interface FintanCdkStackProps extends cdk.StackProps {
  /**                                                                                                     /**
   * (Require) Enter System Name (lowercase). *** Only lowercase English ***                               * (Require) Enter System Name (lowercase). *** Only lowercase English ***

   * @default 'systemname'                                                                                 * @default 'systemname'
   */                                                                                                      */
  readonly systemname?: string;                                                                           readonly systemname?: string;
  /**                                                                                                     /**
   * (Require) Select Environment.                                                                         * (Require) Select Environment.

   * @default 'dev'                                                                                        * @default 'dev'
   */                                                                                                      */
  // ----- Problem_1   env To environment                                                          |
  readonly environment?: string;                                                                   |      readonly env?: string;
  /**                                                                                                     /**
   * (Require) Select "true" to create VgaScript1 Bucket.                                                  * (Require) Select "true" to create VgaScript1 Bucket.

   */                                                                                                      */
  readonly makeVgaScript1Bucket: boolean;                                                                 readonly makeVgaScript1Bucket: boolean;
  /**                                                                                                     /**
   * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***                 * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***

   * @default '3650'                                                                                       * @default '3650'
   */                                                                                                      */
  // ----- Problem_2   string To number                                                            <
  readonly expirationDaysApplog?: number;                                                          |      readonly expirationDaysApplog?: string;
  /**                                                                                                     /**
   * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***                 * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***

   * @default '90'                                                                                         * @default '90'
   */                                                                                                      */
  // ----- Problem_2   string To number                                                            <
  readonly expirationDaysPdlog?: number;                                                           |      readonly expirationDaysPdlog?: string;
  /**                                                                                                     /**
   * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***                 * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***

   * @default '450'                                                                                        * @default '450'
   */                                                                                                      */
  // ----- Problem_2   string To number                                                            <
  readonly expirationDaysPerflog?: number;                                                         |      readonly expirationDaysPerflog?: string;
  /**                                                                                                     /**
   * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***                 * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***

   * @default '3650'                                                                                       * @default '3650'
   */                                                                                                      */
  // ----- Problem_2   string To number                                                            <
  readonly logExpirationDaysVersioning?: number;                                                   |      readonly logExpirationDaysVersioning?: string;
  /**                                                                                                     /**
   * Put the number of log retention period                                                                * Put the number of log retention period

   * @default '90'                                                                                         * @default '90'
   */                                                                                                      */
  // ----- Problem_2   string To number                                                            <
  readonly logRetentionPeriod?: number;                                                            |      readonly logRetentionPeriod?: string;
  /**                                                                                                     /**
   * (Require) Enter Expiration In Days for versioning. If not Deleted, Leave blank. *** Only Nu           * (Require) Enter Expiration In Days for versioning. If not Deleted, Leave blank. *** Only Nu

   * @default '90'                                                                                         * @default '90'
   */                                                                                                      */
  // ----- Problem_2   string To number                                                            <
  readonly scriptExpirationDaysVersioning?: number;                                                |      readonly scriptExpirationDaysVersioning?: string;
}                                                                                                       }

/**                                                                                                     /**
 * FINTANTEST                                                                                            * FINTANTEST
 */                                                                                                      */
export class FintanCdkStack extends cdk.Stack {                                                         export class FintanCdkStack extends cdk.Stack {
  public constructor(scope: cdk.App, id: string, props: FintanCdkStackProps) {                            public constructor(scope: cdk.App, id: string, props: FintanCdkStackProps) {
    super(scope, id, props);                                                                                super(scope, id, props);

    // Applying default props                                                                               // Applying default props
    props = {                                                                                               props = {
      ...props,                                                                                               ...props,
      systemname: props.systemname ?? 'systemname',                                                           systemname: props.systemname ?? 'systemname',
      // ----- Problem_1   env To environment                                                      <          
      environment: props.environment ?? 'dev',                                                     |          env: props.env ?? 'dev',
      // ----- Problem_2   string To number                                                        <
      expirationDaysApplog: props.expirationDaysApplog ?? 3650,                                    |          expirationDaysApplog: props.expirationDaysApplog ?? '3650',
      expirationDaysPdlog: props.expirationDaysPdlog ?? 90,                                        |          expirationDaysPdlog: props.expirationDaysPdlog ?? '90',
      expirationDaysPerflog: props.expirationDaysPerflog ?? 450,                                   |          expirationDaysPerflog: props.expirationDaysPerflog ?? '450',
      logExpirationDaysVersioning: props.logExpirationDaysVersioning ?? 3650,                      |          logExpirationDaysVersioning: props.logExpirationDaysVersioning ?? '3650',
      logRetentionPeriod: props.logRetentionPeriod ?? 90,                                          |          logRetentionPeriod: props.logRetentionPeriod ?? '90',
      scriptExpirationDaysVersioning: props.scriptExpirationDaysVersioning ?? 90,                  |          scriptExpirationDaysVersioning: props.scriptExpirationDaysVersioning ?? '90',
    };                                                                                                      };

    // Conditions                                                                                           // Conditions
    // ----- Problem_2   string To boolean                                                        <
    const makeVgaScript1Bucket = props.makeVgaScript1Bucket! === true;                             |        const makeVgaScript1Bucket = props.makeVgaScript1Bucket! === 'true';
    // ----- Problem_3   undefined定義追加                                                        <
    const selectApplog = props.expirationDaysApplog === undefined;                                 |        const selectApplog = props.expirationDaysApplog! === '';
    const selectLogExpirationDaysVersioning = props.logExpirationDaysVersioning === undefined;     |        const selectLogExpirationDaysVersioning = props.logExpirationDaysVersioning! === '';
    const selectPdlog = props.expirationDaysPdlog === undefined;                                   |        const selectPdlog = props.expirationDaysPdlog! === '';
    const selectPerflog = props.expirationDaysPerflog === undefined;                               |        const selectPerflog = props.expirationDaysPerflog! === '';
    const selectScriptExpirationDaysVersioning = props.scriptExpirationDaysVersioning === undefi   |        const selectScriptExpirationDaysVersioning = props.scriptExpirationDaysVersioning! === '';

    // Resources                                                                                            // Resources
    const kinesisCloudwatchlogsToS3Role = new iam.CfnRole(this, 'KinesisCloudwatchlogsToS3Role',            const kinesisCloudwatchlogsToS3Role = new iam.CfnRole(this, 'KinesisCloudwatchlogsToS3Role',
      roleName: 'kinesis-cloudwatchlogstos3-role',                                                            roleName: 'kinesis-cloudwatchlogstos3-role',
      path: '/',                                                                                              path: '/',
      assumeRolePolicyDocument: {                                                                             assumeRolePolicyDocument: {
        Version: '2012-10-17',                                                                                  Version: '2012-10-17',
        Statement: [                                                                                            Statement: [
          {                                                                                                       {
            Effect: 'Allow',                                                                                        Effect: 'Allow',
            Principal: {                                                                                            Principal: {
              Service: [                                                                                              Service: [
                'firehose.amazonaws.com',                                                                               'firehose.amazonaws.com',
                'logs.us-east-1.amazonaws.com',                                                                         'logs.us-east-1.amazonaws.com',
              ],                                                                                                      ],
            },                                                                                                      },
            Action: 'sts:AssumeRole',                                                                               Action: 'sts:AssumeRole',
          },                                                                                                      },
        ],                                                                                                      ],
      },                                                                                                      },
      policies: [                                                                                             policies: [
        {                                                                                                       {
          policyName: 'kinesis-cloudwatchlogstos3-policy',                                                        policyName: 'kinesis-cloudwatchlogstos3-policy',
          policyDocument: {                                                                                       policyDocument: {
            Version: '2012-10-17',                                                                                  Version: '2012-10-17',
            Statement: [                                                                                            Statement: [
              {                                                                                                       {
                Effect: 'Allow',                                                                                        Effect: 'Allow',
                Action: [                                                                                               Action: [
                  's3:AbortMultipartUpload',                                                                              's3:AbortMultipartUpload',
                  's3:GetBucketLocation',                                                                                 's3:GetBucketLocation',
                  's3:GetObject',                                                                                         's3:GetObject',
                  's3:ListBucket',                                                                                        's3:ListBucket',
                  's3:ListBucketMultipartUploads',                                                                        's3:ListBucketMultipartUploads',
                  's3:PutObject',                                                                                         's3:PutObject',
                  's3:PutObjectAcl',                                                                                      's3:PutObjectAcl',
                ],                                                                                                      ],
                Resource: '*',                                                                                          Resource: '*',
              },                                                                                                      },
              {                                                                                                       {
                Effect: 'Allow',                                                                                        Effect: 'Allow',
                Action: [                                                                                               Action: [
                  'kinesis:DescribeStream',                                                                               'kinesis:DescribeStream',
                  'kinesis:GetShardIterator',                                                                             'kinesis:GetShardIterator',
                  'kinesis:GetRecords',                                                                                   'kinesis:GetRecords',
                  'kinesis:ListShards',                                                                                   'kinesis:ListShards',
                ],                                                                                                      ],
                Resource: '*',                                                                                          Resource: '*',
              },                                                                                                      },
              {                                                                                                       {
                Effect: 'Allow',                                                                                        Effect: 'Allow',
                Action: [                                                                                               Action: [
                  'firehose:*',                                                                                           'firehose:*',
                ],                                                                                                      ],
                Resource: '*',                                                                                          Resource: '*',
              },                                                                                                      },
              {                                                                                                       {
                Effect: 'Allow',                                                                                        Effect: 'Allow',
                Action: [                                                                                               Action: [
                  'iam:PassRole',                                                                                         'iam:PassRole',
                ],                                                                                                      ],
                Resource: '*',                                                                                          Resource: '*',
              },                                                                                                      },
              {                                                                                                       {
                Effect: 'Allow',                                                                                        Effect: 'Allow',
                Action: [                                                                                               Action: [
                  'logs:PutLogEvents',                                                                                    'logs:PutLogEvents',
                ],                                                                                                      ],
                Resource: '*',                                                                                          Resource: '*',
              },                                                                                                      },
              {                                                                                                       {
                Effect: 'Allow',                                                                                        Effect: 'Allow',
                Action: [                                                                                               Action: [
                  'lambda:InvokeFunction',                                                                                'lambda:InvokeFunction',
                  'lambda:GetFunctionConfiguration',                                                                      'lambda:GetFunctionConfiguration',
                ],                                                                                                      ],
                Resource: '*',                                                                                          Resource: '*',
              },                                                                                                      },
              {                                                                                                       {
                Effect: 'Allow',                                                                                        Effect: 'Allow',
                Action: [                                                                                               Action: [
                  'kms:Decrypt',                                                                                          'kms:Decrypt',
                ],                                                                                                      ],
                Resource: `arn:aws:kms:${this.region}:${this.account}:key/%FIREHOSE_POLICY_TEMPL                        Resource: `arn:aws:kms:${this.region}:${this.account}:key/%FIREHOSE_POLICY_TEMPL
                Condition: {                                                                                            Condition: {
                  StringEquals: {                                                                                         StringEquals: {
                    'kms:ViaService': `kinesis.${this.region}.amazonaws.com`,                                               'kms:ViaService': `kinesis.${this.region}.amazonaws.com`,
                  },                                                                                                      },
                  StringLike: {                                                                                           StringLike: {
                    'kms:EncryptionContext:aws:kinesis:arn': `arn:aws:kinesis:${this.region}:${t                            'kms:EncryptionContext:aws:kinesis:arn': `arn:aws:kinesis:${this.region}:${t
                  },                                                                                                      },
                },                                                                                                      },
              },                                                                                                      },
            ],                                                                                                      ],
          },                                                                                                      },
        },                                                                                                      },
      ],                                                                                                      ],
    });                                                                                                     });

    const kinesisLog = new logs.CfnLogGroup(this, 'KinesisLog', {                                           const kinesisLog = new logs.CfnLogGroup(this, 'KinesisLog', {
      // ----- Problem_1   env To environment                                                      <
      logGroupName: `${props.environment!}-kinesis-logs`,                                          |          logGroupName: `${props.env!}-kinesis-logs`,
      retentionInDays: props.logRetentionPeriod!,                                                             retentionInDays: props.logRetentionPeriod!,
    });                                                                                                     });
    kinesisLog.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;                                    kinesisLog.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;

    const vgaLogBucket = new s3.CfnBucket(this, 'VgaLogBucket', {                                           const vgaLogBucket = new s3.CfnBucket(this, 'VgaLogBucket', {
      // ----- Problem_1   env To environment                                                      <
      bucketName: `${props.environment!}-${this.account}-vga-log-1`,                               |          bucketName: `${props.env!}-${this.account}-vga-log-1`,
      bucketEncryption: {                                                                                     bucketEncryption: {
        serverSideEncryptionConfiguration: [                                                                    serverSideEncryptionConfiguration: [
          {                                                                                                       {
            serverSideEncryptionByDefault: {                                                                        serverSideEncryptionByDefault: {
              sseAlgorithm: 'AES256',                                                                                 sseAlgorithm: 'AES256',
            },                                                                                                      },
          },                                                                                                      },
        ],                                                                                                      ],
      },                                                                                                      },
      ownershipControls: {                                                                                    ownershipControls: {
        rules: [                                                                                                rules: [
          {                                                                                                       {
            objectOwnership: 'ObjectWriter',                                                                        objectOwnership: 'ObjectWriter',
          },                                                                                                      },
        ],                                                                                                      ],
      },                                                                                                      },
      publicAccessBlockConfiguration: {                                                                       publicAccessBlockConfiguration: {
        blockPublicAcls: true,                                                                                  blockPublicAcls: true,
        blockPublicPolicy: true,                                                                                blockPublicPolicy: true,
        ignorePublicAcls: true,                                                                                 ignorePublicAcls: true,
        restrictPublicBuckets: true,                                                                            restrictPublicBuckets: true,
      },                                                                                                      },
      // ----- Problem_3   undefined定義追加 ※配列の中にundefinedは指定できない                    <
      lifecycleConfiguration: selectApplog && selectPdlog && selectPerflog && selectLogExpiratio   |          lifecycleConfiguration: {
        rules: [                                                                                                rules: [
          {                                                                                        |              selectApplog ? undefined : {
            id: 'AWSLogs_delete_applog',                                                                            id: 'AWSLogs_delete_applog',
            status: 'Enabled',                                                                                      status: 'Enabled',
            prefix: 'applog/',                                                                                      prefix: 'applog/',
            expirationInDays: props.expirationDaysApplog!,                                         |                expirationInDays: `${props.expirationDaysApplog!}`,
          },                                                                                                      },
          {                                                                                        |              selectPdlog ? undefined : {
            id: 'AWSLogs_delete_pdlog',                                                                             id: 'AWSLogs_delete_pdlog',
            status: 'Enabled',                                                                                      status: 'Enabled',
            prefix: 'pdlog/',                                                                                       prefix: 'pdlog/',
            expirationInDays: props.expirationDaysPdlog!,                                          |                expirationInDays: `${props.expirationDaysPdlog!}`,
          },                                                                                                      },
          {                                                                                        |              selectPerflog ? undefined : {
            id: 'AWSLogs_delete_perflog',                                                                           id: 'AWSLogs_delete_perflog',
            status: 'Enabled',                                                                                      status: 'Enabled',
            prefix: 'perflog/',                                                                                     prefix: 'perflog/',
            expirationInDays: props.expirationDaysPerflog!,                                        |                expirationInDays: `${props.expirationDaysPerflog!}`,
          },                                                                                                      },
          {                                                                                        |              selectLogExpirationDaysVersioning ? undefined : {
            id: 'NoncurrentVersionExpiration',                                                                      id: 'NoncurrentVersionExpiration',
            status: 'Enabled',                                                                                      status: 'Enabled',
            noncurrentVersionExpiration: {                                                                          noncurrentVersionExpiration: {
              noncurrentDays: props.logExpirationDaysVersioning!,                                  |                  noncurrentDays: `${props.logExpirationDaysVersioning!}`,
            },                                                                                                      },
          },                                                                                                      },
        ],                                                                                                      ],
      },                                                                                                      },
      metricsConfigurations: [                                                                                metricsConfigurations: [
        {                                                                                                       {
          id: 'EntireBucket',                                                                                     id: 'EntireBucket',
        },                                                                                                      },
      ],                                                                                                      ],
      versioningConfiguration: {                                                                              versioningConfiguration: {
        status: 'Enabled',                                                                                      status: 'Enabled',
      },                                                                                                      },
      tags: [                                                                                                 tags: [
        {                                                                                                       {
          key: 'Name',                                                                                            key: 'Name',
          // ----- Problem_1   env To environment                                                  <
          value: `${props.environment!}-${this.account}-vga-log-1`,                                |              value: `${props.env!}-${this.account}-vga-log-1`,
        },                                                                                                      },
        {                                                                                                       {
          key: 'System',                                                                                          key: 'System',
          value: `${props.systemname!}`,                                                                          value: `${props.systemname!}`,
        },                                                                                                      },
      ],                                                                                                      ],
    });                                                                                                     });
    vgaLogBucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;                                  vgaLogBucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;

    const vgaScriptBucket = makeVgaScript1Bucket                                                            const vgaScriptBucket = makeVgaScript1Bucket
      ? new s3.CfnBucket(this, 'VgaScriptBucket', {                                                           ? new s3.CfnBucket(this, 'VgaScriptBucket', {
          // ----- Problem_1   env To environment                                                  <
          bucketName: `${props.environment!}-${this.account}-vga-script-1`,                        |              bucketName: `${props.env!}-${this.account}-vga-script-1`,
          bucketEncryption: {                                                                                     bucketEncryption: {
            serverSideEncryptionConfiguration: [                                                                    serverSideEncryptionConfiguration: [
              {                                                                                                       {
                serverSideEncryptionByDefault: {                                                                        serverSideEncryptionByDefault: {
                  sseAlgorithm: 'AES256',                                                                                 sseAlgorithm: 'AES256',
                },                                                                                                      },
              },                                                                                                      },
            ],                                                                                                      ],
          },                                                                                                      },
          ownershipControls: {                                                                                    ownershipControls: {
            rules: [                                                                                                rules: [
              {                                                                                                       {
                objectOwnership: 'ObjectWriter',                                                                        objectOwnership: 'ObjectWriter',
              },                                                                                                      },
            ],                                                                                                      ],
          },                                                                                                      },
          publicAccessBlockConfiguration: {                                                                       publicAccessBlockConfiguration: {
            blockPublicAcls: true,                                                                                  blockPublicAcls: true,
            blockPublicPolicy: true,                                                                                blockPublicPolicy: true,
            ignorePublicAcls: true,                                                                                 ignorePublicAcls: true,
            restrictPublicBuckets: true,                                                                            restrictPublicBuckets: true,
          },                                                                                                      },
          versioningConfiguration: {                                                                              versioningConfiguration: {
            status: 'Enabled',                                                                                      status: 'Enabled',
          },                                                                                                      },
          // ----- Problem_3   undefined定義追加 ※配列の中にundefinedは指定できない                <
          lifecycleConfiguration: selectScriptExpirationDaysVersioning ? undefined : {                            lifecycleConfiguration: selectScriptExpirationDaysVersioning ? undefined : {
            rules: [                                                                                                rules: [
              {                                                                                                       {
                id: 'NoncurrentVersionExpiration',                                                                      id: 'NoncurrentVersionExpiration',
                status: 'Enabled',                                                                                      status: 'Enabled',
                noncurrentVersionExpiration: {                                                                          noncurrentVersionExpiration: {
                  noncurrentDays: props.scriptExpirationDaysVersioning!,                           |                      noncurrentDays: `${props.scriptExpirationDaysVersioning!}`,
                },                                                                                                      },
              },                                                                                                      },
            ],                                                                                                      ],
          },                                                                                                      },
          tags: [                                                                                                 tags: [
            {                                                                                                       {
              key: 'Name',                                                                                            key: 'Name',
              // ----- Problem_1   env To environment                                              <
              value: `${props.environment!}-${this.account}-vga-script-1`,                         |                  value: `${props.env!}-${this.account}-vga-script-1`,
            },                                                                                                      },
            {                                                                                                       {
              key: 'System',                                                                                          key: 'System',
              // ----- Problem_1   env environment                                                 <
              value: `${props.systemname!}`,                                                                          value: `${props.systemname!}`,
            },                                                                                                      },
          ],                                                                                                      ],
        })                                                                                                      })
      : undefined;                                                                                            : undefined;
    if (vgaScriptBucket != null) {                                                                          if (vgaScriptBucket != null) {
      vgaScriptBucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;                               vgaScriptBucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
    }                                                                                                       }

    const kinesisErrorDeliveryS3DeliveryStreamError = new logs.CfnLogStream(this, 'KinesisErrorD            const kinesisErrorDeliveryS3DeliveryStreamError = new logs.CfnLogStream(this, 'KinesisErrorD
      logGroupName: kinesisLog.ref,                                                                           logGroupName: kinesisLog.ref,
      // ----- Problem_1   env To environment                                                      <
      logStreamName: `${props.environment!}-${props.systemname!}-kinesis-error-logs-s3-deliverys   |          logStreamName: `${props.env!}-${props.systemname!}-kinesis-error-logs-s3-deliverystream-er
    });                                                                                                     });

    const ssmParameterKinesisCloudwatchlogsToS3Role = new ssm.CfnParameter(this, 'SSMParameterKi            const ssmParameterKinesisCloudwatchlogsToS3Role = new ssm.CfnParameter(this, 'SSMParameterKi
      // ----- Problem_1   env To environment                                                      <
      name: `/${props.environment!}/infra/${props.systemname!}/IAMRole/KinesisTransferCloudwatch   |          name: `/${props.env!}/infra/${props.systemname!}/IAMRole/KinesisTransferCloudwatchlogstos3
      type: 'String',                                                                                         type: 'String',
      value: kinesisCloudwatchlogsToS3Role.attrArn,                                                           value: kinesisCloudwatchlogsToS3Role.attrArn,
    });                                                                                                     });

    const ssmParameterKinesisLog = new ssm.CfnParameter(this, 'SSMParameterKinesisLog', {                   const ssmParameterKinesisLog = new ssm.CfnParameter(this, 'SSMParameterKinesisLog', {
      // ----- Problem_1   env To environment                                                      <
      name: `/${props.environment!}/infra/${props.systemname!}/CloudWatch/kinesis-logs/name`,      |          name: `/${props.env!}/infra/${props.systemname!}/CloudWatch/kinesis-logs/name`,
      type: 'String',                                                                                         type: 'String',
      value: kinesisLog.ref,                                                                                  value: kinesisLog.ref,
    });                                                                                                     });

    const ssmParameterVgaLogBucketName = new ssm.CfnParameter(this, 'SSMParameterVgaLogBucketNam            const ssmParameterVgaLogBucketName = new ssm.CfnParameter(this, 'SSMParameterVgaLogBucketNam
      // ----- Problem_1   env To environment                                                      <
      name: `/${props.environment!}/infra/${props.systemname!}/S3/VgaLog1Bucket/name`,             |          name: `/${props.env!}/infra/${props.systemname!}/S3/VgaLog1Bucket/name`,
      type: 'String',                                                                                         type: 'String',
      value: vgaLogBucket.ref,                                                                                value: vgaLogBucket.ref,
    });                                                                                                     });

    const ssmParameterVgaScriptBucketName = makeVgaScript1Bucket                                            const ssmParameterVgaScriptBucketName = makeVgaScript1Bucket
      ? new ssm.CfnParameter(this, 'SSMParameterVgaScriptBucketName', {                                       ? new ssm.CfnParameter(this, 'SSMParameterVgaScriptBucketName', {
          // ----- Problem_1   env To environment                                                  <
          name: `/${props.environment!}/infra/${props.systemname!}/S3/VgaScript1Bucket/name`,      |              name: `/${props.env!}/infra/${props.systemname!}/S3/VgaScript1Bucket/name`,
          type: 'String',                                                                                         type: 'String',
          // ----- Problem_3   undefined定義追加                                                 <
          value: vgaScriptBucket?.ref || '',                                                       |              value: vgaScriptBucket?.ref,
        })                                                                                                      })
      : undefined;                                                                                            : undefined;
    if (ssmParameterVgaScriptBucketName != null) {                                                          if (ssmParameterVgaScriptBucketName != null) {
    }                                                                                                       }

    const vgaLogBucketPolicy = new s3.CfnBucketPolicy(this, 'VgaLogBucketPolicy', {                         const vgaLogBucketPolicy = new s3.CfnBucketPolicy(this, 'VgaLogBucketPolicy', {
      bucket: vgaLogBucket.ref,                                                                               bucket: vgaLogBucket.ref,
      policyDocument: {                                                                                       policyDocument: {
        Version: '2012-10-17',                                                                                  Version: '2012-10-17',
        Statement: [                                                                                            Statement: [
          {                                                                                                       {
            Sid: 'AllowSSLRequestsOnly',                                                                            Sid: 'AllowSSLRequestsOnly',
            Effect: 'Deny',                                                                                         Effect: 'Deny',
            Principal: '*',                                                                                         Principal: '*',
            Action: 's3:*',                                                                                         Action: 's3:*',
            Resource: [                                                                                             Resource: [
              // ----- Problem_1   env To environment                                              <
              `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1`,                      |                  `arn:aws:s3:::${props.env!}-${this.account}-vga-log-1`,
              `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1/*`,                    |                  `arn:aws:s3:::${props.env!}-${this.account}-vga-log-1/*`,
            ],                                                                                                      ],
            Condition: {                                                                                            Condition: {
              Bool: {                                                                                                 Bool: {
                'aws:SecureTransport': false,                                                                           'aws:SecureTransport': false,
              },                                                                                                      },
            },                                                                                                      },
          },                                                                                                      },
          {                                                                                                       {
            Sid: 'AclCheck',                                                                                        Sid: 'AclCheck',
            Effect: 'Allow',                                                                                        Effect: 'Allow',
            Principal: {                                                                                            Principal: {
              Service: [                                                                                              Service: [
                'config.amazonaws.com',                                                                                 'config.amazonaws.com',
                'cloudtrail.amazonaws.com',                                                                             'cloudtrail.amazonaws.com',
                `logs.${this.region}.amazonaws.com`,                                                                    `logs.${this.region}.amazonaws.com`,
                'delivery.logs.amazonaws.com',                                                                          'delivery.logs.amazonaws.com',
              ],                                                                                                      ],
            },                                                                                                      },
            Action: 's3:GetBucketAcl',                                                                              Action: 's3:GetBucketAcl',
            // ----- Problem_1   env To environment                                                <
            Resource: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1`,              |                Resource: `arn:aws:s3:::${props.env!}-${this.account}-vga-log-1`,
          },                                                                                                      },
          {                                                                                                       {
            Sid: 'Write',                                                                                           Sid: 'Write',
            Effect: 'Allow',                                                                                        Effect: 'Allow',
            Principal: {                                                                                            Principal: {
              Service: [                                                                                              Service: [
                'config.amazonaws.com',                                                                                 'config.amazonaws.com',
                'cloudtrail.amazonaws.com',                                                                             'cloudtrail.amazonaws.com',
                `logs.${this.region}.amazonaws.com`,                                                                    `logs.${this.region}.amazonaws.com`,
                'guardduty.amazonaws.com',                                                                              'guardduty.amazonaws.com',
                'delivery.logs.amazonaws.com',                                                                          'delivery.logs.amazonaws.com',
              ],                                                                                                      ],
            },                                                                                                      },
            Action: 's3:PutObject',                                                                                 Action: 's3:PutObject',
            // ----- Problem_1   env To environment                                                <
            Resource: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1/*`,            |                Resource: `arn:aws:s3:::${props.env!}-${this.account}-vga-log-1/*`,
            Condition: {                                                                                            Condition: {
              StringEquals: {                                                                                         StringEquals: {
                's3:x-amz-acl': 'bucket-owner-full-control',                                                            's3:x-amz-acl': 'bucket-owner-full-control',
              },                                                                                                      },
            },                                                                                                      },
          },                                                                                                      },
          {                                                                                                       {
            Sid: 'LocationCheck & List',                                                                            Sid: 'LocationCheck & List',
            Effect: 'Allow',                                                                                        Effect: 'Allow',
            Principal: {                                                                                            Principal: {
              Service: [                                                                                              Service: [
                'config.amazonaws.com',                                                                                 'config.amazonaws.com',
                'guardduty.amazonaws.com',                                                                              'guardduty.amazonaws.com',
              ],                                                                                                      ],
            },                                                                                                      },
            Action: [                                                                                               Action: [
              's3:GetBucketLocation',                                                                                 's3:GetBucketLocation',
              's3:ListBucket',                                                                                        's3:ListBucket',
            ],                                                                                                      ],
            // ----- Problem_1   env To environment                                                <
            Resource: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1`,              |                Resource: `arn:aws:s3:::${props.env!}-${this.account}-vga-log-1`,
          },                                                                                                      },
          {                                                                                                       {
            Sid: 'ELBLogWrite',                                                                                     Sid: 'ELBLogWrite',
            Effect: 'Allow',                                                                                        Effect: 'Allow',
            Principal: {                                                                                            Principal: {
              AWS: 'arn:aws:iam::127311923021:root',                                                                  AWS: 'arn:aws:iam::127311923021:root',
            },                                                                                                      },
            Action: 's3:PutObject',                                                                                 Action: 's3:PutObject',
            // ----- Problem_1   env To environment                                                <
            Resource: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1/*`,            |                Resource: `arn:aws:s3:::${props.env!}-${this.account}-vga-log-1/*`,
          },                                                                                                      },
        ],                                                                                                      ],
      },                                                                                                      },
    });                                                                                                     });

    const vgaScriptBucketPolicy = makeVgaScript1Bucket                                                      const vgaScriptBucketPolicy = makeVgaScript1Bucket
      ? new s3.CfnBucketPolicy(this, 'VgaScriptBucketPolicy', {                                               ? new s3.CfnBucketPolicy(this, 'VgaScriptBucketPolicy', {
          // ----- Problem_3   undefined定義追加                                                  <
          bucket: vgaScriptBucket?.ref || '',                                                      |              bucket: vgaScriptBucket?.ref,
          policyDocument: `{                                                                                      policyDocument: `{
              "Version": "2012-10-17",                                                                                "Version": "2012-10-17",
              "Statement": [                                                                                          "Statement": [
                  {                                                                                                       {
                      "Sid": "AllowSSLRequestsOnly",                                                                          "Sid": "AllowSSLRequestsOnly",
                      "Effect": "Deny",                                                                                       "Effect": "Deny",
                      "Principal": "*",                                                                                       "Principal": "*",
                      "Action": "s3:*",                                                                                       "Action": "s3:*",
                      "Resource": [                                                                                           "Resource": [
                        // ----- Problem_1   env To environment                                    < 
                        "arn:aws:s3:::${props.environment!}-${this.account}-vga-script-1",         |                              "arn:aws:s3:::${props.env!}-${this.account}-vga-script-1",                             
                        "arn:aws:s3:::${props.environment!}-${this.account}-vga-script-1/*"        |                              "arn:aws:s3:::${props.env!}-${this.account}-vga-script-1/*"
                      ],                                                                                                      ],
                      "Condition": {                                                                                          "Condition": {
                          "Bool": {                                                                                               "Bool": {
                              "aws:SecureTransport": false                                                                            "aws:SecureTransport": false
                          }                                                                                                       }
                      }                                                                                                       }
                  }                                                                                                       }
              ]                                                                                                       ]
          }                                                                                                       }
          `,                                                                                                      `,
        })                                                                                                      })
      : undefined;                                                                                            : undefined;
    if (vgaScriptBucketPolicy != null) {                                                                    if (vgaScriptBucketPolicy != null) {
    }                                                                                                       }

    const kinesisErrorS3DeliveryStream = new kinesisfirehose.CfnDeliveryStream(this, 'KinesisErr            const kinesisErrorS3DeliveryStream = new kinesisfirehose.CfnDeliveryStream(this, 'KinesisErr
      // ----- Problem_1   env To environment                                                      <
      deliveryStreamName: `${props.environment!}-${props.systemname!}-kinesis-error-logs-s3-deli   |          deliveryStreamName: `${props.env!}-${props.systemname!}-kinesis-error-logs-s3-deliverystre
      deliveryStreamType: 'DirectPut',                                                                        deliveryStreamType: 'DirectPut',
      deliveryStreamEncryptionConfigurationInput: {                                                           deliveryStreamEncryptionConfigurationInput: {
        keyType: 'AWS_OWNED_CMK',                                                                               keyType: 'AWS_OWNED_CMK',
      },                                                                                                      },
      extendedS3DestinationConfiguration: {                                                                   extendedS3DestinationConfiguration: {
       // ----- Problem_1   env To environment                                                     <
        bucketArn: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1`,                 |            bucketArn: `arn:aws:s3:::${props.env!}-${this.account}-vga-log-1`,
        bufferingHints: {                                                                                       bufferingHints: {
          intervalInSeconds: 60,                                                                                  intervalInSeconds: 60,
          sizeInMBs: 5,                                                                                           sizeInMBs: 5,
        },                                                                                                      },
        compressionFormat: 'UNCOMPRESSED',                                                                      compressionFormat: 'UNCOMPRESSED',
        prefix: `pdlog/AWSLogs/${this.account}/KinesisDataFirehose/`,                                           prefix: `pdlog/AWSLogs/${this.account}/KinesisDataFirehose/`,
        roleArn: kinesisCloudwatchlogsToS3Role.attrArn,                                                         roleArn: kinesisCloudwatchlogsToS3Role.attrArn,
        errorOutputPrefix: `pdlog/AWSLogs/${this.account}/KinesisDataFirehose/error/`,                          errorOutputPrefix: `pdlog/AWSLogs/${this.account}/KinesisDataFirehose/error/`,
        cloudWatchLoggingOptions: {                                                                             cloudWatchLoggingOptions: {
          enabled: true,                                                                                          enabled: true,
          logGroupName: kinesisLog.ref,                                                                           logGroupName: kinesisLog.ref,
          logStreamName: kinesisErrorDeliveryS3DeliveryStreamError.ref,                                           logStreamName: kinesisErrorDeliveryS3DeliveryStreamError.ref,
        },                                                                                                      },
      },                                                                                                      },
    });                                                                                                     });

    const kinesisErrorSubscriptionFilter = new logs.CfnSubscriptionFilter(this, 'KinesisErrorSub            const kinesisErrorSubscriptionFilter = new logs.CfnSubscriptionFilter(this, 'KinesisErrorSub
      roleArn: kinesisCloudwatchlogsToS3Role.attrArn,                                                         roleArn: kinesisCloudwatchlogsToS3Role.attrArn,
      logGroupName: kinesisLog.ref,                                                                           logGroupName: kinesisLog.ref,
      filterPattern: '',                                                                                      filterPattern: '',
      destinationArn: kinesisErrorS3DeliveryStream.attrArn,                                                   destinationArn: kinesisErrorS3DeliveryStream.attrArn,
    });                                                                                                     });
  }                                                                                                       }
}                                                                                                       }
root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk#  

②-5.CDKエラー解消の手直し

◆JSONエラー
これで解消と思いきや、バケットポリシーを一部JSONで書いていた箇所でエラーがでてしまいました。

root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# cdk synth -c envPrefix=dev
CfnSynthesisError: Resolution error: Supplied properties not correct for "CfnBucketPolicyProps"
  policyDocument: "{\n              \"Version\": \"2012-10-17\",\n              \"Statement\": [\n                  {\n                      \"Sid\": \"AllowSSLRequestsOnly\",\n                      \"Effect\": \"Deny\",\n                      \"Principal\": \"*\",\n                      \"Action\": \"s3:*\",\n                      \"Resource\": [\n                        // ----- Problem_1   env To environment\n                        \"arn:aws:s3:::dev-<アカウントID>-vga-script-1\",\n                        \"arn:aws:s3:::dev-<アカウントID>-vga-script-1/*\"\n                      ],\n                      \"Condition\": {\n                          \"Bool\": {\n                              \"aws:SecureTransport\": false\n                          }\n                      }\n                  }\n              ]\n          }\n          " should be an 'object'.
    at ValidationResult.assertSuccess (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/core/lib/runtime.js:1:2707)
    at convertCfnBucketPolicyPropsToCloudFormation (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/aws-s3/lib/s3.generated.js:1:154378)
    at CfnBucketPolicy.renderProperties (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/aws-s3/lib/s3.generated.js:1:153161)
    at PostResolveToken.Resources (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/core/lib/cfn-resource.js:1:7779)
    at PostResolveToken.postProcess (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/core/lib/util.js:1:1653)
    at Object.postProcess (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:1205)
    at DefaultTokenResolver.resolveToken (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/core/lib/resolvable.js:1:1483)
    at resolve (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:2711)
    at Object.resolve (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:1079)
    at resolve (/infra/fintan/cdk/fintan-cdk/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:2990) {
  type: 'CfnSynthesisError'
}

Subprocess exited with error 1
root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# 

CloudFormationでYAMLではなくJSONで作っていた部分がうまく変換されませんでした。

CloudFormationコード(抜粋)
  VgaScriptBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Condition: MakeVgaScript1Bucket
    Properties:
      Bucket: !Ref VgaScriptBucket
      PolicyDocument: !Sub |
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "AllowSSLRequestsOnly",
                    "Effect": "Deny",
                    "Principal": "*",
                    "Action": "s3:*",
                    "Resource": [
                        "arn:aws:s3:::${env}-${AWS::AccountId}-vga-script-1",
                        "arn:aws:s3:::${env}-${AWS::AccountId}-vga-script-1/*"
                    ],
                    "Condition": {
                        "Bool": {
                            "aws:SecureTransport": false
                        }
                    }
                }
            ]
        }

JSONをバッククォートで括っている部分は直接objectで定義する必要があるようで、バッククォートを削除します。
また、変数を利用している箇所はバッククォートで括らないと変数が代入されないため修正していきます。

./lib/fintan-cdk-stack.tsをProblem_5として修正します。

./lib/fintan-cdk-stack.tsコード(抜粋)
    const vgaScriptBucketPolicy = makeVgaScript1Bucket
      ? new s3.CfnBucketPolicy(this, 'VgaScriptBucketPolicy', {
          // ----- Problem_3   undefined定義追加
          bucket: vgaScriptBucket?.ref || '',
          // ----- Problem_5   JSONエラー修正バッククォート削除
          //  policyDocument: `{
          policyDocument: {
            Version: '2012-10-17',
            Statement: [
              {
                Sid: 'AllowSSLRequestsOnly',
                Effect: 'Deny',
                Principal: '*',
                Action: 's3:*',
                Resource: [
                  // ----- Problem_1  env To environment
                  // ----- Problem_5 JSONエラー修正 バッククォートで括る
                  `arn:aws:s3:::${props.environment!}-${this.account}-vga-script-1`,
                  `arn:aws:s3:::${props.environment!}-${this.account}-vga-script-1/*`,
                ],
                Condition: {
                  Bool: {
                    'aws:SecureTransport': false,
                  },
                },
              },
            ],
         // ----- Problem_5   JSONエラー修正バッククォート削除
         // }
         // `,
          },
        })
      : undefined;
    if (vgaScriptBucketPolicy != null) {
    }

 

◆UpdateReplacePolicy定義の追加

CloudFormationの各種データストアに定義していた、UpdateReplacePolicyが消えていました。

DeleteionPolicyは定義されていましたが、cdk migrateすると消えてしまうようです。

CloudFormationコード(抜粋)
   KinesisLog:
    Type: AWS::Logs::LogGroup
    DeletionPolicy: Retain
    UpdateReplacePolicy: Delete
    Properties:
      LogGroupName: !Sub ${env}-kinesis-logs
      RetentionInDays: !Ref LogRetentionPeriod

UpdateReplacePolicyを定義するため、Problem_6として修正します。

./lib/fintan-cdk-stack.tsコード(抜粋)

    kinesisLog.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
    // Problem_6 UpdateReplacePolicy追加
    kinesisLog.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.DELETE; 
・
・
・
    vgaLogBucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
    // Problem_6 UpdateReplacePolicy追加
    vgaLogBucket.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN; 
・
・
・
    if (vgaScriptBucket != null) {
      vgaScriptBucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
      // Problem_6 UpdateReplacePolicy追加
      vgaScriptBucket.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN; 
    }

◆修正後のコード

JSONエラーをProblem_5のコメントで、UpdateReplacePolicy定義をProblem_6のコメントで取り込みました。

コードは長いため折り畳みます。

./lib/fintan-cdk-stack.tsファイル
fintan-cdk-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as kinesisfirehose from 'aws-cdk-lib/aws-kinesisfirehose';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as ssm from 'aws-cdk-lib/aws-ssm';

export interface FintanCdkStackProps extends cdk.StackProps {
  /**
   * (Require) Enter System Name (lowercase). *** Only lowercase English ***

   * @default 'systemname'
   */
  readonly systemname?: string;
  /**
   * (Require) Select Environment.

   * @default 'dev'
   */
  // ----- Problem_1   env To environment
  readonly environment?: string;
  /**
   * (Require) Select "true" to create VgaScript1 Bucket.

   */
  readonly makeVgaScript1Bucket: boolean;
  /**
   * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***

   * @default '3650'
   */
  // ----- Problem_2   string To number
  readonly expirationDaysApplog?: number;
  /**
   * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***

   * @default '90'
   */
  // ----- Problem_2   string To number
  readonly expirationDaysPdlog?: number;
  /**
   * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***

   * @default '450'
   */
  // ----- Problem_2   string To number
  readonly expirationDaysPerflog?: number;
  /**
   * (Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***

   * @default '3650'
   */
  // ----- Problem_2   string To number
  readonly logExpirationDaysVersioning?: number;
  /**
   * Put the number of log retention period

   * @default '90'
   */
  // ----- Problem_2   string To number
  readonly logRetentionPeriod?: number;
  /**
   * (Require) Enter Expiration In Days for versioning. If not Deleted, Leave blank. *** Only Number ***

   * @default '90'
   */
  // ----- Problem_2   string To number
  readonly scriptExpirationDaysVersioning?: number;
}

/**
 * FINTANTEST
 */
export class FintanCdkStack extends cdk.Stack {
  public constructor(scope: cdk.App, id: string, props: FintanCdkStackProps) {
    super(scope, id, props);

    // Applying default props
    props = {
      ...props,
      systemname: props.systemname ?? 'systemname',
      // ----- Problem_1   env To environment
      environment: props.environment ?? 'dev',
      // ----- Problem_2   string To number
      expirationDaysApplog: props.expirationDaysApplog ?? 3650,
      expirationDaysPdlog: props.expirationDaysPdlog ?? 90,
      expirationDaysPerflog: props.expirationDaysPerflog ?? 450,
      logExpirationDaysVersioning: props.logExpirationDaysVersioning ?? 3650,
      logRetentionPeriod: props.logRetentionPeriod ?? 90,
      scriptExpirationDaysVersioning: props.scriptExpirationDaysVersioning ?? 90,
    };

    // Conditions
    // ----- Problem_2   string To boolean
    const makeVgaScript1Bucket = props.makeVgaScript1Bucket! === true;
    // ----- Problem_3   undefined定義追加 
    const selectApplog = props.expirationDaysApplog === undefined;
    const selectLogExpirationDaysVersioning = props.logExpirationDaysVersioning === undefined;
    const selectPdlog = props.expirationDaysPdlog === undefined;
    const selectPerflog = props.expirationDaysPerflog === undefined;
    const selectScriptExpirationDaysVersioning = props.scriptExpirationDaysVersioning === undefined;
    // Resources
    const kinesisCloudwatchlogsToS3Role = new iam.CfnRole(this, 'KinesisCloudwatchlogsToS3Role', {
      roleName: 'kinesis-cloudwatchlogstos3-role',
      path: '/',
      assumeRolePolicyDocument: {
        Version: '2012-10-17',
        Statement: [
          {
            Effect: 'Allow',
            Principal: {
              Service: [
                'firehose.amazonaws.com',
                'logs.us-east-1.amazonaws.com',
              ],
            },
            Action: 'sts:AssumeRole',
          },
        ],
      },
      policies: [
        {
          policyName: 'kinesis-cloudwatchlogstos3-policy',
          policyDocument: {
            Version: '2012-10-17',
            Statement: [
              {
                Effect: 'Allow',
                Action: [
                  's3:AbortMultipartUpload',
                  's3:GetBucketLocation',
                  's3:GetObject',
                  's3:ListBucket',
                  's3:ListBucketMultipartUploads',
                  's3:PutObject',
                  's3:PutObjectAcl',
                ],
                Resource: '*',
              },
              {
                Effect: 'Allow',
                Action: [
                  'kinesis:DescribeStream',
                  'kinesis:GetShardIterator',
                  'kinesis:GetRecords',
                  'kinesis:ListShards',
                ],
                Resource: '*',
              },
              {
                Effect: 'Allow',
                Action: [
                  'firehose:*',
                ],
                Resource: '*',
              },
              {
                Effect: 'Allow',
                Action: [
                  'iam:PassRole',
                ],
                Resource: '*',
              },
              {
                Effect: 'Allow',
                Action: [
                  'logs:PutLogEvents',
                ],
                Resource: '*',
              },
              {
                Effect: 'Allow',
                Action: [
                  'lambda:InvokeFunction',
                  'lambda:GetFunctionConfiguration',
                ],
                Resource: '*',
              },
              {
                Effect: 'Allow',
                Action: [
                  'kms:Decrypt',
                ],
                Resource: `arn:aws:kms:${this.region}:${this.account}:key/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLDER%`,
                Condition: {
                  StringEquals: {
                    'kms:ViaService': `kinesis.${this.region}.amazonaws.com`,
                  },
                  StringLike: {
                    'kms:EncryptionContext:aws:kinesis:arn': `arn:aws:kinesis:${this.region}:${this.account}:stream/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLDER%`,
                  },
                },
              },
            ],
          },
        },
      ],
    });

    const kinesisLog = new logs.CfnLogGroup(this, 'KinesisLog', {
      // ----- Problem_1   env To environment
      logGroupName: `${props.environment!}-kinesis-logs`,
      retentionInDays: props.logRetentionPeriod!,
    });
    kinesisLog.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
    // Problem_6 UpdateReplacePolicy追加
    kinesisLog.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.DELETE; 

    const vgaLogBucket = new s3.CfnBucket(this, 'VgaLogBucket', {
      // ----- Problem_1   env To environment
      bucketName: `${props.environment!}-${this.account}-vga-log-1`,
      bucketEncryption: {
        serverSideEncryptionConfiguration: [
          {
            serverSideEncryptionByDefault: {
              sseAlgorithm: 'AES256',
            },
          },
        ],
      },
      ownershipControls: {
        rules: [
          {
            objectOwnership: 'ObjectWriter',
          },
        ],
      },
      publicAccessBlockConfiguration: {
        blockPublicAcls: true,
        blockPublicPolicy: true,
        ignorePublicAcls: true,
        restrictPublicBuckets: true,
      },
      // ----- Problem_3   undefined定義追加 ※配列の中にundefinedは指定できない
      lifecycleConfiguration: selectApplog && selectPdlog && selectPerflog && selectLogExpirationDaysVersioning ? undefined : {
        rules: [
          {
            id: 'AWSLogs_delete_applog',
            status: 'Enabled',
            prefix: 'applog/',
            expirationInDays: props.expirationDaysApplog!,
          },
          {
            id: 'AWSLogs_delete_pdlog',
            status: 'Enabled',
            prefix: 'pdlog/',
            expirationInDays: props.expirationDaysPdlog!,
          },
          {
            id: 'AWSLogs_delete_perflog',
            status: 'Enabled',
            prefix: 'perflog/',
            expirationInDays: props.expirationDaysPerflog!,
          },
          {
            id: 'NoncurrentVersionExpiration',
            status: 'Enabled',
            noncurrentVersionExpiration: {
              noncurrentDays: props.logExpirationDaysVersioning!,
            },
          },
        ],
      },
      metricsConfigurations: [
        {
          id: 'EntireBucket',
        },
      ],
      versioningConfiguration: {
        status: 'Enabled',
      },
      tags: [
        {
          key: 'Name',
          // ----- Problem_1   env To environment   
          value: `${props.environment!}-${this.account}-vga-log-1`,
        },
        {
          key: 'System',
          value: `${props.systemname!}`,
        },
      ],
    });
    vgaLogBucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
    // Problem_6 UpdateReplacePolicy追加
    vgaLogBucket.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN; 

    const vgaScriptBucket = makeVgaScript1Bucket
      ? new s3.CfnBucket(this, 'VgaScriptBucket', {
          // ----- Problem_1   env To environment
          bucketName: `${props.environment!}-${this.account}-vga-script-1`,
          bucketEncryption: {
            serverSideEncryptionConfiguration: [
              {
                serverSideEncryptionByDefault: {
                  sseAlgorithm: 'AES256',
                },
              },
            ],
          },
          ownershipControls: {
            rules: [
              {
                objectOwnership: 'ObjectWriter',
              },
            ],
          },
          publicAccessBlockConfiguration: {
            blockPublicAcls: true,
            blockPublicPolicy: true,
            ignorePublicAcls: true,
            restrictPublicBuckets: true,
          },
          versioningConfiguration: {
            status: 'Enabled',
          },
          // ----- Problem_3   undefined定義追加 ※配列の中にundefinedは指定できない
          lifecycleConfiguration: selectScriptExpirationDaysVersioning ? undefined : {
            rules: [
              {
                id: 'NoncurrentVersionExpiration',
                status: 'Enabled',
                noncurrentVersionExpiration: {
                  noncurrentDays: props.scriptExpirationDaysVersioning!,
                },
              },
            ],
          },
          tags: [
            {
              key: 'Name',
              // ----- Problem_1   env To environment
              value: `${props.environment!}-${this.account}-vga-script-1`,
            },
            {
              key: 'System',
              // ----- Problem_1   env environment
              value: `${props.systemname!}`,
            },
          ],
        })
      : undefined;
    if (vgaScriptBucket != null) {
      vgaScriptBucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
      // Problem_6 UpdateReplacePolicy追加
      vgaScriptBucket.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN; 
    }

    const kinesisErrorDeliveryS3DeliveryStreamError = new logs.CfnLogStream(this, 'KinesisErrorDeliveryS3DeliveryStreamError', {
      logGroupName: kinesisLog.ref,
      // ----- Problem_1   env To environment
      logStreamName: `${props.environment!}-${props.systemname!}-kinesis-error-logs-s3-deliverystream-error`,
    });

    const ssmParameterKinesisCloudwatchlogsToS3Role = new ssm.CfnParameter(this, 'SSMParameterKinesisCloudwatchlogsToS3Role', {
      // ----- Problem_1   env To environment
      name: `/${props.environment!}/infra/${props.systemname!}/IAMRole/KinesisTransferCloudwatchlogstos3Role/arn`,
      type: 'String',
      value: kinesisCloudwatchlogsToS3Role.attrArn,
    });

    const ssmParameterKinesisLog = new ssm.CfnParameter(this, 'SSMParameterKinesisLog', {
      // ----- Problem_1   env To environment
      name: `/${props.environment!}/infra/${props.systemname!}/CloudWatch/kinesis-logs/name`,
      type: 'String',
      value: kinesisLog.ref,
    });

    const ssmParameterVgaLogBucketName = new ssm.CfnParameter(this, 'SSMParameterVgaLogBucketName', {
      // ----- Problem_1   env To environment
      name: `/${props.environment!}/infra/${props.systemname!}/S3/VgaLog1Bucket/name`,
      type: 'String',
      value: vgaLogBucket.ref,
    });

    const ssmParameterVgaScriptBucketName = makeVgaScript1Bucket
      ? new ssm.CfnParameter(this, 'SSMParameterVgaScriptBucketName', {
          // ----- Problem_1   env To environment
          name: `/${props.environment!}/infra/${props.systemname!}/S3/VgaScript1Bucket/name`,
          type: 'String',
          // ----- Problem_3   undefined定義追加
          value: vgaScriptBucket?.ref || '',
        })
      : undefined;
    if (ssmParameterVgaScriptBucketName != null) {
    }

    const vgaLogBucketPolicy = new s3.CfnBucketPolicy(this, 'VgaLogBucketPolicy', {
      bucket: vgaLogBucket.ref,
      policyDocument: {
        Version: '2012-10-17',
        Statement: [
          {
            Sid: 'AllowSSLRequestsOnly',
            Effect: 'Deny',
            Principal: '*',
            Action: 's3:*',
            Resource: [
              `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1`,
              `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1/*`,
            ],
            Condition: {
              Bool: {
                'aws:SecureTransport': false,
              },
            },
          },
          {
            Sid: 'AclCheck',
            Effect: 'Allow',
            Principal: {
              Service: [
                'config.amazonaws.com',
                'cloudtrail.amazonaws.com',
                `logs.${this.region}.amazonaws.com`,
                'delivery.logs.amazonaws.com',
              ],
            },
            Action: 's3:GetBucketAcl',
            // ----- Problem_1   env To environment
            Resource: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1`,
          },
          {
            Sid: 'Write',
            Effect: 'Allow',
            Principal: {
              Service: [
                'config.amazonaws.com',
                'cloudtrail.amazonaws.com',
                `logs.${this.region}.amazonaws.com`,
                'guardduty.amazonaws.com',
                'delivery.logs.amazonaws.com',
              ],
            },
            Action: 's3:PutObject',
            // ----- Problem_1   env To environment
            Resource: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1/*`,
            Condition: {
              StringEquals: {
                's3:x-amz-acl': 'bucket-owner-full-control',
              },
            },
          },
          {
            Sid: 'LocationCheck & List',
            Effect: 'Allow',
            Principal: {
              Service: [
                'config.amazonaws.com',
                'guardduty.amazonaws.com',
              ],
            },
            Action: [
              's3:GetBucketLocation',
              's3:ListBucket',
            ],
            // ----- Problem_1   env To environment
            Resource: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1`,
          },
          {
            Sid: 'ELBLogWrite',
            Effect: 'Allow',
            Principal: {
              AWS: 'arn:aws:iam::127311923021:root',
            },
            Action: 's3:PutObject',
            // ----- Problem_1   env To environment
            Resource: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1/*`,
          },
        ],
      },
    });

    const vgaScriptBucketPolicy = makeVgaScript1Bucket
      ? new s3.CfnBucketPolicy(this, 'VgaScriptBucketPolicy', {
          // ----- Problem_3   undefined定義追加
          bucket: vgaScriptBucket?.ref || '',
          // ----- Problem_5   JSONエラー修正バッククォート削除
          policyDocument: {
            Version: '2012-10-17',
            Statement: [
              {
                Sid: 'AllowSSLRequestsOnly',
                Effect: 'Deny',
                Principal: '*',
                Action: 's3:*',
                Resource: [
                  // ----- Problem_1  env To environment
                  // ----- Problem_5 JSONエラー修正 バッククォートで括る
                  `arn:aws:s3:::${props.environment!}-${this.account}-vga-script-1`,
                  `arn:aws:s3:::${props.environment!}-${this.account}-vga-script-1/*`,
                ],
                Condition: {
                  Bool: {
                    'aws:SecureTransport': false,
                  },
                },
              },
            ],
         // ----- Problem_5   JSONエラー修正バッククォート削除
          },
        })
      : undefined;
    if (vgaScriptBucketPolicy != null) {
    }

    const kinesisErrorS3DeliveryStream = new kinesisfirehose.CfnDeliveryStream(this, 'KinesisErrorS3DeliveryStream', {
      // ----- Problem_1   env To environment
      deliveryStreamName: `${props.environment!}-${props.systemname!}-kinesis-error-logs-s3-deliverystream`,
      deliveryStreamType: 'DirectPut',
      deliveryStreamEncryptionConfigurationInput: {
        keyType: 'AWS_OWNED_CMK',
      },
      extendedS3DestinationConfiguration: {
       // ----- Problem_1   env To environment
        bucketArn: `arn:aws:s3:::${props.environment!}-${this.account}-vga-log-1`,
        bufferingHints: {
          intervalInSeconds: 60,
          sizeInMBs: 5,
        },
        compressionFormat: 'UNCOMPRESSED',
        prefix: `pdlog/AWSLogs/${this.account}/KinesisDataFirehose/`,
        roleArn: kinesisCloudwatchlogsToS3Role.attrArn,
        errorOutputPrefix: `pdlog/AWSLogs/${this.account}/KinesisDataFirehose/error/`,
        cloudWatchLoggingOptions: {
          enabled: true,
          logGroupName: kinesisLog.ref,
          logStreamName: kinesisErrorDeliveryS3DeliveryStreamError.ref,
        },
      },
    });

    const kinesisErrorSubscriptionFilter = new logs.CfnSubscriptionFilter(this, 'KinesisErrorSubscriptionFilter', {
      roleArn: kinesisCloudwatchlogsToS3Role.attrArn,
      logGroupName: kinesisLog.ref,
      filterPattern: '',
      destinationArn: kinesisErrorS3DeliveryStream.attrArn,
    });
  }
}

②-6.エラー解消の確認

これで、再度cdk synthコマンドでCloudFormationパラメータ出力を試してみます。

./bin/fintan-cdk.tsでコンテキストを定義したので-cオプションも必要となります。

以下の結果の通り、無事上手くいきました。

cdk synthコマンド実行結果
root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# cdk synth -c envPrefix=dev
Resources:
  KinesisCloudwatchlogsToS3Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - firehose.amazonaws.com
                - logs.us-east-1.amazonaws.com
            Action: sts:AssumeRole
      Path: /
      Policies:
        - PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - s3:AbortMultipartUpload
                  - s3:GetBucketLocation
                  - s3:GetObject
                  - s3:ListBucket
                  - s3:ListBucketMultipartUploads
                  - s3:PutObject
                  - s3:PutObjectAcl
                Resource: "*"
              - Effect: Allow
                Action:
                  - kinesis:DescribeStream
                  - kinesis:GetShardIterator
                  - kinesis:GetRecords
                  - kinesis:ListShards
                Resource: "*"
              - Effect: Allow
                Action:
                  - firehose:*
                Resource: "*"
              - Effect: Allow
                Action:
                  - iam:PassRole
                Resource: "*"
              - Effect: Allow
                Action:
                  - logs:PutLogEvents
                Resource: "*"
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction
                  - lambda:GetFunctionConfiguration
                Resource: "*"
              - Effect: Allow
                Action:
                  - kms:Decrypt
                Resource: arn:aws:kms:ap-northeast-1:<アカウントID>:key/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLDER%
                Condition:
                  StringEquals:
                    kms:ViaService: kinesis.ap-northeast-1.amazonaws.com
                  StringLike:
                    kms:EncryptionContext:aws:kinesis:arn: arn:aws:kinesis:ap-northeast-1:<アカウントID>:stream/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLDER%
          PolicyName: kinesis-cloudwatchlogstos3-policy
      RoleName: kinesis-cloudwatchlogstos3-role
    Metadata:
      aws:cdk:path: fintan-cdk/KinesisCloudwatchlogsToS3Role
  KinesisLog:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: dev-kinesis-logs
      RetentionInDays: 90
    UpdateReplacePolicy: Delete
    DeletionPolicy: Retain
    Metadata:
      aws:cdk:path: fintan-cdk/KinesisLog
  VgaLogBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      BucketName: dev-<アカウントID>-vga-log-1
      LifecycleConfiguration:
        Rules:
          - ExpirationInDays: 3650
            Id: AWSLogs_delete_applog
            Prefix: applog/
            Status: Enabled
          - ExpirationInDays: 90
            Id: AWSLogs_delete_pdlog
            Prefix: pdlog/
            Status: Enabled
          - ExpirationInDays: 450
            Id: AWSLogs_delete_perflog
            Prefix: perflog/
            Status: Enabled
          - Id: NoncurrentVersionExpiration
            NoncurrentVersionExpiration:
              NoncurrentDays: 3650
            Status: Enabled
      MetricsConfigurations:
        - Id: EntireBucket
      OwnershipControls:
        Rules:
          - ObjectOwnership: ObjectWriter
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      Tags:
        - Key: Name
          Value: dev-<アカウントID>-vga-log-1
        - Key: System
          Value: fintan
      VersioningConfiguration:
        Status: Enabled
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Metadata:
      aws:cdk:path: fintan-cdk/VgaLogBucket
  VgaScriptBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      BucketName: dev-<アカウントID>-vga-script-1
      LifecycleConfiguration:
        Rules:
          - Id: NoncurrentVersionExpiration
            NoncurrentVersionExpiration:
              NoncurrentDays: 90
            Status: Enabled
      OwnershipControls:
        Rules:
          - ObjectOwnership: ObjectWriter
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      Tags:
        - Key: Name
          Value: dev-<アカウントID>-vga-script-1
        - Key: System
          Value: fintan
      VersioningConfiguration:
        Status: Enabled
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain

    Metadata:
      aws:cdk:path: fintan-cdk/VgaScriptBucket
  KinesisErrorDeliveryS3DeliveryStreamError:
    Type: AWS::Logs::LogStream
    Properties:
      LogGroupName:
        Ref: KinesisLog
      LogStreamName: dev-fintan-kinesis-error-logs-s3-deliverystream-error
    Metadata:
      aws:cdk:path: fintan-cdk/KinesisErrorDeliveryS3DeliveryStreamError
  SSMParameterKinesisCloudwatchlogsToS3Role:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /dev/infra/fintan/IAMRole/KinesisTransferCloudwatchlogstos3Role/arn
      Type: String
      Value:
        Fn::GetAtt:
          - KinesisCloudwatchlogsToS3Role
          - Arn
    Metadata:
      aws:cdk:path: fintan-cdk/SSMParameterKinesisCloudwatchlogsToS3Role
  SSMParameterKinesisLog:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /dev/infra/fintan/CloudWatch/kinesis-logs/name
      Type: String
      Value:
        Ref: KinesisLog
    Metadata:
      aws:cdk:path: fintan-cdk/SSMParameterKinesisLog
  SSMParameterVgaLogBucketName:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /dev/infra/fintan/S3/VgaLog1Bucket/name
      Type: String
      Value:
        Ref: VgaLogBucket
    Metadata:
      aws:cdk:path: fintan-cdk/SSMParameterVgaLogBucketName
  SSMParameterVgaScriptBucketName:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /dev/infra/fintan/S3/VgaScript1Bucket/name
      Type: String
      Value:
        Ref: VgaScriptBucket
    Metadata:
      aws:cdk:path: fintan-cdk/SSMParameterVgaScriptBucketName
  VgaLogBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: VgaLogBucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: AllowSSLRequestsOnly
            Effect: Deny
            Principal: "*"
            Action: s3:*
            Resource:
              - arn:aws:s3:::dev-<アカウントID>-vga-log-1
              - arn:aws:s3:::dev-<アカウントID>-vga-log-1/*
            Condition:
              Bool:
                aws:SecureTransport: false
          - Sid: AclCheck
            Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
                - cloudtrail.amazonaws.com
                - logs.ap-northeast-1.amazonaws.com
                - delivery.logs.amazonaws.com
            Action: s3:GetBucketAcl
            Resource: arn:aws:s3:::dev-<アカウントID>-vga-log-1
          - Sid: Write
            Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
                - cloudtrail.amazonaws.com
                - logs.ap-northeast-1.amazonaws.com
                - guardduty.amazonaws.com
                - delivery.logs.amazonaws.com
            Action: s3:PutObject
            Resource: arn:aws:s3:::dev-<アカウントID>-vga-log-1/*
            Condition:
              StringEquals:
                s3:x-amz-acl: bucket-owner-full-control
          - Sid: LocationCheck & List
            Effect: Allow
            Principal:
              Service:
                - config.amazonaws.com
                - guardduty.amazonaws.com
            Action:
              - s3:GetBucketLocation
              - s3:ListBucket
            Resource: arn:aws:s3:::dev-<アカウントID>-vga-log-1
          - Sid: ELBLogWrite
            Effect: Allow
            Principal:
              AWS: arn:aws:iam::127311923021:root
            Action: s3:PutObject
            Resource: arn:aws:s3:::dev-<アカウントID>-vga-log-1/*
    Metadata:
      aws:cdk:path: fintan-cdk/VgaLogBucketPolicy
  VgaScriptBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: VgaScriptBucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: AllowSSLRequestsOnly
            Effect: Deny
            Principal: "*"
            Action: s3:*
            Resource:
              - arn:aws:s3:::dev-<アカウントID>-vga-script-1
              - arn:aws:s3:::dev-<アカウントID>-vga-script-1/*
            Condition:
              Bool:
                aws:SecureTransport: false
    Metadata:
      aws:cdk:path: fintan-cdk/VgaScriptBucketPolicy
  KinesisErrorS3DeliveryStream:
    Type: AWS::KinesisFirehose::DeliveryStream
    Properties:
      DeliveryStreamEncryptionConfigurationInput:
        KeyType: AWS_OWNED_CMK
      DeliveryStreamName: dev-fintan-kinesis-error-logs-s3-deliverystream
      DeliveryStreamType: DirectPut
      ExtendedS3DestinationConfiguration:
        BucketARN: arn:aws:s3:::dev-<アカウントID>-vga-log-1
        BufferingHints:
          IntervalInSeconds: 60
          SizeInMBs: 5
        CloudWatchLoggingOptions:
          Enabled: true
          LogGroupName:
            Ref: KinesisLog
          LogStreamName:
            Ref: KinesisErrorDeliveryS3DeliveryStreamError
        CompressionFormat: UNCOMPRESSED
        ErrorOutputPrefix: pdlog/AWSLogs/<アカウントID>/KinesisDataFirehose/error/
        Prefix: pdlog/AWSLogs/<アカウントID>/KinesisDataFirehose/
        RoleARN:
          Fn::GetAtt:
            - KinesisCloudwatchlogsToS3Role
            - Arn
    Metadata:
      aws:cdk:path: fintan-cdk/KinesisErrorS3DeliveryStream
  KinesisErrorSubscriptionFilter:
    Type: AWS::Logs::SubscriptionFilter
    Properties:
      DestinationArn:
        Fn::GetAtt:
          - KinesisErrorS3DeliveryStream
          - Arn
      FilterPattern: ""
      LogGroupName:
        Ref: KinesisLog
      RoleArn:
        Fn::GetAtt:
          - KinesisCloudwatchlogsToS3Role
          - Arn
    Metadata:
      aws:cdk:path: fintan-cdk/KinesisErrorSubscriptionFilter
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:H4sIAAAAAAAA/z2NwQrCMBBEv8V7uloF0auKXjyU9gMkxq2uTbNlN1Gk9N+lFjzNG4aZWUK+XMNiZt+auVuTebpCX0XrGmPfeunJtrCvQ8kejee7Qr+vw5nvJ+HUmYmrKGjb0VTpqk6oi8ThSD6iDEZXv84uuQaj+VPBntxnMKq/g8KKbTGimIYCKmlNgg9WHMMDenqhfKajYRwpUTmJw8EEviE8df7KN5BvYTF7KlEmKURqEcpJv8YP/tXnAAAA
    Metadata:
      aws:cdk:path: fintan-cdk/CDKMetadata/Default
Parameters:
  BootstrapVersion:
    Type: AWS::SSM::Parameter::Value
    Default: /cdk-bootstrap/hnb659fds/version
    Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]
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.


root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# 

③cdk deployで既存スタックの上書き

出来上がったCDKをデプロイしていきます。

③-1.cdk diff

既存スタックとの比較を行います。※
(※)CloudFormationの変更セットを作成してくれますが、CloudFormationの変更セットからはすぐに削除されてしまいます。

◆変更点

リソースとして以下が追加されています。

CloudFormationのParameterにCDK BootStrapの定義が追加

◆その他の差分

・過去定義していたCloudFormationのParameterが消えている。

・Subで変換していた環境変数※が実際の値に変換されている。アカウントIDは機密情報のため<AccountID>で表示していますが、実際の表示は自身のアカウントIDです。

※${env}、${${AWS::Region}、${AWS::AccountId}

CloudFormationのCondition定義が消えている。

root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# cdk diff -c envPrefix=dev
Stack fintan-cdk
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Template
[-] AWSTemplateFormatVersion AWSTemplateFormatVersion: 2010-09-09
[-] Description Description: FINTANTEST

IAM Statement Changes
┌───┬─────────────────────────────────────────────────┬────────┬─────────────────────────────────────────────────┬─────────────────────────────────────────────────┬────────────────────────────────────────────────────┐
│   │ Resource                                        │ Effect │ Action                                          │ Principal                                       │ Condition                                          │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ - │ {"Fn::Sub":"arn:aws:kms:${AWS::Region}:${AWS::A │ Allow  │ kms:Decrypt                                     │ AWS:${KinesisCloudwatchlogsToS3Role}            │ "StringEquals": {                                  │
│   │ ccountId}:key/%FIREHOSE_POLICY_TEMPLATE_PLACEHO │        │                                                 │                                                 │   "kms:ViaService": "{\"Fn::Sub\":\"kinesis.${AWS: │
│   │ LDER%"}                                         │        │                                                 │                                                 │ :Region}.amazonaws.com\"}"                         │
│   │                                                 │        │                                                 │                                                 │ },                                                 │
│   │                                                 │        │                                                 │                                                 │ "StringLike": {                                    │
│   │                                                 │        │                                                 │                                                 │   "kms:EncryptionContext:aws:kinesis:arn": "{\"Fn: │
│   │                                                 │        │                                                 │                                                 │ :Sub\":\"arn:aws:kinesis:${AWS::Region}:${AWS::Acc │
│   │                                                 │        │                                                 │                                                 │ ountId}:stream/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLD │
│   │                                                 │        │                                                 │                                                 │ ER%\"}"                                            │
│   │                                                 │        │                                                 │                                                 │ }                                                  │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Allow  │ s3:GetBucketAcl                                 │ Service:cloudtrail.amazonaws.com                │                                                    │
│   │ d}-vga-log-1"}                                  │        │                                                 │ Service:config.amazonaws.com                    │                                                    │
│   │                                                 │        │                                                 │ Service:delivery.logs.amazonaws.com             │                                                    │
│   │                                                 │        │                                                 │ Service:{"Fn::Sub":"logs.${AWS::Region}.amazona │                                                    │
│   │                                                 │        │                                                 │ ws.com"}                                        │                                                    │
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Allow  │ s3:GetBucketLocation                            │ Service:config.amazonaws.com                    │                                                    │
│   │ d}-vga-log-1"}                                  │        │ s3:ListBucket                                   │ Service:guardduty.amazonaws.com                 │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Deny   │ s3:*                                            │ *                                               │ "Bool": {                                          │
│   │ d}-vga-log-1"}                                  │        │                                                 │                                                 │   "aws:SecureTransport": false                     │
│   │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │        │                                                 │                                                 │ }                                                  │
│   │ d}-vga-log-1/*"}                                │        │                                                 │                                                 │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Allow  │ s3:PutObject                                    │ Service:cloudtrail.amazonaws.com                │ "StringEquals": {                                  │
│   │ d}-vga-log-1/*"}                                │        │                                                 │ Service:config.amazonaws.com                    │   "s3:x-amz-acl": "bucket-owner-full-control"      │
│   │                                                 │        │                                                 │ Service:delivery.logs.amazonaws.com             │ }                                                  │
│   │                                                 │        │                                                 │ Service:guardduty.amazonaws.com                 │                                                    │
│   │                                                 │        │                                                 │ Service:{"Fn::Sub":"logs.${AWS::Region}.amazona │                                                    │
│   │                                                 │        │                                                 │ ws.com"}                                        │                                                    │
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Allow  │ s3:PutObject                                    │ AWS:arn:aws:iam::127311923021:root              │                                                    │
│   │ d}-vga-log-1/*"}                                │        │                                                 │                                                 │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:kms:us-east-1:<AccountID>:key/%FIREHOS  │ Allow  │ kms:Decrypt                                     │ AWS:${KinesisCloudwatchlogsToS3Role}            │ "StringEquals": {                                  │
│   │ E_POLICY_TEMPLATE_PLACEHOLDER%                  │        │                                                 │                                                 │   "kms:ViaService": "kinesis.us-east-1.amazonaws.c │
│   │                                                 │        │                                                 │                                                 │ om"                                                │
│   │                                                 │        │                                                 │                                                 │ },                                                 │
│   │                                                 │        │                                                 │                                                 │ "StringLike": {                                    │
│   │                                                 │        │                                                 │                                                 │   "kms:EncryptionContext:aws:kinesis:arn": "arn:aw │
│   │                                                 │        │                                                 │                                                 │ s:kinesis:us-east-1:<AccountID>:stream/%FIREHOSE_  │
│   │                                                 │        │                                                 │                                                 │ POLICY_TEMPLATE_PLACEHOLDER%"                      │
│   │                                                 │        │                                                 │                                                 │ }                                                  │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1          │ Allow  │ s3:GetBucketAcl                                 │ Service:cloudtrail.amazonaws.com                │                                                    │
│   │                                                 │        │                                                 │ Service:config.amazonaws.com                    │                                                    │
│   │                                                 │        │                                                 │ Service:delivery.logs.amazonaws.com             │                                                    │
│   │                                                 │        │                                                 │ Service:logs.us-east-1.amazonaws.com            │                                                    │
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1          │ Allow  │ s3:GetBucketLocation                            │ Service:config.amazonaws.com                    │                                                    │
│   │                                                 │        │ s3:ListBucket                                   │ Service:guardduty.amazonaws.com                 │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1          │ Deny   │ s3:*                                            │ *                                               │ "Bool": {                                          │
│   │ arn:aws:s3:::dev-<AccountID>-vga-log-1/*        │        │                                                 │                                                 │   "aws:SecureTransport": false                     │
│   │                                                 │        │                                                 │                                                 │ }                                                  │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1/*        │ Allow  │ s3:PutObject                                    │ Service:cloudtrail.amazonaws.com                │ "StringEquals": {                                  │
│   │                                                 │        │                                                 │ Service:config.amazonaws.com                    │   "s3:x-amz-acl": "bucket-owner-full-control"      │
│   │                                                 │        │                                                 │ Service:delivery.logs.amazonaws.com             │ }                                                  │
│   │                                                 │        │                                                 │ Service:guardduty.amazonaws.com                 │                                                    │
│   │                                                 │        │                                                 │ Service:logs.us-east-1.amazonaws.com            │                                                    │
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1/*        │ Allow  │ s3:PutObject                                    │ AWS:arn:aws:iam::127311923021:root              │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:s3:::dev-<AccountID>-vga-script-1       │ Deny   │ s3:*                                            │ *                                               │ "Bool": {                                          │
│   │ arn:aws:s3:::dev-<AccountID>-vga-script-1/*     │        │                                                 │                                                 │   "aws:SecureTransport": false                     │
│   │                                                 │        │                                                 │                                                 │ }                                                  │
└───┴─────────────────────────────────────────────────┴────────┴─────────────────────────────────────────────────┴─────────────────────────────────────────────────┴────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Parameters
[-] Parameter SYSTEMNAME: {"Description":"(Require) Enter System Name (lowercase). *** Only lowercase English ***\n","Type":"String","AllowedPattern":"[a-z,-,_]+","Default":"systemname"}
[-] Parameter env: {"Description":"(Require) Select Environment.\n","Type":"String","AllowedValues":["dev","stg","prd"],"Default":"dev"}
[-] Parameter MakeVgaScript1Bucket: {"Description":"(Require) Select \"true\" to create VgaScript1 Bucket.\n","Type":"String","AllowedValues":[true,false]}
[-] Parameter ExpirationDaysApplog: {"Description":"(Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***\n","Type":"String","Default":3650}
[-] Parameter ExpirationDaysPdlog: {"Description":"(Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***\n","Type":"String","Default":90}
[-] Parameter ExpirationDaysPerflog: {"Description":"(Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***\n","Type":"String","Default":450}
[-] Parameter LogExpirationDaysVersioning: {"Description":"(Require) Enter Expiration In Days . If not Deleted, Leave blank. *** Only Number ***\n","Type":"String","Default":3650}
[-] Parameter LogRetentionPeriod: {"Description":"Put the number of log retention period\n","Type":"String","AllowedValues":[1,3,5,7,14,30,60,90,120,150,180,365,400,545,731,1827,3653],"Default":90}
[-] Parameter ScriptExpirationDaysVersioning: {"Description":"(Require) Enter Expiration In Days for versioning. If not Deleted, Leave blank. *** Only Number ***\n","Type":"String","Default":90}
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}

Conditions
[-] Condition MakeVgaScript1Bucket: {"Fn::Equals":[{"Ref":"MakeVgaScript1Bucket"},"true"]}
[-] Condition SelectApplog: {"Fn::Equals":[{"Ref":"ExpirationDaysApplog"},""]}
[-] Condition SelectPdlog: {"Fn::Equals":[{"Ref":"ExpirationDaysPdlog"},""]}
[-] Condition SelectPerflog: {"Fn::Equals":[{"Ref":"ExpirationDaysPerflog"},""]}
[-] Condition SelectLogExpirationDaysVersioning: {"Fn::Equals":[{"Ref":"LogExpirationDaysVersioning"},""]}
[-] Condition SelectScriptExpirationDaysVersioning: {"Fn::Equals":[{"Ref":"ScriptExpirationDaysVersioning"},""]}

Resources
[~] AWS::S3::Bucket VgaScriptBucket VgaScriptBucket 
 └─ [-] Condition
     └─ MakeVgaScript1Bucket
[~] AWS::S3::BucketPolicy VgaScriptBucketPolicy VgaScriptBucketPolicy 
 ├─ [~] PolicyDocument
 │   ├─ [-] Removed: .Fn::Sub
 │   ├─ [+] Added: .Statement
 │   └─ [+] Added: .Version
 └─ [-] Condition
     └─ MakeVgaScript1Bucket
[~] AWS::KinesisFirehose::DeliveryStream KinesisErrorS3DeliveryStream KinesisErrorS3DeliveryStream 
 └─ [~] ExtendedS3DestinationConfiguration
     ├─ [~] .BucketARN:
     │   └─ @@ -1,3 +1,1 @@
     │      [-] {
     │      [-]   "Fn::Sub": "arn:aws:s3:::${env}-${AWS::AccountId}-vga-log-1"
     │      [-] }
     │      [+] "arn:aws:s3:::dev-<AccountID>-vga-log-1"
     ├─ [~] .ErrorOutputPrefix:
     │   └─ @@ -1,3 +1,1 @@
     │      [-] {
     │      [-]   "Fn::Sub": "pdlog/AWSLogs/${AWS::AccountId}/KinesisDataFirehose/error/"
     │      [-] }
     │      [+] "pdlog/AWSLogs/<AccountID>/KinesisDataFirehose/error/"
     └─ [~] .Prefix:
         └─ @@ -1,3 +1,1 @@
            [-] {
            [-]   "Fn::Sub": "pdlog/AWSLogs/${AWS::AccountId}/KinesisDataFirehose/"
            [-] }
            [+] "pdlog/AWSLogs/<AccountID>/KinesisDataFirehose/"
[~] AWS::SSM::Parameter SSMParameterVgaScriptBucketName SSMParameterVgaScriptBucketName 
 └─ [-] Condition
     └─ MakeVgaScript1Bucket

Other Changes
[+] Unknown 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."}]}}


✨  Number of stacks with differences: 1

root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk#  

③-2.cdk deploy

デプロイしてみます。

無事デプロイは完了しました。

root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# cdk deploy -c envPrefix=dev

✨  Synthesis time: 7.51s

fintan-cdk:  start: Building fe6934b56498725f26276efb5bc8857e4020b7779b046601aea2837c9e3212cb:<AccountID>-us-east-1
fintan-cdk:  success: Built fe6934b56498725f26276efb5bc8857e4020b7779b046601aea2837c9e3212cb:<AccountID>-us-east-1
fintan-cdk:  start: Publishing fe6934b56498725f26276efb5bc8857e4020b7779b046601aea2837c9e3212cb:<AccountID>-us-east-1
fintan-cdk:  success: Published fe6934b56498725f26276efb5bc8857e4020b7779b046601aea2837c9e3212cb:<AccountID>-us-east-1
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬─────────────────────────────────────────────────┬────────┬─────────────────────────────────────────────────┬─────────────────────────────────────────────────┬────────────────────────────────────────────────────┐
│   │ Resource                                        │ Effect │ Action                                          │ Principal                                       │ Condition                                          │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ - │ {"Fn::Sub":"arn:aws:kms:${AWS::Region}:${AWS::A │ Allow  │ kms:Decrypt                                     │ AWS:${KinesisCloudwatchlogsToS3Role}            │ "StringEquals": {                                  │
│   │ ccountId}:key/%FIREHOSE_POLICY_TEMPLATE_PLACEHO │        │                                                 │                                                 │   "kms:ViaService": "{\"Fn::Sub\":\"kinesis.${AWS: │
│   │ LDER%"}                                         │        │                                                 │                                                 │ :Region}.amazonaws.com\"}"                         │
│   │                                                 │        │                                                 │                                                 │ },                                                 │
│   │                                                 │        │                                                 │                                                 │ "StringLike": {                                    │
│   │                                                 │        │                                                 │                                                 │   "kms:EncryptionContext:aws:kinesis:arn": "{\"Fn: │
│   │                                                 │        │                                                 │                                                 │ :Sub\":\"arn:aws:kinesis:${AWS::Region}:${AWS::Acc │
│   │                                                 │        │                                                 │                                                 │ ountId}:stream/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLD │
│   │                                                 │        │                                                 │                                                 │ ER%\"}"                                            │
│   │                                                 │        │                                                 │                                                 │ }                                                  │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Allow  │ s3:GetBucketAcl                                 │ Service:cloudtrail.amazonaws.com                │                                                    │
│   │ d}-vga-log-1"}                                  │        │                                                 │ Service:config.amazonaws.com                    │                                                    │
│   │                                                 │        │                                                 │ Service:delivery.logs.amazonaws.com             │                                                    │
│   │                                                 │        │                                                 │ Service:{"Fn::Sub":"logs.${AWS::Region}.amazona │                                                    │
│   │                                                 │        │                                                 │ ws.com"}                                        │                                                    │
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Allow  │ s3:GetBucketLocation                            │ Service:config.amazonaws.com                    │                                                    │
│   │ d}-vga-log-1"}                                  │        │ s3:ListBucket                                   │ Service:guardduty.amazonaws.com                 │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Deny   │ s3:*                                            │ *                                               │ "Bool": {                                          │
│   │ d}-vga-log-1"}                                  │        │                                                 │                                                 │   "aws:SecureTransport": false                     │
│   │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │        │                                                 │                                                 │ }                                                  │
│   │ d}-vga-log-1/*"}                                │        │                                                 │                                                 │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Allow  │ s3:PutObject                                    │ Service:cloudtrail.amazonaws.com                │ "StringEquals": {                                  │
│   │ d}-vga-log-1/*"}                                │        │                                                 │ Service:config.amazonaws.com                    │   "s3:x-amz-acl": "bucket-owner-full-control"      │
│   │                                                 │        │                                                 │ Service:delivery.logs.amazonaws.com             │ }                                                  │
│   │                                                 │        │                                                 │ Service:guardduty.amazonaws.com                 │                                                    │
│   │                                                 │        │                                                 │ Service:{"Fn::Sub":"logs.${AWS::Region}.amazona │                                                    │
│   │                                                 │        │                                                 │ ws.com"}                                        │                                                    │
│ - │ {"Fn::Sub":"arn:aws:s3:::${env}-${AWS::AccountI │ Allow  │ s3:PutObject                                    │ AWS:arn:aws:iam::127311923021:root              │                                                    │
│   │ d}-vga-log-1/*"}                                │        │                                                 │                                                 │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:kms:us-east-1:<AccountID>:key/%FIREHOS  │ Allow  │ kms:Decrypt                                     │ AWS:${KinesisCloudwatchlogsToS3Role}            │ "StringEquals": {                                  │
│   │ E_POLICY_TEMPLATE_PLACEHOLDER%                  │        │                                                 │                                                 │   "kms:ViaService": "kinesis.us-east-1.amazonaws.c │
│   │                                                 │        │                                                 │                                                 │ om"                                                │
│   │                                                 │        │                                                 │                                                 │ },                                                 │
│   │                                                 │        │                                                 │                                                 │ "StringLike": {                                    │
│   │                                                 │        │                                                 │                                                 │   "kms:EncryptionContext:aws:kinesis:arn": "arn:aw │
│   │                                                 │        │                                                 │                                                 │ s:kinesis:us-east-1:<AccountID> :stream/%FIREHOSE_ │
│   │                                                 │        │                                                 │                                                 │ POLICY_TEMPLATE_PLACEHOLDER%"                      │
│   │                                                 │        │                                                 │                                                 │ }                                                  │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1          │ Allow  │ s3:GetBucketAcl                                 │ Service:cloudtrail.amazonaws.com                │                                                    │
│   │                                                 │        │                                                 │ Service:config.amazonaws.com                    │                                                    │
│   │                                                 │        │                                                 │ Service:delivery.logs.amazonaws.com             │                                                    │
│   │                                                 │        │                                                 │ Service:logs.us-east-1.amazonaws.com            │                                                    │
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1          │ Allow  │ s3:GetBucketLocation                            │ Service:config.amazonaws.com                    │                                                    │
│   │                                                 │        │ s3:ListBucket                                   │ Service:guardduty.amazonaws.com                 │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1          │ Deny   │ s3:*                                            │ *                                               │ "Bool": {                                          │
│   │ arn:aws:s3:::dev-<AccountID>-vga-log-1/*        │        │                                                 │                                                 │   "aws:SecureTransport": false                     │
│   │                                                 │        │                                                 │                                                 │ }                                                  │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1/*        │ Allow  │ s3:PutObject                                    │ Service:cloudtrail.amazonaws.com                │ "StringEquals": {                                  │
│   │                                                 │        │                                                 │ Service:config.amazonaws.com                    │   "s3:x-amz-acl": "bucket-owner-full-control"      │
│   │                                                 │        │                                                 │ Service:delivery.logs.amazonaws.com             │ }                                                  │
│   │                                                 │        │                                                 │ Service:guardduty.amazonaws.com                 │                                                    │
│   │                                                 │        │                                                 │ Service:logs.us-east-1.amazonaws.com            │                                                    │
│ + │ arn:aws:s3:::dev-<AccountID>-vga-log-1/*        │ Allow  │ s3:PutObject                                    │ AWS:arn:aws:iam::127311923021:root              │                                                    │
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤
│ + │ arn:aws:s3:::dev-<AccountID>-vga-script-1       │ Deny   │ s3:*                                            │ *                                               │ "Bool": {                                          │
│   │ arn:aws:s3:::dev-<AccountID>-vga-script-1/*     │        │                                                 │                                                 │   "aws:SecureTransport": false                     │
│   │                                                 │        │                                                 │                                                 │ }                                                  │
└───┴─────────────────────────────────────────────────┴────────┴─────────────────────────────────────────────────┴─────────────────────────────────────────────────┴────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Do you wish to deploy these changes (y/n)? y
fintan-cdk: deploying... [1/1]
fintan-cdk: creating CloudFormation changeset...

 ✅  fintan-cdk

✨  Deployment time: 32.04s

Stack ARN:
arn:aws:cloudformation:us-east-1:<AccountID>:stack/fintan-cdk/cd34ca90-e018-11ee-a17a-0a41b9c91b2d

✨  Total time: 39.55s


root@f5bd1aaccbee:/infra/fintan/cdk/fintan-cdk# 

CloudFormationコンソールも確認してみます。

CDKMetadataが追加されています。

それ以外の定義もModifyとなっています。

各リソースにCDKのMetadataの情報が追加されているようでした。

各AWSリソースにMetadataというのが追加されていた変更のみで設定値には影響はありませんでした。

念のため、ツールで単体テストを行いましたが、問題なしで安心しました。

root@f5bd1aaccbee:/infra/fintan# rspec ./spec/*_spec.rb

cloudwatch_logs 'dev-kinesis-logs'
  is expected to exist
  :all_subscription_filter_filter
  retention_in_days
    is expected to eq 90

cloudwatch_logs 'dev-kinesis-logs'
  is expected to exist
  is expected to have log stream "dev-fintan-kinesis-error-logs-s3-deliverystream-error"
  :kinesis_parameter_value

iam_role 'kinesis-cloudwatchlogstos3-role'
  is expected to exist
  is expected to have inline policy "kinesis-cloudwatchlogstos3-policy"
  assume_role_policy_document
    is expected to eq {"Statement"=>[{"Action"=>"sts:AssumeRole", "Effect"=>"Allow", "Principal"=>{"Service"=>["firehose.amazonaws.com", "logs.us-east-1.amazonaws.com"]}}], "Version"=>"2012-10-17"}
  max_session_duration
    is expected to eq 3600
  role_inlinepolicy_docu
    is expected to eq {"Statement"=>[{"Action"=>["s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:Lis...s:kms:us-east-1:<AccountID>:key/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLDER%"}], "Version"=>"2012-10-17"}

s3_bucket 'dev-<AccountID>-vga-log-1'
  is expected to exist
  is expected to have server side encryption {:algorithm=>"AES256"}
  is expected to have policy "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AllowSSLRequestsOnly\",\"Effect\":\"Deny\",\"P...1:root\"},\"Action\":\"s3:PutObject\",\"Resource\":\"arn:aws:s3:::dev-<AccountID>-vga-log-1/*\"}]}"
  is expected to have versioning enabled
  is expected to have lifecycle rule {:expiration=>{:days=>3650}, :id=>"AWSLogs_delete_applog"}
  is expected to have lifecycle rule {:expiration=>{:days=>90}, :id=>"AWSLogs_delete_pdlog"}
  is expected to have lifecycle rule {:expiration=>{:days=>450}, :id=>"AWSLogs_delete_perflog"}
  is expected to have lifecycle rule {:id=>"NoncurrentVersionExpiration", :noncurrent_version_expiration=>{:noncurrent_days=>3650}}
  is expected to have tag "System" value "fintan"
  is expected to have tag "Name" value "dev-<AccountID>-vga-log-1"

s3_bucket 'dev-<AccountID>-vga-script-1'
  is expected to exist
  is expected to have server side encryption {:algorithm=>"AES256"}
  is expected to have policy "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AllowSSLRequestsOnly\",\"Effect\":\"Deny\",\"P...:dev-<AccountID>-vga-script-1/*\"],\"Condition\":{\"Bool\":{\"aws:SecureTransport\":\"false\"}}}]}"
  is expected to have versioning enabled
  is expected to have lifecycle rule {:id=>"NoncurrentVersionExpiration", :noncurrent_version_expiration=>{:noncurrent_days=>90}}
  is expected to have tag "System" value "fintan"
  is expected to have tag "Name" value "dev-<AccountID>-vga-script-1"

ssm_parameter '/dev/infra/fintan/CloudWatch/kinesis-logs/name'
  :ssm_parameter_value

ssm_parameter '/dev/infra/fintan/IAMRole/KinesisTransferCloudwatchlogstos3Role/arn'
  :ssm_parameter_value

ssm_parameter '/dev/infra/fintan/S3/VgaLog1Bucket/name'
  :ssm_parameter_value

ssm_parameter '/dev/infra/fintan/S3/VgaScript1Bucket/name'
  :ssm_parameter_value

Finished in 28.27 seconds (files took 3.45 seconds to load)
32 examples, 0 failures

root@f5bd1aaccbee:/infra/fintan# 

まとめ

既存のCloudFormationスタックをCloudFormationコードからCDKにcdk migrateで変換しました。

ParameterやConditionなど作りこみを行っているスタック程CDK移行は難しくなり、どんな修正が必要か少し見えたかと思います。

CloudFormationのFn::ForEachは対応していないことや CDK L1での変換になりますのでご注意ください。

cdk migrateコマンドは検証段階(2024/3/22時点)のためこれから機能が拡充していくことを期待しています。

 

CDK化のメリットとしてはバックエンドエンジニアやフロントエンジニアとも言語を合わせることができ、

YAMLファイルの手書きより少ない記述で、条件分岐(IF文)や繰り返し処理(FOR文)など

プログラミング言語の制御命令も使用することができるようになります。

ただし、L1だとコード量削減は期待できないため、今回移行したものを L1からL2に変換すること進めていく必要があります。

 

本記事がこれからCDK移行を考えている方の助けになれば幸いです。

追伸:今回エラーが出た部分はまだIssueにはなさそうだったので、修正依頼してみようと思います。

 

おまけ①

cdk-nagでセキュリティチェックを行うことも推奨されています。

チェックを行うために./bin/fintan-cdk.tsに以下のように定義しておきます。

※実行できない場合は npm install cdk-nagでインストールしましょう。

コンテキストを用いて実行するしないを判断させるようにしました。

fintan-cdk.ts
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import 'source-map-support/register';
import { FintanCdkStack } from '../lib/fintan-cdk-stack';
// cdk-nag import
import { AwsSolutionsChecks } from 'cdk-nag';
// 環境変数 
import { config as devProperties } from '../config/dev';
import { config as prdProperties } from '../config/prd';
import { config as stgProperties } from '../config/stg';

const app = new cdk.App();

// 環境変数 
const envPrefix = app.node.tryGetContext('envPrefix');
if (envPrefix == undefined)
throw new Error(`Please specify environment with context option. ex) cdk deploy -c envPrefix=dev [-c awsAccount=XXXXXXXX ] [-c awsRegion=us-east-1 ]`);
console.log(`envPrefix is "${envPrefix}".`)

//AWSアカウント設定
const awsAccount =
  app.node.tryGetContext('awsAccount') || process.env.CDK_DEFAULT_ACCOUNT
console.log(`awsAccount is "${awsAccount}".`)

//REGION設定
const awsRegion =
  app.node.tryGetContext('awsRegion') || process.env.CDK_DEFAULT_REGION
console.log(`awsRegion is "${awsRegion}".`)

//CDK-NAG実行
const cdkNag = 
  app.node.tryGetContext('cdkNag') 

// configファイルを読み込む
const properties = getProperties(envPrefix)

new FintanCdkStack(app, 'fintan-cdk', {
  /* If you don't specify 'env', this stack will be environment-agnostic.
   * Account/Region-dependent features and context lookups will not work,
   * but a single synthesized template can be deployed anywhere. */

  /* Uncomment the next line to specialize this stack for the AWS Account
   * and Region that are implied by the current CLI configuration. */
  env: { account: awsAccount, region: awsRegion },

  // configファイルを読み込む
  ...properties
  
  /* Uncomment the next line if you know exactly what Account and Region you
   * want to deploy the stack to. */
  // env: { account: '123456789012', region: 'us-east-1' },

  /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
});

//CDK-NAG実行(コンテキストでcdkNag=trueとした場合に実行)
if (cdkNag == "true") {
  cdk.Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
}

//環境変数
function getProperties(envKey: String) {
  if (envKey === "dev") {
    return devProperties
  } else if (envKey === "prd") {
    return prdProperties
  } else if (envKey === "stg") {
    return stgProperties
  }  else {
    throw new Error("No Support environment")
  }
}

◆実行例

-c cdkNag=trueを定義したときのみチェックするようにしました。

root@f5bd1aaccbee:/infra/git/fintan-cdk# cdk synth -c envPrefix=dev -c cdkNag=true

[Error at /fintan-cdk/KinesisCloudwatchlogsToS3Role] AwsSolutions-IAM5[Resource::*]: The IAM entity contains wildcard permissions and does not have a cdk-nag rule suppression with evidence for those permission. Metadata explaining the evidence (e.g. via supporting links) for wildcard permissions allows for transparency to operators. This is a granular rule that returns individual findings that can be suppressed with 'appliesTo'. The findings are in the format 'Action::' for policy actions and 'Resource::' for resources. Example: appliesTo: ['Action::s3:*'].

おまけ②

GitHub Actionsからデプロイ可能なようにしてみます。

過去に投稿した以下の記事同様に各AWSアカウント(本番/STG/開発環境)にそれぞれにデプロイできるようにGitHub workflowをCDK用に用意してみます。

GitHub Actionsを利用したAWS CloudFormationの安全なデプロイ方法

◆ポイント

cdk diffでチェックして問題なければcdk deployを行う方法でもよいのですが、

cdk diffの場合変更セットが消えてしまうので、履歴や差分をしっかり確認するためにあえてcdk deploy –no-executeで変更セットを作成して残し、

問題なければGitHub Actionsをリランすることでデプロイを行います。

◆デプロイイメージ

◆GitHub Workflowファイルの作成

.github/workflows/fintan-cdkdeploy.yamlファイル
name: Cfn Deploy fintan-cdk

on:
  push:
    branches:
      - develop
      - main
      - release
    paths:
      - .github/workflows/*.yaml
      - package.json
      - package-lock.json
      - tsconfig.json
      - cdk.json
      - script/**
      - lambda/**
      - bin/**
      - lib/**
      - config/**

env:
  AWS_ACCOUNT: ${{ secrets.AWS_ACCOUNT }}
  AWS_REGION_TKY: ${{ secrets.AWS_REGION_TKY }}
  AWS_REGION_OSK: ${{ secrets.AWS_REGION_OSK }}
  AWS_REGION_VGA: ${{ secrets.AWS_REGION_VGA }}
  ENV_PREFIX: ${{ secrets.ENV_PREFIX }}
  AWS_ROLE: ${{ secrets.AWS_ROLE }}
  STACK_NAME_NO1: fintan-cdk

# These permissions are needed to interact with GitHub's OIDC Token endpoint.
permissions:
  id-token: write
  contents: read
  packages: read

jobs:
  deploy:
    runs-on: >-
      ${{ 
        github.ref == 'refs/heads/main'         && fromJSON('[ "self-hosted", "prd" ]') ||
        github.ref == 'refs/heads/release'      && fromJSON('[ "self-hosted", "stg" ]') ||
        fromJSON('[ "self-hosted", "dev" ]')
      }}
    timeout-minutes: 40
    environment:
      name: >-
        ${{
          github.ref == 'refs/heads/main'         && 'production' ||
          github.ref == 'refs/heads/release'      && 'staging'    ||
          'development'
        }}
    steps:
      - name: GitHub Event Context Info
        run: |
          echo "github.event_name=${{github.event_name}}"
          echo "BRACHE : github.ref=${{github.ref}}"

      - name: Checkout
        uses: actions/checkout@v2

      - name: Destination settings
        env:
          TZ: "Asia/Tokyo"
        run: |
          echo "REPO_NAME=${GITHUB_REPOSITORY#${GITHUB_REPOSITORY_OWNER}/}" >> $GITHUB_ENV
          echo "CURRENT_DATETIME=$(date +'%Y%m%d-%H%M%S')"  >> $GITHUB_ENV
          echo "RUN_NO=${{github.run_attempt}}"             >> $GITHUB_ENV

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT }}:role/${{ secrets.AWS_ROLE }}
          aws-region: ${{ secrets.AWS_REGION_VGA }}
          
      - name: Install AWS CDK
        run: npm ci
      - name: Transpile
        run: npm run build
        
      ## ChangeSet No.1 ##
      # YAML_NAME_NO1 , STACK_NAME_NO1 , JSON_FILE_NO1 , CHANGESETID_NO1 , STATUS_NO1
      - name: CDK diff ${{env.STACK_NAME_NO1}}
        run: >
          cdk diff --no-change-set -c envPrefix=${ENV_PREFIX} -c awsAccount=${AWS_ACCOUNT} -c awsRegion=${AWS_REGION_VGA} 
          
      - name: Create Change Set ${{env.STACK_NAME_NO1}}
        run: >
          cdk deploy --no-execute -c envPrefix=${ENV_PREFIX} -c awsAccount=${AWS_ACCOUNT} -c awsRegion=${AWS_REGION_VGA}   

      - name: Change Set ID Get ${{env.STACK_NAME_NO1}}
        run: >
          echo "CHANGESETID_NO1=$(aws cloudformation list-change-sets --stack-name ${{env.STACK_NAME_NO1}} --output yaml | 
          grep changeSet/cdk-deploy-change-set | 
          awk '{print $3}')"  >> ${GITHUB_ENV}

      - name: Change Set Check ${{env.STACK_NAME_NO1}}
        run: |
          while true 
          do             
            sleep 1 
            STATUS=$(aws cloudformation describe-change-set --change-set-name ${{env.CHANGESETID_NO1}} --output text --query Status )
            STATUSREASON=$(aws cloudformation describe-change-set --change-set-name ${{env.CHANGESETID_NO1}} --output text --query StatusReason)
            STATUSREASONOK="The submitted information didn't contain changes. Submit different information to create a change set."
            STATUSREASONOK2="No updates are to be performed."
            # Changeset OK return 0 
            [ "${STATUS}" = "CREATE_COMPLETE" ] && echo "STATUS_NO1=${STATUS}"  >> ${GITHUB_ENV}
            [ "${STATUS}" = "CREATE_COMPLETE" ] && aws cloudformation describe-change-set --change-set-name ${{env.CHANGESETID_NO1}} --output table --query Changes && break
            # Not Change return 0
            [ "${STATUSREASON}" = "${STATUSREASONOK}" ]  && echo ${STATUSREASON} && break
            [ "${STATUSREASON}" = "${STATUSREASONOK2}" ] && echo ${STATUSREASON} && break
            # FAILED retrun 1
            [ "${STATUS}" = "FAILED" ] && echo ${STATUSREASON} && exit 1
            sleep 5 
          done

      - name: CDK Deploy ${{env.YAML_NAME_NO1}}
        if: env.RUN_NO != '1'  && env.STATUS_NO1 == 'CREATE_COMPLETE'
        run: cdk deploy -c envPrefix=${ENV_PREFIX} -c awsAccount=${AWS_ACCOUNT} -c awsRegion=${AWS_REGION_VGA}

      - name: Cfn Deploy Check ${{env.YAML_NAME_NO1}}
        if: env.RUN_NO != '1'  && env.STATUS_NO1 == 'CREATE_COMPLETE'
        run: aws cloudformation wait stack-update-complete --stack-name ${{env.STACK_NAME_NO1}}


◆GitHub Actionsデプロイ

./config/dev.tsのS3ライフサイクルポリシーの閾値を変えてGitHubにPushします。

expirationDaysApplog: 3650 ⇒  expirationDaysApplog: 365

・logExpirationDaysVersioning: 3650 ⇒ logExpirationDaysVersioning: 365

GitHub上にマージを行い、GitHub Actionsを実行します。

変更セットを作成し、問題なければリランを行うことで、安全にデプロイを行います。

◆反映確認

ツールで単体テストしてみましたが、想定通りの差分結果となりました。

expirationDaysApplog: 3650 ⇒  expirationDaysApplog: 365

・logExpirationDaysVersioning: 3650 ⇒ logExpirationDaysVersioning: 365

root@f5bd1aaccbee:/infra/fintan# rspec ./spec/*.rb

cloudwatch_logs 'dev-kinesis-logs'
  is expected to exist
  :all_subscription_filter_filter
  retention_in_days
    is expected to eq 90

cloudwatch_logs 'dev-kinesis-logs'
  is expected to exist
  is expected to have log stream "dev-fintan-kinesis-error-logs-s3-deliverystream-error"
  :kinesis_parameter_value

iam_role 'kinesis-cloudwatchlogstos3-role'
  is expected to exist
  is expected to have inline policy "kinesis-cloudwatchlogstos3-policy"
  assume_role_policy_document
    is expected to eq {"Statement"=>[{"Action"=>"sts:AssumeRole", "Effect"=>"Allow", "Principal"=>{"Service"=>["firehose.amazonaws.com", "logs.us-east-1.amazonaws.com"]}}], "Version"=>"2012-10-17"}
  max_session_duration
    is expected to eq 3600
  role_inlinepolicy_docu
    is expected to eq {"Statement"=>[{"Action"=>["s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:Lis...s:kms:us-east-1:938230760815:key/%FIREHOSE_POLICY_TEMPLATE_PLACEHOLDER%"}], "Version"=>"2012-10-17"}

s3_bucket 'dev-938230760815-vga-log-1'
  is expected to exist
  is expected to have server side encryption {:algorithm=>"AES256"}
  is expected to have policy "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AllowSSLRequestsOnly\",\"Effect\":\"Deny\",\"P...1:root\"},\"Action\":\"s3:PutObject\",\"Resource\":\"arn:aws:s3:::dev-938230760815-vga-log-1/*\"}]}"
  is expected to have versioning enabled
  is expected to have lifecycle rule {:expiration=>{:days=>3650}, :id=>"AWSLogs_delete_applog"} (FAILED - 1)
  is expected to have lifecycle rule {:expiration=>{:days=>90}, :id=>"AWSLogs_delete_pdlog"}
  is expected to have lifecycle rule {:expiration=>{:days=>450}, :id=>"AWSLogs_delete_perflog"}
  is expected to have lifecycle rule {:id=>"NoncurrentVersionExpiration", :noncurrent_version_expiration=>{:noncurrent_days=>3650}} (FAILED - 2)
  is expected to have tag "System" value "fintan"
  is expected to have tag "Name" value "dev-938230760815-vga-log-1"

s3_bucket 'dev-938230760815-vga-script-1'
  is expected to exist
  is expected to have server side encryption {:algorithm=>"AES256"}
  is expected to have policy "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AllowSSLRequestsOnly\",\"Effect\":\"Deny\",\"P...:dev-938230760815-vga-script-1/*\"],\"Condition\":{\"Bool\":{\"aws:SecureTransport\":\"false\"}}}]}"
  is expected to have versioning enabled
  is expected to have lifecycle rule {:id=>"NoncurrentVersionExpiration", :noncurrent_version_expiration=>{:noncurrent_days=>90}}
  is expected to have tag "System" value "fintan"
  is expected to have tag "Name" value "dev-938230760815-vga-script-1"

ssm_parameter '/dev/infra/fintan/CloudWatch/kinesis-logs/name'
  :ssm_parameter_value

ssm_parameter '/dev/infra/fintan/IAMRole/KinesisTransferCloudwatchlogstos3Role/arn'
  :ssm_parameter_value

ssm_parameter '/dev/infra/fintan/S3/VgaLog1Bucket/name'
  :ssm_parameter_value

ssm_parameter '/dev/infra/fintan/S3/VgaScript1Bucket/name'
  :ssm_parameter_value

Failures:

  1) s3_bucket 'dev-938230760815-vga-log-1' is expected to have lifecycle rule {:expiration=>{:days=>3650}, :id=>"AWSLogs_delete_applog"}
     Failure/Error: should have_lifecycle_rule(id: s3['S3LIFECYCLEID1'], expiration: {days: s3['S3LIFECYCLEEXPIREDAY1']})
       expected `s3_bucket 'dev-938230760815-vga-log-1'.has_lifecycle_rule?({:expiration=>{:days=>3650}, :id=>"AWSLogs_delete_applog"})` to be truthy, got false
     # ./spec/lvs-s3_spec.rb:68:in `block (4 levels) in '

  2) s3_bucket 'dev-938230760815-vga-log-1' is expected to have lifecycle rule {:id=>"NoncurrentVersionExpiration", :noncurrent_version_expiration=>{:noncurrent_days=>3650}}
     Failure/Error: should have_lifecycle_rule(id: s3['S3LIFECYCLEID4'], noncurrent_version_expiration: {noncurrent_days: s3['S3LIFECYCLENONCONCARENTDAY4']})
       expected `s3_bucket 'dev-938230760815-vga-log-1'.has_lifecycle_rule?({:id=>"NoncurrentVersionExpiration", :noncurrent_version_expiration=>{:noncurrent_days=>3650}})` to be truthy, got false
     # ./spec/lvs-s3_spec.rb:104:in `block (4 levels) in '

Finished in 35.57 seconds (files took 2.96 seconds to load)
32 examples, 2 failures

Failed examples:

rspec ./spec/lvs-s3_spec.rb[1:5] # s3_bucket 'dev-938230760815-vga-log-1' is expected to have lifecycle rule {:expiration=>{:days=>3650}, :id=>"AWSLogs_delete_applog"}
rspec ./spec/lvs-s3_spec.rb:98 # s3_bucket 'dev-938230760815-vga-log-1' is expected to have lifecycle rule {:id=>"NoncurrentVersionExpiration", :noncurrent_version_expiration=>{:noncurrent_days=>3650}}

root@f5bd1aaccbee:/infra/fintan# 

参考

CDK Migrate: AWS CDK への移行コマンドの発表 | Amazon Web Services ブログ
CDK アプリケーションの複雑さを軽減する L2 Construct の活用
AWS CDKで複数の環境を設定する際の設定値(config)の渡し方 | DevelopersIO (classmethod.jp)