※2025/07/11追記:一部間違いがありましたので、修正しました
今回のお話
いままでプレビュー版だったAWS CDK Toolkit Libraryが、5/30(金)についにGAされました。
そこで今回は、AWS CDK Toolkit Libraryについて2回に分けて紹介したいと思います。
参考サイト
- Perform programmatic actions using the CDK Toolkit Library
- AWS CDK Toolkit Library公式ドキュメント
- Build Custom CLI's, Deployment Automation, and more with the AWS CDK Toolkit Library
- Adam Keller氏によるAWS CDK Toolkit Libraryの紹介ブログ
AWS CDK Toolkit Libraryとは?
今までCDK CLIで行っていたCDKの各種機能(cdk synth, cdk deployなど)を、(CLI経由ではなく)アプリケーション同様プログラムコードを介して実行可能なライブラリ。
現在はNode.jsにのみ対応しています。
アプリケーション的に実行できると何がいいの?(AWS CDK Toolkit Libraryの特徴・メリット)
インフラライフサイクルの細かいハンドリング&CDKソースとの統合
synth,deployなどをアプリ的に実行することで、成功・失敗時の挙動やエラーハンドリングなどをコードで細かく制御できます。- それにより、進行状況を監視し、インジケータで表示したり細かいログ制御を行ったり...といったカスタマイズも可能です。
- CDKをインフラライフサイクルのワークフローに統合し、組織やチームで専用のアプリを構築する、といったことも可能になります。
インテグレーションテスト
- AWS SDKを用いたインテグレーションテスト(Lambdaなどを実際にAWSにデプロイしたあとにAWS上のLambdaを実行し、インフラとアプリロジックが正しいことを確認するテスト)を比較的容易に実行することができます。*1
一時的な環境の動作検証
PoCやお試し用などで作成した一時的な環境の動作検証・破棄をCLIを使うことなく容易に実現可能になります。
「コードベース」による恩恵
アプリケーション的にコードベースの記載になるため、下記のようなコードの機能による恩恵(?)を受けることができます。
- TypeScriptによる型の恩恵(構成やコンパイルの問題早期など)を受けられる
- 命名規則、セキュリティ ベースライン、コーディングルールなどを一元管理化し、コードレベルで自動的に適用可能(各種Linter, cdk-nag, zodなど)
- 詳細に構造化されたエラー処理機構を実装可能
- AWS CDK Toolkit Libraryには独自のエラーハンドリング機能が用意されています。(次回紹介します)
早速触ってみる
では、早速AWS CDK Toolkit LibraryでAWS CDKの一通りの機能を実行してみましょう。
下記サンプルコード*2をベースに説明します。
import { Toolkit, StackSelectionStrategy, AssemblyError } from '@aws-cdk/toolkit-lib'; import { App } from 'aws-cdk-lib'; import { AwsCdkToolkitLibraryDemoStack } from './lib/aws-cdk-toolkit-library-demo-stack'; import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda'; const NO_ERROR = 0; const WITH_ERROR = 1; // クラウドアセンブリ作成関数 async function createCloudAssembly(toolkit: Toolkit, stackName: string) { console.info('Creating CloudAssebmly...'); const assembly = await toolkit.fromAssemblyBuilder(async () => { const app = new App(); new AwsCdkToolkitLibraryDemoStack(app, stackName); return app.synth(); }); console.info('Creating Successful'); return assembly; } // デプロイ関数 async function deployStack(toolkit: Toolkit, cloudAssembly: any, stackName: string) { console.info(`Deploying stack: ${stackName}...`); const result = await toolkit.deploy(cloudAssembly, { stacks: { strategy: StackSelectionStrategy.PATTERN_MUST_MATCH, patterns: [stackName], }, }); console.info('Deployment successful.'); const stack = result.stacks.find((s) => s.stackName === stackName); if (!stack) { throw new Error(`Stack ${stackName} not found after deployment`); } return stack; } // インテグレーションテスト実行関数(必要に応じて実行) async function invokeLambda(functionArn: string, payload: any): Promise<void> { console.info('Invoking Lambda with payload...'); const lambdaClient = new LambdaClient({}); const invokeCommand = new InvokeCommand({ FunctionName: functionArn, Payload: Buffer.from(JSON.stringify(payload)), }); const response = await lambdaClient.send(invokeCommand); const responseData = JSON.parse(Buffer.from(response.Payload ?? []).toString()); if (responseData.statusCode && responseData.statusCode === 200) { console.info('Invoke Lambda passed'); } else { console.error('Invoke Lambda failed'); console.error(responseData); } return; } // スタック削除関数(必要に応じて) async function destroyStack(toolkit: Toolkit, cloudAssembly: any, stackName: string) { console.info(`Destroying stack: ${stackName}...`); try { await toolkit.destroy(cloudAssembly, { stacks: { strategy: StackSelectionStrategy.PATTERN_MUST_MATCH, patterns: [stackName], }, }); console.info('Stack destroyed successfully.'); } catch (error) { console.error('Error during stack destruction:'); throw error; } } // メイン関数 async function main() { // Toolkitインスタンス作成 const toolkit = new Toolkit(); const stackName = 'AwsCdkToolkitLibraryDemoStack'; let cloudAssembly; // ここはコードを省略しているが、インテグレーションテストやスタック削除は // フラグを設定し、フラグがtrueの場合のみ実行する const isIntegTest = ... const isDestroyStack = ... try { // クラウドアセンブリ作成 cloudAssembly = await createCloudAssembly(toolkit, stackName); // デプロイ const stack = await deployStack(toolkit, cloudAssembly, stackName); // インテグレーションテスト if (isIntegTest) { // Lambda関数のARNをCFnの「出力」から取得。 // ただしLambda関数のARNのフォーマットは固定なので、 // わざわざCFnの「出力」から取得しなくてもOK const functionArn = stack.outputs['functionArn']; await invokeLambda(functionArn, { payload: 'test-failed' }); } // スタック削除 if (isDestroyStack) { await destroyStack(toolkit, cloudAssembly, stackName); } process.exit(NO_ERROR); } catch (error) { console.error('Error during this harness:'); console.error(error); process.exit(WITH_ERROR); } } // メイン処理呼び出し main();
AWS CDK Toolkit Libraryのインストール
下記コマンドを実行するだけです。
npm install @aws-sdk/toolkit-lib
AWS CDK Toolkit Library以外のAWS CDKの環境構築の手順は、今までと同じです。(一応簡単なコマンドを下に書いておきます)
※もちろんAdam Keller氏のブログに書いてある手順でもOKです
# とりあえずプロジェクトフォルダ作って mkdir aws-cdk-toolkit-library-demo cd aws-cdk-toolkit-library-demo # npx cdk initすればそれっぽい構成ができる # フォルダが空じゃないとエラーになる npx cdk init --language typescript # あとはnpm install すればCDKに最低限必要なものはすべてインストールされる npm install # その他AWS SDK JapaScript v3やLambdaの型定義など、必要なものをインストールする npm i @aws-sdk/client-lambda npm i -D @types/aws-lambda
StackおよびLambda関数の定義
まずはAWS CDKでStack、およびLambda関数を定義します。(ここも今までと同じです。確認したい場合は下記を参考)
// aws-cdk-toolkit-library-demo/lib/aws-cdk-toolkit-library-demo-stack.ts // ↑のファイル名は適宜置き換えてください。 // Stack定義 import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { LoggingFormat } from 'aws-cdk-lib/aws-lambda'; import { RetentionDays } from 'aws-cdk-lib/aws-logs'; import path from 'node:path'; export class AwsCdkToolkitLibraryDemoStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // 「entry」にpath.resolveで絶対パス指定しているのがポイント const demoFunc = new NodejsFunction(this, 'DemoFunction', { entry: path.resolve(__dirname, '../lambda/index.ts'), functionName: 'CdkToolkitDemoFunction', runtime: cdk.aws_lambda.Runtime.NODEJS_22_X, timeout: cdk.Duration.seconds(30), handler: 'handler', loggingFormat: LoggingFormat.JSON, logRetention: RetentionDays.ONE_WEEK, }); demoFunc.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE); // CFnの「出力」にLambda関数のARNを出力しておくのもポイント new cdk.CfnOutput(this, 'functionArn', { value: demoFunc.functionArn, }); } }
// aws-cdk-toolkit-library-demo/lambda/index.ts // ↑のファイル名は適宜置き換えてください。 // Lambda関数定義 import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; // 見てわかる通り、お試し用Lambdaです。 export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => { return { statusCode: 200, body: 'OK', }; };
AWS CDK Toolkit Libraryの挙動定義
ではここから、AWS CDK Toolkit Libraryの挙動を定義するファイルを作成します。
「aws-cdk-toolkit-library-demo」フォルダ直下に「aws-cdk-toolkit-library.ts」というファイルを作成してください。
なおAWS CDK Toolkit Libraryでの(最低限必要な)処理の流れは、以下の通りになります。
1. Toolkitインスタンス作成
new Toolkit()するだけです。
またこの時、デプロイ時などに使用するAWSプロファイル情報を指定することもできます。(下記ソース参照)
※2025/07/11追記:間違いがありましたので、修正しました
import { Toolkit, BaseCredentials } from '@aws-cdk/toolkit-lib'; // 普通にインスタンス作成する場合はこれでOK const toolkit = new Toolkit(); // 下記のように指定することで、AWSプロファイル情報を指定可能 // 注:訂正前のコード。これは動作しません。 // const toolkit = new Toolkit({ // sdkConfig: { profile: "xxxxx" }, // }); // 正しいコードはこちらになります。失礼しました。 const toolkit = new Toolkit({ sdkConfig: { baseCredentials: BaseCredentials.awsCliCompatible({ profile: 'xxxxx' }), }, });
2. クラウドアセンブリ作成
「クラウドアセンブリ」はCDKソースからCloudFormationテンプレートを作成ためのもので*3 、Toolkitクラスの以下3つのメソッドから作成できます。
| メソッド | 説明 | 具体的なソース |
|---|---|---|
| fromAssemblyBuilder | 引数に指定したインラインソースを使用して作成する | インラインソースで直にスタック作成しても、importしたスタックを使用しても良い |
| fromCdkApp | 既存のCDKアプリケーション(app)から作成する | 例:app.tsなど |
| fromAssemblyDirectory | すでにcdk synthなどで作成されたクラウドアセンブリがあるディレクトリを参照して作成する | 例:「cdk.out」など |
// 注:toolkitインスタンスは作成済の前提 // fromAssemblyBuilderの例 import { App } from 'aws-cdk-lib'; import { AwsCdkToolkitLibraryDemoStack } from './lib/aws-cdk-toolkit-library-demo-stack'; const assembly = await toolkit.fromAssemblyBuilder(async () => { const app = new App(); // スタックはCDKからimportしても、ここでclass定義をインラインで書いても良い new AwsCdkToolkitLibraryDemoStack(app, 'AwsCdkToolkitLibraryDemoStack'); return app.synth(); }); // fromCdkAppの例 const assembly = await toolkit.fromCdkApp("ts-node app.ts"); // fromAssemblyDirectoryの例 const assembly = await toolkit.fromAssemblyDirectory("cdk.out");
3. デプロイ
Toolkitクラスの deploy(assembly) メソッドを実施すればOKです。
※第1引数のクラウドアセンブリは必須です。(これはほぼすべてのToolkitクラスのメソッドに当てはまります)
また第2引数で、デプロイするスタックの指定やパラメータ・デプロイメソッドなどを指定可能です(この辺の詳細は次回触れます)
import { StackSelectionStrategy } from '@aws-cdk/toolkit-lib'; // toolkit.deployでデプロイを実行する。 // 第2引数に下記の指定することで、デプロイするスタックを指定できる。 // (未指定の場合、全スタックがデプロイされる) const stackName = 'xxxx'; const result = await toolkit.deploy(assembly , { stacks: { strategy: StackSelectionStrategy.PATTERN_MUST_MATCH, patterns: [stackName], }, }); // deploy()メソッドの戻り値から、デプロイしたスタックを取得可能。 // 後でインテグレーションテストなどを実施する場合に使用する const stack = result.stacks.find((s) => s.stackName === stackName); if (!stack) { throw new Error(`Stack ${stackName} not found after deployment`); } return stack;
その他の処理の実行
その他の処理として、下記の3つを説明します
- Lambda実行(インテグレーションテスト)
- 手動ロールバック
- スタック削除
Lambda実行(インテグレーションテスト)
AWS CDK Toolkit Library自体にはインテグレーションテストの機能はありませんが、アプリケーション統合したことでインテグレーションテストがやりやすくなっています。
インテグレーションテスト自体は、ソース内部でAWS SDKを使用して実施します。
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda'; const payload = { payload: 'test-failed', }; const lambdaClient = new LambdaClient({}); const invokeCommand = new InvokeCommand({ // stackは「デプロイ」処理で取得した「stack」定数 FunctionName: stack.outputs['functionArn'];, Payload: Buffer.from(JSON.stringify(payload)), }); const response = await lambdaClient.send(invokeCommand); const responseData = JSON.parse(Buffer.from(response.Payload ?? []).toString()); if (responseData.statusCode && responseData.statusCode === 200) { console.info('Invoke Lambda passed'); } else { console.error('Invoke Lambda failed'); console.error(responseData); } return;
手動ロールバック&スタック削除
手動ロールバック、およびスタック削除はそれぞれToolkitクラスの rollback(assembly) および destroy(assembly) メソッドで実施できます。
この2つは使い方が似ているので、まとめて下記ソースを参照してください。
なお、これらの注意点は下記の通りです。
- 基本的にロールバックはデプロイが失敗した際、自動で実施されます。この自動ロールバックが失敗して手動でロールバックしなければならない際に
toolkit.rollback()を実施してください。 - CDK CLIと違い、
toolkit.destroy()を実行すると 確認メッセージを表示することなくダイレクトにスタックを削除します。実行には十分注意してください。
import { StackSelectionStrategy } from '@aws-cdk/toolkit-lib'; const stackName = 'xxxx'; // 下記の通り、どちらも第二引数でdeploy()と同じオブジェクトを // 指定することで、対象のスタックを指定可能 // 手動ロールバック await toolkit.rollback(assembly , { stacks: { strategy: StackSelectionStrategy.PATTERN_MUST_MATCH, patterns: [stackName], }, }); // スタック削除 await toolkit.destroy(assembly , { stacks: { strategy: StackSelectionStrategy.PATTERN_MUST_MATCH, patterns: [stackName], }, });
その他
今回は触れませんでしたが、CDK CLIにあった下記コマンドもAWS CDK Toolkit Libraryのメソッドとして用意されていますので、気になる方は一度実行してみてください。
- bootstrap
- list
- diff
- drift
- watch など
まとめ
以上、AWS CDK Toolkit Libraryの概要でした。
まだGAされたばかりで、よく内容がわからないかもしれませんが、ちょっと使用した限り、今のところ大きな不便もなく問題なく使えています。
またアプリ統合されたことでいろいろ細かい制御やコードベースならではの恩恵を受けられる点は便利なので、今後もしばらくはAWS CDK Toolkit Libraryを使っていこうと思っています。
宣伝
昨年開催された「AWS CDK Conference Japan」が、今年も開催されます。
昨年同様、今年も有識者の発表に加え、下記のようなワークショップが開催されますので、気になった方はぜひ参加しましょう。
- 【初心者ワークショップ】AWS CDK Conference Japan 2025
- 【CDKコントリビュートワークショップ】AWS CDK Conference Japan 2025
- 【Amplifyワークショップ】AWS CDK Conference Japan 2025
また、私も現在CFPを1つ提出しておりますので、よろしければよろしくお願いします。
なお、AWS CDK Conference Japan 2025の1週間前の7/5(土)には、愛知県豊橋市で「JAWSミート2025」が今年も開催されます。
こちらもぜひ、皆さん参加しましょう!
それでは、今回はこの辺で。