※このブログは、2025/09/08にこちらで公開した内容のQiita版になります。
今回のお話
前々回および前回の記事で、AWS CDK Toolkit Libraryの基本、および「CDK CLIで行っていたあの機能、AWS CDK Toolkit Libraryではどうやるの?」という事を書きました。
最終回となる今回は、エラーハンドリング、そしてちょっと高度な「IIoHost」について紹介しようと思います。
※AWS CDK Toolkit LibraryのAWS公式サイトはこちら。
エラーハンドリング
CDK Toolkit Libraryには、ToolkitError
クラスにあらかじめエラーハンドリング用のヘルパー関数が用意されており、 容易にエラーハンドリング処理を実装できます。
具体的には以下の通りです。(すべてstatic関数です)1
ヘルパー関数 | 説明 | 備考 |
---|---|---|
isToolkitError(error) | CDK Toolkitライブラリのエラーかどうかを判定する | 以下エラーの親クラス |
isAuthenticationError(error) | クレデンシャル違いなど、認証に関するエラーかどうかを判定する | |
isAssemblyError(error) | CDKの定義間違いなので発生したエラーかどうかを判定する。 | |
isContextProviderError(error) | ContextProvider(≒cintext)に起因するエラーかどうか判定する | ContextProviderは、synth 時のcdk.context.json出力などに関係するクラス。 |
withCause(message, error) | errorをcause(エラーの根本原因)としてオリジナルエラーをToolkitErrorとして返す | これはエラー種類の判定ではない |
下記がサンプルコードになります(一部AWS公式サイトより)
下記サンプルコードの通りToolkitErrorは、コンストラクタなどから自分で生成することもできます。
ちなみにCDK Toolkit Libraryのエラーはエラータイプのinstanceof
チェックに頼らず、ヘルパー関数を使用する事がAWS公式より案内されています。
import { ToolkitError } from '@aws-cdk/toolkit-lib';
try {
// なんか処理
await toolkit.deploy(cloudAssemblySource, {
stacks: { strategy: StackSelectionStrategy.ALL_STACKS }
});
} catch (error) {
if (ToolkitError.isAuthenticationError(error)) {
// 認証エラー(クレデンシャル不一致など)
console.error('Authentication failed. Check your AWS credentials.');
} else if (ToolkitError.isAssemblyError(error)) {
// CDKの定義が違う
console.error('CDK app error:', error.message);
} else if (ToolkitError.isToolkitError(error)) {
// context周りでエラー
console.error('CDK Toolkit error:', error.message);
} else if (ToolkitError.isToolkitError(error)) {
// 上記に該当しないToolkitErrorのエラー
console.error('CDK Toolkit error:', error.message);
// withCauseで、cause付きのToolkitErrorを取得する
throw ToolkitError.withCause(error.message, error);
} else {
// ToolkitErrorではない、予期しないエラー
console.error('Unexpected error:', error);
// こんな感じで、ToolkitErrorエラーの作成もできる
throw new ToolkitError('予期しないエラー', 'unknown', error)
}
}
IIoHostによるメッセージとインタラクション
CDK Toolkit Libraryではオペレーションを実行時、下記「メッセージ」および「リクエスト」という2つの主要なメカニズムを介して通信します。
- メッセージ:オペレーションの実行状況などを通知する。(例:「デプロイの進行中」など)
- リクエスト:ユーザーからの入力または確認が必要な決定ポイントで、これらを確認する機会を提供する。(例:「このスタックをデプロイしていいですか?」など)
これらを構成するのに必要なのが IIoHost
インターフェースです
IIoHostインターフェースについて
IIoHost
インターフェースは、以下2つの構成で成り立っています。
-
notify
:上記「メッセージ」を処理します。 -
requestResponse
:上記「リクエスト」を処理します。
ソースコードでは、下記の内容になっています。
import type { IoMessage, IoRequest } from './io-message';
export interface IIoHost {
notify(msg: IoMessage<unknown>): Promise<void>;
requestResponse<T>(msg: IoRequest<unknown, T>): Promise<T>;
}
またCDK Toolkit Libraryのコンストラクタでは、以下のようにIIoHost
を定義します。
const toolkit = new Toolkit({
ioHost: {
notify: async function (msg) {...(なんか処理)},
requestResponse: async function (msg) {...(なんか処理)},
}
});
notifyについて
notify
には、IoMessage
型の引数msg
の内容に応じたメッセージ処理を記載します。
AWS公式のサンプルでは下記のように msg.level
に応じたログを出力する処理が記載されており、アプリのログ出力とも似通った分かりやすい内容となっています。
notify: async function (msg) {
// notify関数の例
switch (msg.level) {
case 'error':
console.error(`[${msg.time}] ERROR: ${msg.message}`);
break;
case 'warning':
console.warn(`[${msg.time}] WARNING: ${msg.message}`);
break;
case 'info':
console.info(`[${msg.time}] INFO: ${msg.message}`);
break;
case 'debug':
console.debug(`[${msg.time}] DEBUG: ${msg.message}`);
break;
case 'trace':
console.debug(`[${msg.time}] TRACE: ${msg.message}`);
break;
default:
console.log(`[${msg.time}] ${msg.level}: ${msg.message}`);
}
},
ちなみにmsg
にはlevel
の他にも、以下のようなプロパティがあります。
プロパティ | 説明 | 備考 |
---|---|---|
time | 日時情報(Date型) | |
action | 実行した処理(deploy, synth, listなど) | |
code | メッセージコード | |
message | メッセージ本文 | |
span | メッセージスパン | 同じ操作に意味的に関連した複数メッセージのグループ。それ以外では無意味 |
data | メッセージに添付されたデータ |
requestResponseについて
requestResponse
は、ユーザーからの入力または確認が必要な場合に送信されるリクエストに対する挙動(=レスポンス処理)を記載します。
なおこのrequestResponse
は、CDK Toolkit Library において非常に重要です。
なぜならCDK Toolkit Libraryでは、このような処理に対する初期値が「何も確認しない」になっているからです。(CDK CLIで言えば、--require-approval
の 初期値が never
になっているのと同じ)
そのため、例えば toolkit.destroy
(=スタックの削除)のような危険な処理もユーザーに確認することなく、ダイレクトで実行されてしまいます。
そこで、これを防ぐために requestResponse
でちゃんと確認するように設定する必要があります。
サンプルとしては以下です。(AWS公式より)
requestResponse: async (msg) => {
// (例)本番だけは何も確認しない場合
if (environment === 'production') {
console.log(`Auto-approving for production: ${msg.message}`);
// msg.defaultResponseは「デフォルトの挙動を行う(=何も確認しない)」ための専用のプロパティ
return msg.defaultResponse;
}
// 何かしら確認のためのプロンプトなどを出す処理を行う
// ちなみに引数のmsgは、defaultResponseプロパティを持つ事以外notifyのmsgと同じです。
return showPromptForApproval(msg.message);
}
なおこのIIoHost
ですが、下記のいずれかの方法で定義できます。
-
Toolkit
コンストラクタにnotify
やrequestResponse
を直で定義する -
NonInteractiveIoHost
クラスを継承したカスタムクラスを自分で定義し、それをToolkit
コンストラクタのIIoHost
に設定する
ちなみに後者を使用すると「変更する必要がある動作のみをカスタマイズしながら、既存の実装を活用できる」というメリットがあります。
これはAWS公式にサンプルコードがありますので、そちらを参照ください。
まとめ
以上、AWS CDK Toolkit LibraryのエラーハンドリングとIIoHost
についての設定についてでした。
今日の内容、特にIIoHost
のRequestResponse
はちょっと難しい内容かもしれませんが、RequestResponse
は非常に重要な設定なので、必ず最初に設定しておいた方が良いと思います。
ちなみに今回RequestResponse
の部分について説明が駆け足になってしまいましたが、詳しく知りたい方は、山梨 蓮さん(X:@ren_yamanashi)がブログで非常に詳しく説明してくれていますので、ぜひそちらもご参照ください。
最後に
9/12(金)に大阪で開催された「JAWS-UG CDK支部#22 大阪でもCDKしたいねん」にて、「CDK CLIでできてたあの機能、CDK Toolkit Libraryではどうやるの?」というタイトルで、このブログに記載の内容でLTをさせていただきました。
また今週末の10/11(土)に金沢で開催される「JAWS Festa 2025」において、ありがたいことにCFPが採用され、業務効率化をさらに加速させる、ノーコードツールとStep Functionsのハイブリッド化というタイトルで登壇させていただくことになりましたので、よろしくお願いいたします。(ノーコードツールとの併用やStep Functionsの効果的な使い方についてお話しさせていただきます)
それでは、今回はこの辺で。
-
公式ページに記載のToolkitError.isDeploymentErrorは、実際のソースコードでは定義されていませんでした。 ↩