Claude Media
Claude CodeでAWS CDKスタックを書く — TypeScriptとPythonの実装フロー

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 typescript

Pythonの場合は、初期化後に仮想環境の有効化と依存インストールが追加で要ります。

cdk init app --language python
source .venv/bin/activate
python -m pip install -r requirements.txt

公式チュートリアルはディレクトリ名を変えないよう注意しているため、名前はcdk initの前に決め切っておきます(理由は後述の「よくあるつまずき」で扱います)。

最後に、デプロイ先のAWS環境を一度だけ準備します。

cdk bootstrap

プロジェクトのルートから実行すれば、環境情報はプロジェクトから自動で取得されます。

Bash権限でcdkコマンドの実行範囲を決める

cdkのサブコマンドは、AWSに触れるかどうかで3つに分かれます。権限ルールはBash(<コマンド>)の形式で、*が任意の文字列を表します。

影響範囲

cdkサブコマンドの影響範囲

  • synth・diff・list

    cdk synthはコードからCloudFormationテンプレートを作るコマンドで、リソースの作成・変更は行いません。fromLookupなどのコンテキスト検索を使うアプリでは、AWSへの問い合わせが入ります。cdk diffは差分表示、cdk listはスタック名の一覧です。allowに置けます。

  • deploy

    CloudFormation経由でリソースを作成・変更します。実行前に人が見るaskが向きます。

  • destroy

    スタックとリソースを削除します。denyに置くと、Claude Codeからは実行されません。

{
  "permissions": {
    "allow": ["Bash(cdk synth *)", "Bash(cdk diff *)", "Bash(cdk list *)"],
    "ask": ["Bash(cdk deploy *)"],
    "deny": ["Bash(cdk destroy *)", "Bash(cdk * destroy *)"]
  }
}

末尾の*の前にスペースを置いた書き方は、引数なしのcdk synthにも一致します。一方Bash(cdk synth)のように*を付けないルールは、cdk synth --quietには一致しません。denyを2本にしているのは、cdk --profile prod destroyのようにオプションが間に入った形がBash(cdk destroy *)では止まらないためです。ルールが照合するのはClaude Codeが書いたコマンド文字列そのもので、npx cdk destroyのように前にコマンドが付いた形も別の文字列です。

評価の順序はdeny、ask、allowで、最初に一致したものが結果になります。allowにBash(cdk *)と書いても、denyに一致するcdk destroyは止まったままです。

cdk deployの確認が二重になるとき

cdk deployは既定(--require-approval broadening)で、IAM文の追加や権限・セキュリティグループの拡大を伴う変更のときだけ、CDK CLI自身がy/nを尋ねます。公式チュートリアルの実行例では、Lambda用のIAMロール作成が次のように提示されます(表は列を省略した抜粋です)。

IAM Statement Changes
┌───┬───────────────────────────────────────┬────────┬──────────────────────────┐
│ + │ ${HelloWorldFunction/ServiceRole.Arn} │ Allow  │ sts:AssumeRole           │
└───┴───────────────────────────────────────┴────────┴──────────────────────────┘
Do you wish to deploy these changes (y/n)?

--require-approvalにはany-change(あらゆる変更で確認)、broadening(既定)、never(確認なし)の3値があります。askルールを置いたうえで、CDK側の確認も残すと、人が答える場面が2回になります。どちらに寄せるかで運用が分かれます。

くらべる

確認をどこに置くか

--require-approval never

Claude Code側に一本化

依頼するコマンドに--require-approval neverを付け、承認はaskルールの許可だけにします。許可の前にcdk diffでIAM変更を見る手順を挟むと、中身を見ないまま許可する事態を避けられます。

自分のターミナル

CDK側の確認を使う

cdk deployは自分のターミナルで実行し、y/nにその場で答えます。broadeningやany-changeをそのまま使えます。Claude Codeにはcdk synthとcdk diffまでを任せます。

より強く介入したい場合は、PreToolUseフックでcdk deployやcdk destroyの呼び出しを検査する方法があります。対象スタック名やリージョンによって拒否・確認強制・素通しを切り替えられます。フックが終了コード2を返すとツール呼び出しはブロックされ、JSONでpermissionDecisionにallowを返しても覆せません。

CLIの引数でも同じルールを渡せる

同じ形式のルールは、設定ファイルを書き換えずにセッション単位でも渡せます。claude --helpには次のオプションが載っています。

claude --version
claude --help | grep -A2 -E "allowedTools|disallowedTools"
2.1.289 (Claude Code)
  --allowedTools, --allowed-tools <tools...>
      Comma or space-separated list of tool names to allow (e.g. "Bash(git *)
      Edit")
  --disallowedTools, --disallowed-tools <tools...>
      Comma or space-separated list of tool names to deny (e.g. "Bash(git *)
      Edit")

使い捨ての検証用ブランチでだけcdk synthを許可したいときは、--allowedTools "Bash(cdk synth *)"のように渡せます。--permission-modeの選択肢はacceptEdits・auto・bypassPermissions・manual・dontAsk・planの6つです。

CLAUDE.mdでTypeScript/Pythonの書き分けを固定する

CLAUDE.mdはセッション開始時に毎回読み込まれるファイルで、プロジェクト全体に効かせたい規約を置く場所です。AWS CDKはTypeScript・Python・Java・C#・Goに対応していますが、同じFunctionコンストラクトでも言語ごとに書き方の慣習が変わります。

観点TypeScriptPython
プロパティの渡し方TypeScript1つのオブジェクト引数Pythonキーワード引数
プロパティ名の書式TypeScriptcamelCasePythonsnake_case
インスタンス化TypeScriptnewキーワードを使うPythonnewは使わない
scope引数の慣習名TypeScriptthisPythonself
モジュールの読み込み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がキーワード引数に展開される点だけが変わります。なおNODEJS_20_Xは公式チュートリアルのコード例にある値で、コメントには「サポートされている任意のNode.jsランタイムを指定できる」とあります。

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 synth

cdk synthはコードを実行してCloudFormationテンプレートを生成し、標準出力にYAML形式で表示します。この時点ではリソースの作成・変更は行われません。テンプレートに問題がなければcdk deployで反映します。

cdk deploy

すでにデプロイ済みのスタックを変更した場合は、cdk diffで差分だけを先に確認できます。デプロイが完了すると、CDK CLIはスタックのARNや出力値(Lambda関数URLなど)を表示します。スタックが不要になったらcdk destroyで削除します。

cdk destroy

cdk destroyも既定では削除前にAre you sure you want to delete: HelloCdkStack (y/n)?と確認します。--force(-f)を付けると、この確認を省いて削除します。前段の権限設定をdenyにしておけば、確認に辿り着く前にClaude Code側で実行がブロックされ、破壊的操作を人の手に残せます。Claude Code経由で削除まで任せたい場合は、denyをaskに変えたうえで--forceを付け、承認をClaude Codeのaskルールに一本化します。

公式チュートリアルの実行例では、2回のデプロイ時間(Deployment time)が26.96秒と41.65秒でした。会話を止めずに進めたい場合は、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を忘れたままコマンドを進めると、依存パッケージが入っていない環境でアプリを実行することになります。TypeScript側では対応する手順が不要なため、言語を切り替えるたびにこの一手間を忘れがちです。

ディレクトリ名を後から変える

公式ドキュメントは、チュートリアル用ディレクトリを作る際に「このディレクトリ名を変えないこと」を明示の注意事項にしています。理由は、cdk initの後に違う名前へ変えるとチュートリアルで問題が起きるためです。名前を決めてからcdk initを実行し、Claude Codeにも後からディレクトリ名を変えさせない運用が無難です。

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セットです。確認をどこに置くかをcdk deployの手前で決めておけば、言語がTypeScriptでもPythonでも、newの有無や引数の渡し方の違いはCLAUDE.mdが吸収します。

テンプレートの構文チェックやコンプライアンス確認、デプロイ失敗時の原因診断まで自動化したいなら、次のステップはCloudFormation/CDKをMCPで自動検証するです。Terraformと使い分けたい場合はTerraform MCPサーバーの使い方が参考になります。破壊的操作をauto modeがどこまで止めるかは、Claude Code auto modeブロック一覧で確認できます。

この記事を共有:XはてブLinkedIn