Claude CodeでAWS Lambda(SAM)を開発 — local invokeで検証しdeployは承認制に
Claude CodeにAWS SAMのLambdaを書かせるとき、sam validate・build・local invokeを自走の検証ループにし、sam deployだけ確認を挟む権限設定とCLAUDE.mdの書き方を示します。
Claude CodeにAWS SAMのLambdaを書かせるなら、sam validate・sam build・sam local invokeは確認なしで回し、sam deployだけ毎回人が承認する構成が扱いやすくなります。前者は手元で完結し、後者はAWSアカウントに変更を加えるからです。この記事は、その線引きを.claude/settings.jsonとCLAUDE.mdに落とす手順をまとめます。
コマンドを「手元で閉じるもの」と「AWSに触るもの」に分ける
SAM CLIのコマンドは、作用する場所で3つに分けられます。分け方が権限ルールの設計そのものになります。
| 区分 | コマンド | 作用先 | Claude Codeでの扱い |
|---|---|---|---|
| 静的検証 | コマンドsam validate | 作用先テンプレートのみ | Claude Codeでの扱いallow |
| ローカル実行 | コマンドsam build / sam local invoke | 作用先手元のファイルとDockerコンテナ | Claude Codeでの扱いallow |
| AWSへの反映 | コマンドsam deploy | 作用先CloudFormationスタック | Claude Codeでの扱いask |
sam validateはSAMテンプレートが正しいかを確認するコマンドです。sam local invokeはLambda関数を1回だけローカルで呼び出します。sam deployはCloudFormationを使ってアプリケーションをAWSクラウドへデプロイします。
Claude CodeのBashは、組み込みの読み取り専用コマンド(lsやcatなど)を除いて承認が要ります。samはその例に挙がっていないので、何も設定しなければ検証コマンドまで毎回確認が出ます。検証は自走させたいので、allowで通します。
settings.jsonに書く権限ルール
プロジェクトの.claude/settings.jsonに次のように置きます。
{
"permissions": {
"allow": [
"Bash(sam validate *)",
"Bash(sam build *)",
"Bash(sam local invoke *)"
],
"ask": [
"Bash(sam deploy *)"
],
"deny": [
"Bash(sam deploy *--no-confirm-changeset*)"
]
}
}ルールの読み方を確認しておきます。
- 評価順はdeny、ask、allowの順で、最初に一致したものが結果を決めます。askに一致するコマンドは、より狭いallowがあっても確認が出ます
- 末尾の
*は、前にスペースがあると引数なしの裸のコマンドにも一致します。Bash(sam deploy *)はsam deploy単体も拾います *はサブコマンドの後ろに置きます。Bash(sam * deploy)のように前へ置くと、意図しないコマンドまで一致します
3つ目のdenyは、後述する--no-confirm-changesetを締め出すための行です。denyがaskより先に評価されるため、このフラグを付けたsam deployは確認画面に進まず拒否されます。
環境変数の前置きも扱いに違いがあります。AWS_PROFILE=dev sam deployのように前へ変数代入を付けても、askとdenyのルールはその代入を飛び越えて一致します。一方allowは、既知の安全な変数以外の代入があると一致しません。AWS_PROFILE付きのsam local invokeはallowに乗らず確認が出る、という挙動になります。
CLAUDE.mdに検証の順序を書く
権限は「できること」を決め、CLAUDE.mdは「やる順序」を決めます。両方そろえると、Claudeが自分で検証を反復します。
## AWS SAM(Lambda)の作業手順
テンプレートやハンドラーを変えたら、次の順で実行して結果を読む。
1. `sam validate --lint`(テンプレートの検証)
2. `sam build`(ビルドが通ること)
3. `sam local invoke <関数の論理ID> -e events/<名前>.json`
- イベントは`events/`のJSONを使う。無い場合は先に作る
- 環境変数は`env.json`を`-n`で渡す。実際のシークレットは書かない
- `sam deploy --no-execute-changeset`は承認を得て実行し、変更セットの要約を示す
- 変更を適用する`sam deploy`は自分から提案せず、人が要約を読んで依頼したときだけ実行する
- `--no-confirm-changeset`と`--guided`は使わないsam validateの--lintは、cfn-lintによるリンティングも合わせて走らせるオプションです。追加の設定はcfnlintrcファイルで指定します。テンプレートの文法エラーだけでなく、CloudFormation側の指摘まで手元で受けられるので、Claudeの修正の入口として向いています。
検証ループの中身
validateとbuild
sam validate --lint
sam buildsam validateは、カレントディレクトリにあるtemplate.yaml(.ymlや.jsonも可)を既定で読みます。別の場所なら--template-file(-t)で指定します。sam buildを直前に実行していれば、このオプションも不要です。
ネイティブにコンパイルされた依存パッケージを含む関数は、sam build --use-container(-u)でLambdaに近いDockerコンテナ内でビルドします。逆に--no-use-containerを付けると手元のマシンでビルドします。どちらを使うかはランタイムと依存によって変わるので、CLAUDE.mdに一行書いておくと迷いません。
local invokeでハンドラーを実際に動かす
sam local invoke HelloWorldFunction -e events/apigw-get.json引数は関数の論理IDです。テンプレートに関数が1つしかなければ省略でき、複数あるときは指定が必要です。
イベントの渡し方は次のとおりです。
| 目的 | オプション |
|---|---|
| JSONファイルのイベントを渡す | オプション--event(-e)events/x.json |
| 標準入力から渡す | オプション-e - |
| 空のイベントで呼ぶ | オプション--no-event |
| 環境変数を渡す | オプション--env-vars(-n)env.json |
--eventを指定しない場合、イベントは渡されません。S3など他サービスのイベント形式は、AWS Lambdaの開発者ガイドが定義しています。イベントJSONはevents/の下にファイルとして置いておくと、同じ入力で何度でも再現できます。sam local generate-eventというサブコマンドもコマンド一覧に載っています。
Claudeへは、出力を読ませたうえで失敗時の修正まで任せます。ステータスコードや例外のスタックトレースが返るので、ハンドラーの修正、再ビルド、再実行が1ターンの中で回ります。
ローカル実行の前提と制約
sam local invokeは、Lambdaのランタイムに近いイメージをDockerコンテナとして起動して関数を動かします。既定ではLambdaの最新のリモートランタイム環境と同期するため、ローカルのイメージを自動更新します。--skip-pull-imageを付けるとこの取得を省けます。毎回の取得を省きたいときに使います。
AWSのドキュメントは、信頼できないコードに対してSAM CLIのローカル呼び出しを使うことを勧めていません。手元の環境から完全に隔離して動かしたいなら、Lambdaサービス上で直接実行するよう案内されています。Claudeが外部から取り込んだコードをsam local invokeで動かす流れは避け、自分のリポジトリのハンドラーに限る運用にすると安心です。
もう一点。allowに入れたBash(sam local invoke *)は、--profileや--regionを付けても通ります。sam local invokeにAWSの認証情報を渡す必要がない場合(ハンドラーが他のAWSサービスを呼ばない場合)は、CLAUDE.mdに「--profileは付けない」と書いて、資格情報の行き場を減らしておきます。
deployを承認制にする
Bash(sam deploy *)をaskに置くと、Claudeがsam deployを実行しようとするたびに確認ダイアログが出ます。承認を毎回求める点が、この設定の要です。ダイアログで「Yes, and don't ask again」を選ぶと、そのコマンドのallowルールが.claude/settings.local.jsonに保存されます。ただし評価順はaskがallowより先なので、askルールが残っている限り、保存後も確認は出続けます。
変更セットを先に見せる
sam deployには、変更を適用せずに確認だけするオプションがあります。
sam deploy --no-execute-changesetこのオプションは、CloudFormationの変更セットを作成して終了します。適用するには、同じコマンドを--no-execute-changesetなしで再実行します。何が作られ、何が置き換わり、何が削除されるのかを、人が読める形で先に出せます。
CLAUDE.mdの手順は、ここで生成された変更セットの内容をClaudeに要約させ、人が読んだうえで本番のdeployを承認する、という2段構えにしてあります。ただし--no-execute-changesetもAWSへ変更セットを作るコマンドなので、askの対象に含まれます。確認なしで実行できるわけではありません。
confirm-changesetと--no-confirm-changeset
--confirm-changesetは、計算された変更セットをSAM CLIがデプロイしてよいか、実行前にプロンプトで確認するオプションです。反対の--no-confirm-changesetを付けると、確認が省かれます。自動化のスクリプトではよく使われるフラグですが、承認制にしたいこのリポジトリではClaudeに使わせたくありません。
先のdenyルールBash(sam deploy *--no-confirm-changeset*)は、その対策です。--guided(対話形式のデプロイ)も同様に、Claudeが扱うと対話待ちで止まるので、CLAUDE.md側で使わない旨を書きます。
IAMを作る変更は--capabilitiesで明示される
テンプレートがIAMリソースを含むと、sam deployには--capabilities CAPABILITY_IAM(名前付きならCAPABILITY_NAMED_IAM)が必要です。指定しないとInsufficientCapabilitiesエラーになります。ネストしたアプリケーションを含むときはCAPABILITY_AUTO_EXPANDです。
承認ダイアログに表示されるコマンド文字列にこのオプションがあれば、IAMを作る変更が含まれるサインになります。承認するときの目印として、CLAUDE.mdにも「IAMを伴うdeployは、変更セットの要約でIAMリソースを必ず挙げる」と書いておくと、確認の質が上がります。
ルールだけでは止められない呼び方
Bashルールは、Claudeが書いたコマンド文字列に一致させる仕組みで、そのプログラムに対する安全境界ではありません。Bash(sam deploy *)のaskが止めるのは、sam deploy ...という通常の書き方です。/usr/local/bin/sam deployやbash -c 'sam deploy'のように別の形で呼ばれた場合、このルールは一致しません。他のルールと権限モードが結果を決めます。
対策は3つあります。
- PreToolUseフックで全文を検査する。フックは権限プロンプトの前に走り、終了コード2でBashの実行を止められます。
- サンドボックスを併用する。ファイルシステムとネットワークへのアクセスをOSレベルで制限できます。プロンプトインジェクションでClaudeの判断が崩れても、サンドボックスの制限は残ります。
- AWS側の権限を絞る。開発用の資格情報にCloudFormationの変更権限を渡さないのが、最も確実です。
フックの例です。bash -cやフルパス経由でsam deployを呼ぶ形だけを止め、通常の書き方はaskに任せます。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-sam-deploy.sh"
}
]
}
]
}
}#!/bin/bash
cmd=$(jq -r '.tool_input.command')
if echo "$cmd" | grep -Eq '(/|sh -c |bash -c ).*sam[[:space:]]+deploy'; then
echo "sam deployは通常の書き方で実行してください。" >&2
exit 2
fi
exit 0jqで標準入力のJSONからtool_input.commandを取り出し、該当すれば終了コード2で止めます。この形は迂回だけを狙うので、通常のsam deployはaskの確認ダイアログまで進みます。
同じ考え方でTerraformのapplyを止める手順は、Claude CodeでTerraformのコードを書く手順にあります。CloudFormationやCDKのコードをMCPで検証する方法は、aws-iac-mcp-serverの使い方が扱っています。
よくあるつまずき
- 検証コマンドで毎回確認が出る。
samは読み取り専用の組み込み集合に入っていないため、allowが無いと承認を求められます。sam local invokeとsam buildをallowに足します - allowを書いたのに
AWS_PROFILE=... sam local invokeで確認が出る。allowは、既知の安全な変数以外の代入を前置きすると一致しません。変数の前置きをやめて--profileで渡すか、そのコマンドだけ確認を許容します - deployが承認なしで動いた。askルールが効いていない可能性があります。askを書いた設定ファイルが別のスコープで、実行中のプロジェクトに読まれていない、
bypassPermissionsなどの権限モードで実行している、/usr/local/bin/sam deployやbash -cのような迂回形で呼ばれた、のどれかを疑います。ルールの居場所と有効な内容は/permissionsで確認できます。迂回形は前節のフックで止めます sam deployでInsufficientCapabilitiesが出る。IAMリソースを含むテンプレートです。--capabilitiesを承認画面のコマンドに含めて再依頼しますsam deployが変更なしで非ゼロ終了する。変更セットに差分が無いとき、既定では非ゼロの終了コードを返します。--no-fail-on-empty-changesetで挙動を変えられますが、Claudeが失敗と誤解しないよう、CLAUDE.mdにこの挙動を書いておくと混乱しません- local invokeの起動ごとにイメージ取得が走る。既定の同期動作です。
--skip-pull-imageで省けます
まとめ
SAMの開発ループは、手元で閉じるvalidate・build・local invokeを自走させ、AWSへ作用するdeployを承認に回すと整理できます。allow・ask・denyの3種のルールで線を引き、CLAUDE.mdで検証の順序と変更セットの確認手順を書き、迂回経路にはフック、サンドボックス、資格情報の絞り込みで備えます。ルールは書いた文字列にしか一致しないため、最後に頼れるのはAWS側の権限です。