Claude CodeでAWS CDKスタックを書く — TypeScriptとPythonの実装フロー
Claude CodeでAWS CDKスタックを書く手順を、環境構築からBash権限設計、TypeScript/Pythonの書き分け、cdk deployの確認フローまで扱います。
Claude CodeでAWS CDKスタックを書くとは
Claude CodeでAWS CDKスタックを書くとは、TypeScriptやPythonといった汎用言語でAWSリソースを定義することです。コードの生成とcdkコマンドの実行はClaude Codeに任せます。AWS CDK(AWS Cloud Development Kit)は、インフラをコードで管理するIaC(Infrastructure as Code)のフレームワークです。書いたコードは最終的にAWS CloudFormationのテンプレートへ変換され、デプロイされます。CDK v1は2023年6月1日にサポートが終了しており、現行はv2です。
CDKのコードはApp・Stack・Constructという3階層で構成されます。Appインスタンスの中に1つ以上のStackを作り、Stackの中でAWSリソースを表すConstructを組み立てます。この階層構造はTypeScriptとPythonで見た目こそ違いますが、意味は共通です。型ヒントやクラス構造で表現されるぶん、Claude Codeにとっても仕様書とコードの区別がつきやすい対象になります。この記事では、環境構築からBash権限の設計、TypeScript/Pythonの書き分け、変更を確認してから反映するまでの流れを扱います。
環境をセットアップする
前提として、使用する言語に関わらずNode.js 22.x以降が要ります。CDKの各言語ランタイムは同じバックエンドで動き、そのバックエンドがNode.js上で動作するためです。加えてTypeScriptなら3.8以降、Pythonなら3.9以降(pip・virtualenv込み)が必要です。AWSアカウントとAWS CLIの設定も事前に済ませておきます。
最初にCDK CLIをインストールします。Node Package Managerでグローバルにインストールするのが公式の推奨です。
npm install -g aws-cdk
cdk --version複数バージョンを使い分けたい場合は、プロジェクトごとにローカルインストールしてnpx aws-cdkで呼び出す方法もあります。デプロイには、実行環境に設定されたAWSの認証情報が要ります。CDK CLIはAWS CLIで設定した認証情報をそのまま使います。IAM Identity Centerのユーザーならaws configure sso、IAMユーザーならaws configureで設定します。プロファイルを複数使う場合は、CDK CLIの--profileオプションで指定します。
プロジェクトを作るときは、cdk initにテンプレート名と言語を渡します。
mkdir hello-cdk && cd hello-cdk
cdk init app --language typescriptPythonの場合は、初期化後に仮想環境の有効化と依存インストールが追加で要ります。
cdk init app --language python
source .venv/bin/activate
python -m pip install -r requirements.txtcdk initで作られるディレクトリ名は、生成されるコード内の命名にも使われるため、名前はcdk initの前に決め切っておきます(理由は後述の「よくあるつまずき」で扱います)。
最後に、デプロイ先のAWS環境を一度だけ準備します。
cdk bootstrapプロジェクトのルートから実行すれば、環境情報はプロジェクトから自動で取得されます。
Bash権限でcdkコマンドの実行範囲を決める
cdkサブコマンドは、ローカルで完結するものとAWS上のリソースを変更するものが混在します。cdk synthはコードからCloudFormationテンプレートを生成するだけの操作で、AWSへの接続を伴いません。一方cdk deployはCloudFormationを通じて実際にリソースを作成・変更し、cdk destroyはスタックとその中のリソースを削除します。Claude Codeの権限設定はBash(<コマンド>)という形式のルールで、*を使えば一群のコマンドを1つのルールでまとめて扱えます。
{
"permissions": {
"allow": ["Bash(cdk synth)", "Bash(cdk diff)", "Bash(cdk list)"],
"ask": ["Bash(cdk deploy *)"],
"deny": ["Bash(cdk destroy *)"]
}
}cdk deployは既定(--require-approval broadening)で、IAM文の追加や権限・セキュリティグループの拡大を伴う変更のときだけ対話のy/n確認を挟みます。公式チュートリアルの実行例では、Lambda用のIAMロール作成が次のように提示されます。
IAM Statement Changes
┌───┬───────────────────────────────────────┬────────┬──────────────────────────┐
│ + │ ${HelloWorldFunction/ServiceRole.Arn} │ Allow │ sts:AssumeRole │
└───┴───────────────────────────────────────┴────────┴──────────────────────────┘
Do you wish to deploy these changes (y/n)?このy/nはCDK CLI自身が出す対話プロンプトで、答えるには標準入力(TTY)が要ります。Claude CodeのBashツールにはTTYが無いため、askルールでClaude Code側の許可を一度通しても、その先にあるCDK側のy/nにはClaude Codeから答えられず、そのままではデプロイは完了しません。Claude Code経由でも実際に回すには、依頼するコマンドに--require-approval neverを付けてCDK側の確認自体を無くし、承認の判断はClaude Codeのaskルール(実行前に人間が許可・拒否する)だけに一本化します。any-changeや既定のbroadeningのまま対話確認を使いたい場合は、cdk deployは自分のターミナルで直接実行し、y/nにはその場で答えます。承認の判断をClaude Codeのaskルールに一本化するときは、許可の前にcdk diffの出力でIAM変更の内容を確認する運用を挟むと、内容を見ないまま許可することを避けられます。
より強く介入したい場合は、PreToolUseフックでcdk deployやcdk destroyの呼び出し自体を検査する方法もあります。対象スタック名やリージョンによって拒否・確認強制・素通しを切り替えられます。フックの判定はallow/askルールより優先されるため、拒否コード(exit code 2)を返せば許可リストに関係なく実行を止められます。
CLAUDE.mdでTypeScript/Pythonの書き分けを固定する
CLAUDE.mdはセッション開始時に毎回読み込まれるファイルで、プロジェクト全体に効かせたい規約を置く場所です。AWS CDKはTypeScript・Python・Java・C#・Goに対応していますが、同じFunctionコンストラクトでも言語ごとに書き方の慣習が変わります。
| 観点 | TypeScript | Python |
|---|---|---|
| プロパティの渡し方 | TypeScript1つのオブジェクト引数 | Pythonキーワード引数 |
| プロパティ名の書式 | TypeScriptcamelCase | Pythonsnake_case |
| インスタンス化 | TypeScriptnewキーワードを使う | Pythonnewは使わない |
| scope引数の慣習名 | TypeScriptthis | Pythonself |
| モジュールの読み込み | TypeScriptimport { aws_s3 as s3 } from 'aws-cdk-lib' | Pythonimport aws_cdk.aws_s3 as s3 |
この違いをCLAUDE.mdに明文化しておくと、Claude Codeが言語をまたいで実装するときにも書式が揺れにくくなります。
# CDKスタック実装の規約
- このプロジェクトの言語はPythonに統一する(TypeScriptのコード例をそのまま移植しない)
- プロパティはキーワード引数で渡し、snake_caseで命名する
- コンストラクトのscope引数は必ず`self`で受け取るスタックとLambda関数を実装させる
cdk initで作られたスタックファイルには、Stackを継承した空のクラスが1つ用意されています。TypeScriptの場合は次のような形です。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class HelloCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Define your constructs here
}
}Pythonではselfを使い、キーワード引数で親クラスを呼び出します。
from aws_cdk import Stack
from constructs import Construct
class HelloCdkStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Define your constructs hereここにAWS Construct LibraryのFunctionを追加すると、Lambda関数がリソースとして定義されます。TypeScriptの例です。
import * as lambda from 'aws-cdk-lib/aws-lambda';
const myFunction = new lambda.Function(this, "HelloWorldFunction", {
runtime: lambda.Runtime.NODEJS_20_X,
handler: "index.handler",
code: lambda.Code.fromInline(`
exports.handler = async function(event) {
return { statusCode: 200, body: JSON.stringify('Hello World!') };
};
`),
});Functionのようなコンストラクトは、どの言語でも同じ3つの引数(scope・id・props)を取ります。scopeは親となるStackインスタンス、idはアプリ内で構成要素を一意に識別する構成ID、propsはランタイムやハンドラー名などの設定値です。Pythonではpropsがキーワード引数に展開される点だけが変わります。
from aws_cdk import aws_lambda as _lambda
my_function = _lambda.Function(
self, "HelloWorldFunction",
runtime=_lambda.Runtime.NODEJS_20_X,
handler="index.handler",
code=_lambda.Code.from_inline("""
exports.handler = async function(event) {
return { statusCode: 200, body: JSON.stringify('Hello World!') };
};
"""),
)構成IDのHelloWorldFunctionは、デプロイ時に位置情報から生成されるハッシュと組み合わさって、実際のAWSリソース名の一部になります。Claude Codeに実装を任せる際も、この構成IDをコンストラクトの追加・削除のたびに変えないようにしておくと、再デプロイ時に既存リソースが意図せず置き換わる事態を避けやすくなります。
変更を依頼するときは、変えてよい範囲を具体的に指定すると事故を防げます。
「
HelloWorldFunctionの構成IDとruntimeは変えずに、handlerだけindex.handlerからapp.handlerに変更して。変更したらcdk synthを実行して、差分がハンドラー名以外に及んでいないか出力を見せて」
このように依頼すると、Claude Codeは構成IDを保持したまま該当プロパティだけを書き換え、cdk synthの出力を提示させられます。生成されたテンプレートのPropertiesを見れば、意図しないリソースの追加・削除が起きていないかをデプロイ前に確認できます。
cdk diff / cdk synth / cdk deployで変更を確認してから反映する
スタックを書き終えたら、まずcdk listでアプリ内のスタック名を確認します。
cdk list
cdk synthcdk synthはコードを実行してCloudFormationテンプレートを生成し、標準出力にYAML形式で表示します。この時点ではAWSへの接続は発生しません。テンプレートに問題がなければcdk deployで反映します。
cdk deployすでにデプロイ済みのスタックを変更した場合は、cdk diffで差分だけを先に確認できます。デプロイが完了すると、CDK CLIはスタックのARNや出力値(Lambda関数URLなど)を表示します。スタックが不要になったらcdk destroyで削除します。
cdk destroycdk destroyも既定では削除前にAre you sure you want to delete: HelloCdkStack (y/n)?という対話確認を挟みます。これもCDK CLI自身のプロンプトなので、TTYの無いClaude Code上ではこのy/nにClaude Codeから答えることができません。前段の権限設定をdenyにしておけば、この確認に辿り着く前にClaude Code側で実行がブロックされ、破壊的操作を人の手に残せます。Claude Code経由で削除まで自動化したい場合は、denyをaskに変えたうえでコマンドに--force(-f)を付けてCDK側の確認をスキップし、承認の判断をClaude Codeのaskルールだけに委ねます。公式チュートリアルの実行例では、デプロイ完了までに数十秒かかっています。会話を止めずに進めたい場合は、Ctrl+Bでコマンドを裏に回すClaude Codeバックグラウンド実行が参考になります。
よくあるつまずき
npx cdk deployが許可ルールに引っかからない
Bash(cdk deploy *)という許可ルールを書いても、Claude Codeがnpx cdk deployとしてコマンドを組み立てた場合はルールが一致せず、毎回確認を求められます。npxはClaude Codeが自動で読み飛ばすラッパーの一覧に含まれないためです。ローカルインストールしたCDK CLIをnpx経由で使う運用なら、npxを含めた形でルールを書き直します。
Pythonの仮想環境を有効化し忘れる
cdk init --language pythonのあと、.venvの有効化とpip install -r requirements.txtを忘れたままコマンドを進めるとどうなるでしょうか。aws_cdkモジュールが見つからないというエラーで、cdk synthやcdk deployが失敗します。TypeScript側では対応する手順が不要なため、言語を切り替えるたびにこの一手間を忘れがちです。
ディレクトリ名を後から変える
公式ドキュメントは、チュートリアル用ディレクトリを作る際に「このディレクトリ名を変えないこと」を明示の注意事項にしています。cdk init実行後にディレクトリ名を変更すると、既に生成済みのApp・Stackファイル内の命名と食い違う可能性があるためです。Claude Codeに後からディレクトリ名の変更を頼むと、生成済みのidや参照がずれる余地があります。命名をやり直したい場合は、ディレクトリを変更するより先にコード内のApp・Stackのidを揃えて書き直すほうが安全です。
cdk bootstrapを忘れたままデプロイする
cdk bootstrapは、CDKのデプロイに使うAWS環境を事前に準備する一度限りの手順です。まだブートストラップしていない環境に対してcdk deployを実行すると、デプロイが失敗します。新しいAWSアカウントやリージョンで初めてCDKを使うときは、cdk deployより先にcdk bootstrapを実行したかを確認します。
まとめ
Claude CodeでAWS CDKスタックを書く流れは、CDK CLIのインストールとプロジェクト初期化から始まります。cdk synthとcdk deployの間にBash権限やPreToolUseフックで確認の層を挟み、CLAUDE.mdで言語ごとの書き分けを固定するところまでが1セットです。TypeScriptとPythonではnewの有無や引数の渡し方が変わるだけで、App・Stack・Constructという構造自体は共通しています。
テンプレートの構文チェックやコンプライアンス確認、デプロイ失敗時の原因診断まで自動化したいなら、次のステップはCloudFormation/CDKをMCPで自動検証するです。Terraformと使い分けたい場合はTerraform MCPサーバーの使い方が参考になります。破壊的操作をauto modeがどこまで止めるかは、Claude Code auto modeブロック一覧で確認できます。