はじめに
CDK は cdk synth が通っても、cdk deploy で CloudFormation 側が失敗することがあります。
最近の CDK CLI には、この失敗を手前で見つけたり、原因を調べたりするコマンド(lsp / validate / diagnose)が追加されています。既存の drift と合わせて試します。
| コマンド | できること | 使う場面 |
|---|---|---|
cdk lsp |
エディタに CDK の違反を波線で出す | コードを書いている最中 |
cdk validate |
デプロイせずに、デプロイ前の検証だけを行う | commit / PR の前 |
cdk drift |
手作業で変更されたリソースを見つける | PR の CI |
cdk diagnose |
失敗したデプロイの原因を後から表示する | デプロイ失敗後 |
この記事では、4 つを手順どおりに 1 つずつ実行して確かめます。
やってみた
前提
- CDK CLI 2.1143.0(
cdk lspは 2.1132.0 以上が必要)、aws-cdk-lib 2.271.0、cdk-nag 3.0.2 - ap-northeast-1(東京リージョン)
-
cdk bootstrap済み -
validateとdiagnoseは--unstableフラグが必要です(diagnoseは CDK CLI の README に記載。validateは 2.1143.0 で付けないとエラーになります)
手順 0:プロジェクトを作る
バケットとキューを 1 つずつ持つスタックを作り、合成時のポリシー検証に cdk-nag を登録します。
npm install -g aws-cdk@latest
mkdir cdk-verify && cd cdk-verify
cdk init app --language typescript
npm install cdk-nag@3
ちなみに、cdk-nag v3 については過去に以下の記事も書いていますので参考に。
bin/cdk-verify.ts と lib/cdk-verify-stack.ts を、次の内容で上書きします。
bin/cdk-verify.ts
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib/core';
import { AwsSolutionsChecks } from 'cdk-nag';
import { CdkVerifyStack } from '../lib/cdk-verify-stack';
const app = new cdk.App();
// cdk-nag v3 を合成時のポリシー検証プラグインとして登録
cdk.Validations.of(app).addPlugins(new AwsSolutionsChecks(app));
new CdkVerifyStack(app, 'CdkVerifyStack', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});
lib/cdk-verify-stack.ts
import * as cdk from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as sqs from 'aws-cdk-lib/aws-sqs';
export class CdkVerifyStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.Bucket(this, 'DataBucket', {
enforceSSL: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
// アクセスログ未設定(AwsSolutions-S1)は検証用なので許容
cdk.Validations.of(bucket).acknowledge({ id: 'AwsSolutions-S1', reason: '検証用' });
const queue = new sqs.Queue(this, 'JobQueue', {
visibilityTimeout: cdk.Duration.seconds(300),
enforceSSL: true,
});
// DLQ 未設定(AwsSolutions-SQS3)は検証用なので許容
cdk.Validations.of(queue).acknowledge({ id: 'AwsSolutions-SQS3', reason: '検証用' });
}
}
手順 1:cdk validate(オフライン)
--no-online を付けると、cdk-nag などのポリシー検証だけを行います。
わざと違反を作るため、11 行目のバケットの enforceSSL をコメントアウトして実行します。
const bucket = new s3.Bucket(this, 'DataBucket', {
// enforceSSL: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
npx cdk --unstable=validate validate --no-online
lib/cdk-verify-stack.ts:11:20 のように、違反のあるリソースを作っている行(new s3.Bucket()が表示されます。
確認できたら、コメントアウトを戻します。
手順 2:cdk lsp(VS Code に波線を出す)
cdk lsp は、synth の結果(cdk.out)にある検証レポートを、エディタのソース行に波線として返す Language Server です(cdk-explorer の README)。
公式のエディタ拡張はまだないため、README の案内どおり汎用拡張「Generic LSP Client」でつなぎます。
この方法で使えるのは波線とホバーだけで、synth はターミナルで行います。
拡張を入れます(code コマンドが無い場合は、VS Code のコマンドパレットで「Shell Command: Install 'code' command in PATH」を実行)。
code --install-extension zsol.vscode-glspc
コマンドパレット →「Preferences: Open User Settings (JSON)」に追記します(README の指示どおり、ワークスペース設定ではなくユーザー設定に書きます)。
{
"glspc.server.command": "cdk",
"glspc.server.commandArguments": ["lsp"],
"glspc.server.languageId": ["typescript", "python"]
}
手順 1 と同じく enforceSSL をコメントアウトして synth し、ターミナルから VS Code を開きます。synth は cdk-nag のエラーを表示して終わりますが、cdk.out に検証レポートは出力されます。
npx cdk synth -q
code .
lib/cdk-verify-stack.ts の 11 行目 new s3.Bucket( に波線が付き、ホバーすると cdk-nag の指摘が表示されます。
確認できたら、コメントアウトを戻します。
手順 3:cdk validate(オンライン)
オプションなし(--online が既定)で実行すると、テンプレートを CloudFormation のデプロイ前検証に送ります。
CloudFormation は、プロパティの構文やリソース名の衝突などをデプロイ前に検証します(CloudFormation のドキュメント)。
AWS の認証情報と bootstrap が必要です。
名前の衝突を起こすため、先に同じ名前のバケットを作ります。
ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
aws s3api create-bucket --bucket cdk-verify-existing-$ACCOUNT \
--create-bucket-configuration LocationConstraint=ap-northeast-1
バケットに bucketName を 1 行追加して、実行します。
const bucket = new s3.Bucket(this, 'DataBucket', {
bucketName: `cdk-verify-existing-${this.account}`,
enforceSSL: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
npx cdk --unstable=validate validate
FATAL ... already exists と NAME_CONFLICT_VALIDATION_VALIDATION_ERROR が、ソースの行付きで表示されます。 デプロイはされないので、スタックは作られません。確認できたら、追加した bucketName の行を消し、バケットを削除します。
aws s3 rb s3://cdk-verify-existing-$ACCOUNT
手順 4:cdk drift
一度デプロイし、手作業での変更を想定して、キューの可視性タイムアウトを CLI で 300 秒 → 60 秒に変えます。
npx cdk deploy --require-approval never
Q=$(aws cloudformation describe-stack-resources --stack-name CdkVerifyStack \
--query "StackResources[?ResourceType=='AWS::SQS::Queue'].PhysicalResourceId" --output text)
aws sqs set-queue-attributes --queue-url "$Q" --attributes VisibilityTimeout=60
npx cdk drift --fail
/VisibilityTimeout が 300 → 60 と表示され、--fail を付けているので exit code 1 で終了します。
CloudFormation のドリフト検出は、テンプレートで明示的に設定したプロパティだけが対象です(ドリフト検出のドキュメント)。
2 unchecked は、ドリフト検出に対応していないバケットポリシーとキューポリシーです(--verbose で確認できます)。
手順 5:cdk diagnose
デプロイ時に失敗するリソースとして、存在しないマネージドポリシーを付けた IAM ロールを追加します。
4 行目(sqs の import)の下に import を 1 行、コンストラクタの最後にロールを追加します。
import * as iam from 'aws-cdk-lib/aws-iam';
// 存在しないマネージドポリシーを付けたロール(デプロイ時に失敗する)
const role = new iam.Role(this, 'BrokenRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('ThisPolicyDoesNotExist')],
});
// AWS マネージドポリシーの使用(AwsSolutions-IAM4)は検証用なので許容
cdk.Validations.of(role).acknowledge({
id: 'AwsSolutions-IAM4[Policy::arn:<AWS::Partition>:iam::aws:policy/ThisPolicyDoesNotExist]',
reason: '検証用',
});
このロールはオンラインの validate では検出されず(Validation did not find any problems.)、デプロイで失敗します。
npx cdk --unstable=validate validate
npx cdk deploy --require-approval never
npx cdk --unstable=diagnose diagnose
diagnose で、失敗したリソース(BrokenRole)、原因(does not exist or is not attachable)、ソースの行(cdk-verify-stack.ts:26:18)が表示されます。
README によると、cdk deploy が失敗時に行う診断を後から実行するコマンドで、CI/CD でデプロイしている場合などに使えます。
手順 6:後片付け
npx cdk destroy --force
開発フローのどこで回すか
| タイミング | 回すもの | 理由 |
|---|---|---|
| ファイル編集 | cdk lsp |
エディタ上で違反の行が分かる |
git commit |
cdk validate --no-online |
AWS の認証情報なしで回せる |
| PR の CI |
cdk validate / cdk drift --fail
|
AWS 上の状態と突き合わせる。exit code でゲートにできる |
| デプロイ失敗時 | cdk diagnose |
失敗の原因を後から確認できる |
まとめ
-
cdk validateのオンライン検証で、名前の衝突のような「デプロイしないと分からない」失敗をデプロイ前に検出できました - ただし存在しないポリシーのように検出できないものもあるので、
driftやdiagnoseと組み合わせるのがよさそうです




