投稿日
AWS CloudFormationからCDKへの道(cdk migration)
もくじ
はじめに
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コード
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~]の部分を解除します。
#!/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」に変更します。
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
./config/stg.ts
②-3.パラメータをCDKに読み込ませる
./bin/fintan-cdk.tsに②で記載した各種ファイルを読み込ませる定義を入れます。
今回はコンテキストを利用して、cdk コマンドの引数で環境を変更できるように改良してみます。
追加した箇所はコメント分を確認ください。
#!/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で作っていた部分がうまく変換されませんでした。
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として修正します。
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すると消えてしまうようです。
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ファイル
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ファイルの作成
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)
