Claude CodeでBicepを書きAzureにデプロイ — what-ifを承認の境界に
Claude CodeにBicepを書かせてAzureへ反映するとき、az deployment what-ifまでを自走させ、createだけ人が承認する権限設定とCLAUDE.mdの書き方、what-ifの出力の読み方を示します。
Claude CodeにBicepを書かせるなら、az deployment group what-ifまでは確認なしで回し、az deployment group createだけ毎回人が承認する構成が組みやすくなります。what-ifはAzureに変更を加えず、デプロイしたらどうなるかだけを返すからです。この記事は、その線引きを.claude/settings.jsonとCLAUDE.mdに落とす手順と、what-ifの出力を承認の材料として読むときの注意点をまとめます。
BicepとClaude Codeの分担
Bicepは、Azureリソースを宣言的な構文でデプロイするためのドメイン固有言語です。Bicepファイルにあるべきインフラを書き、同じファイルを開発のライフサイクル全体で繰り返し使います。リソースの種類とAPIバージョンは、プレビューもGAも、リソースプロバイダーが出した時点でBicepから使えます。ツール側の対応を待つ必要はありません。
Bicepは手順を並べる言語ではないので、Claude Codeの出力は「欲しい状態の記述」になります。確認の対象は、ファイルの差分と、Azureが返す変更予測の2つです。
Claude Codeが書いたBicepが意図どおりかは、デプロイしなくてもwhat-ifで確認できます。そこで作業を次の3段に分けます。
| 段 | 主なコマンド | Azureへの影響 | 権限の扱い |
|---|---|---|---|
| 書く・静的に確認する | 主なコマンドBicepファイルの編集、リンター | Azureへの影響なし | 権限の扱いallow(編集はClaude Codeの通常の編集権限) |
| 変更を予測する | 主なコマンドaz deployment group what-if | Azureへの影響なし(予測のみ) | 権限の扱いallow |
| 反映する | 主なコマンドaz deployment group create | Azureへの影響あり | 権限の扱いask(毎回承認) |
Claude Codeの権限は、この表の3行目だけを人の手元に残すように組みます。
what-ifが承認の境界に向いている理由
what-ifは、Bicepファイルをデプロイしたら何が変わるかを事前に見せる操作です。既存リソースには何も変更を加えません。スコープはリソースグループ、サブスクリプション、管理グループ、テナントの4つに対応しています。
コマンドはスコープごとに分かれています。
az deployment group what-if: リソースグループaz deployment sub what-if: サブスクリプションaz deployment mg what-if: 管理グループaz deployment tenant what-if: テナント
実行にはAzure CLI 2.14.0以降が必要です。Azure CLIのcreate系コマンドにも--confirm-with-what-if(短縮形-c)があり、変更を表示してから続行するかを対話で尋ねます。ただし、この確認はAzure CLIの標準入力に対するy/nです。Claude Codeに任せる場合、承認の主体をAzure CLIのy/nではなくClaude Codeの権限プロンプトに置いた方が、承認の記録と設定が一か所に集まります。
Azure側の権限では分けられない
見落としやすい点があります。what-ifに必要な権限は、デプロイと同じです。Bicepファイルをデプロイするには、対象リソースへの書き込み権限と、Microsoft.Resources/deploymentsリソースタイプへのすべての操作の権限が要ります。例として、仮想マシンのデプロイにはMicrosoft.Compute/virtualMachines/writeとMicrosoft.Resources/deployments/*が必要で、what-ifも同じ要件です。
つまり、Claude Codeに渡す資格情報でwhat-ifだけを許し、createを拒むという分け方は、Azureのロールだけでは作れません。what-ifを回せる資格情報は、createも実行できる資格情報です。
何を許し、何を止めるかは、資格情報の外、Claude Code側の権限ルールが決めます。これが記事題の「what-ifを承認の境界にする」の意味です。
Azure CLI 2.76.0以降には--validation-levelがあり、検証の厳しさを選べます。
| 値 | 内容 |
|---|---|
Provider(既定) | 内容構文、リソース定義、依存関係に加え、全リソースをデプロイできる権限があるかまで確認する |
ProviderNoRbac | 内容Providerと同様に検証するが、各リソースについて確認する権限は読み取りだけ |
Template | 内容構文と構造の静的検証のみ。事前チェックと権限チェックは省略される |
Templateは、リソースの空き状況のような事前チェックを飛ばすため、デプロイが失敗する原因を見逃すことがあります。Claude Codeに最初に回させる軽い確認には向きますが、承認の材料にはなりません。承認前の確認には既定のProviderを使います。
settings.jsonに書く権限ルール
プロジェクトの.claude/settings.jsonに次のように置きます。
{
"permissions": {
"allow": [
"Bash(az deployment group what-if *)",
"Bash(az deployment sub what-if *)"
],
"ask": [
"Bash(az deployment group create *)",
"Bash(az deployment sub create *)",
"Bash(az deployment mg create *)",
"Bash(az deployment tenant create *)",
"Bash(az group delete *)"
]
}
}設計上の要点は4つあります。
- スコープごとに1行ずつ書く。
Bash(az deployment * what-if *)のように*をサブコマンドの位置に置くと、allowルールは起動時に警告の対象になります。ワイルドカードは、what-ifの後ろに置きます。 - ワイルドカードの前の空白は意味を持つ。
what-if *と書けば、引数なしのwhat-ifにも一致します。 - askはallowより優先される。ルールの評価順はdeny、ask、allowの順で、最初に一致したものが結果を決めます。後から「今後確認しない」を選んで広いallowが増えても、askに書いた
createは確認が残ります。 - 書いていないものは確認される。
azは組み込みの読み取り専用コマンドの集合に含まれないため、mgやtenantのwhat-ifを許可に書かなければ、既定で承認プロンプトが出ます。
az group deleteをaskに入れているのは、what-ifの公式手順の後片付けで使うコマンドだからです。サンプルのリソースグループを消す操作が、そのまま本番のグループにも通じてしまうのを防ぎます。
複合コマンドの扱いにも触れておきます。Claude Codeはシェルの区切り記号を解釈し、許可ルールは各サブコマンドが個別に一致する必要があります。askやdenyのルールは、いずれかのサブコマンドが一致すれば効きます。az deployment group what-if ... && az deployment group create ...と1行に繋がれても、createの部分でaskが働きます。
Bashルールは境界の全体ではない
Bashルールは、Claudeが書いたコマンドの文面に対して一致を見ます。同じプログラムを別の形で呼ぶ書き方には一致しません。公式の権限ドキュメントも、denyやaskのルールは「Claudeが普通に出力する形を止めるもの」で、プログラムを囲むセキュリティ境界ではないと書いています。/usr/bin/curlやsh -c '...'の形はルールをすり抜ける例として挙がっています。同じことがazにも言えます。
文面に依存しない強制が必要なら、サンドボックスか、実行前にコマンド全文を自前のロジックで検査するPreToolUse hookを併用します。本番のサブスクリプションに繋がる環境では、Claude Codeに渡す資格情報のスコープ自体も絞っておく方が確実です。
CLAUDE.mdに書く作業手順
権限ルールは「できること」を決めます。「どの順で何をするか」はCLAUDE.mdに書きます。たとえば次のような断片です。
## Bicep / Azure の作業手順
- インフラは `infra/` 以下の Bicep で管理する。ポータルや CLI での手作業変更はしない
- パラメータは `*.bicepparam` に置き、秘密の値はファイルに書かない
- Bicep を変更したら、まず `az deployment group what-if` を実行する
- `--resource-group` はユーザーが指定したものだけを使う
- 差分は Create / Modify / Delete / Deploy に分けて要約し、Delete と Modify は
プロパティ単位で理由を説明する
- `az deployment ... create` は実行しない。what-if の要約を示して承認を待つ
- what-if が評価できない式(`utcNow()` / `newGuid()` / `reference()` /
`listKeys()` など)は、要約に「評価できず未確認」と明記する最後の項目が効く理由は次の節で説明します。what-ifが見せる範囲には限りがあり、「差分が小さい」と「安全」は同じ意味ではありません。
パラメータファイルは、Bicepのパラメータファイル(.bicepparam)でも、JSONのパラメータファイルでも使えます。Azure CLI 2.53.0以降とBicep CLI 0.22.X以降なら、.bicepparamの中のusing文でテンプレートを指定できるため、--template-fileは省略できます。
az deployment group what-if \
--resource-group ExampleGroup \
--parameters main.bicepparamwhat-ifの出力を承認の材料として読む
what-ifの出力は、Claude Codeが要約して人に見せる材料です。要約の元になる出力の癖を知っておくと、承認する側も要約の抜けに気づけます。
公式の例では、仮想ネットワークのBicepから、タグ1つ、サブネット1つを消し、アドレス範囲を10.0.0.0/16から10.0.0.0/15に変えたファイルを、既存のリソースに対してwhat-ifします。出力は次の形です。
Resource and property changes are indicated with these symbols:
- Delete
+ Create
~ Modify
~ Microsoft.Network/virtualNetworks/vnet-001 [2018-10-01]
- tags.Owner: "Team A"
~ properties.addressSpace.addressPrefixes: [
- 0: "10.0.0.0/16"
+ 0: "10.0.0.0/15"
]
~ properties.subnets: [
- 0:
name: "subnet001"
properties.addressPrefix: "10.0.0.0/24"
]
Resource changes: 1 to modify.リソースは1つしか変わらないと表示されます。末尾の行だけを見て承認すると、サブネットの削除を見落とします。承認前に読むのは、リソース数ではなくプロパティの行です。
変更の種類は7つ
what-ifが返す変更の種類は次の7つです。
| 種類 | 意味 |
|---|---|
| Create | 意味存在しないリソースが作られる |
| Delete | 意味完全モードのJSONテンプレートのデプロイでのみ使われる。Bicepファイルにないリソースが消える |
| Ignore | 意味リソースは存在するがBicepファイルにない。デプロイでも変更されない |
| NoChange | 意味リソースは再デプロイされるが、プロパティは変わらない |
| NoEffect | 意味読み取り専用のプロパティで、サービスに無視される |
| Modify | 意味再デプロイされ、プロパティが変わる |
| Deploy | 意味再デプロイされるが、プロパティが変わるかは不明(ResourceIdOnlyのときだけ出る) |
承認の観点では、CreateとModifyに加えて、プロパティ行の-(削除)を拾います。公式の例のタグやサブネットのように、Bicepから外した設定は削除として表示されます。
返却の詳しさは--result-formatで選べます。既定はFullResourcePayloadsで、変わるプロパティの詳細まで返します。ResourceIdOnlyはリソースの一覧だけを返すため、承認の材料には足りません。要約させるときは既定のまま使います。プログラムで処理するなら、--no-pretty-printでJSONを受け取れます。
評価できない式と、ノイズ
次の式はwhat-if中に評価されず、式のまま表示されます。
newGuid()やutcNow()のような非決定的な関数- セキュアパラメータの値への参照
- 同じテンプレートでデプロイされないリソースへの参照
- 同じテンプレートで定義されていないリソースのプロパティへの参照
listKeys()のようなリソース関数
公式の例では、タグにutcNow()を入れると"20250808T200145Z" => "[utcNow()]"のように、式がそのまま差分として出ます。reference関数を使うプロパティも、what-ifは常に「変わる」と報告します。デプロイ時に実際の値が変わるかは、式を解決した後でないと決まらないためです。
もう1つの癖がノイズです。Bicepファイルに書かず、デプロイ時に既定値が自動で入るプロパティが、削除として報告されることがあります。公式も、一部の「削除」は実際には変わらないと説明しています。削除の行を見つけたClaude Codeが、すぐにBicepへ書き足して「解消」しようとしないよう、CLAUDE.mdには「Deleteの行は理由を説明する」と書いておきます。
what-ifが見えない範囲
制限事項も承認の判断に関わります。
- ネストしたテンプレートは、500個、クロスリソースグループのデプロイで800リソースグループ、展開に5分のいずれかに達するまで展開されます。上限に達すると、残りのリソースの変更種別は
Ignoreになります templateLinkの評価と展開には対応していません。ネストしたデプロイの中でtemplateLinkを使って入れたリソースは、テンプレートスペックの参照を含め、結果に出ません- 「ショートサーキット」と呼ばれる状態があります。リソースIDやAPIバージョンをデプロイ前に計算できないとき、そのリソースや、まれにモジュール全体が分析から外れます。Azure CLI 2.75.0以降なら、分析できなかった部分の診断が表示されます。2.74.0のような古い版では、同じ問題が起きても表示されません
Azure CLIが古いと、what-ifの結果が「問題なし」に見えるだけの状態になりえます。CLAUDE.mdにaz --versionの確認を足しておく価値があります。Azure Deployment Stacksを使っている場合も、スタックの変更(切り離される、または削除されるリソースを含む)をwhat-ifでプレビューできます。
つまずきやすい点
ローカルのファイルしか渡せない。Azure CLIは、リモートのBicepファイルのデプロイに対応していません。パラメータファイルもローカルのファイルが必要です。Bicep CLIでJSONテンプレートにビルドして、リモートに置く手順が公式に書かれています。
Azure CLIにBicep CLIが含まれる。Azure CLIは、自己完結したBicep CLIのインスタンスをインストールします。PATHには追加されません。バージョンはaz bicep version、更新はaz bicep upgradeで扱います。手動で入れたBicep CLIを使うときは、az bicepではなくbicepのコマンドを実行します。
リンターの既定は最小限。Bicepのリンターは、Bicep CLIとVS Code拡張に組み込まれていて、構文エラーとベストプラクティス違反を検出します。既定のルールセットはarm-ttkのテストケースを元にした最小限のものです。ルール名の一覧にはno-hardcoded-env-urlsやuse-recent-api-versionsなどが並び、bicepconfig.jsonで有効・無効や適用の仕方を変えられます。Claude Codeに書かせたBicepの品質を揃えるなら、この設定ファイルをリポジトリに置きます。
リソースグループは先に要る。 az deployment group what-ifは、存在するリソースグループに対して実行します。リソースグループが存在しない場合はaz group createで作る必要があります。この作成はAzureへの変更なので、権限ルールに書いていなければ承認が出ます。
承認する側のチェック
createを承認するときに見る点を、表にまとめます。
| 見る点 | 理由 |
|---|---|
Deleteが出ていないか | 理由完全モードのJSONでのみ出る。Bicepでは出ない前提でも、出たら理由を確認する |
-行が実際の削除を指していないか | 理由宣言から外した設定は、実際に消える場合がある。ノイズと見分けて確認する |
Ignoreが出ていないか | 理由展開の上限に達した可能性がある。残りのリソースは評価されていない |
| 式のまま表示された行 | 理由評価されていない。デプロイ時の実際の値は、what-ifでは分からない |
| スコープとリソースグループ名 | 理由--resource-groupと--locationが意図したものか |
| Azure CLIのバージョン | 理由2.75.0未満だと、分析できなかった部分の診断が出ない |
この確認を人が毎回行うのが、askルールの役目です。Claude Codeには、what-ifを何度でも回して差分を小さくさせ、最後に1回だけ承認を求める流れを作らせます。
Azure全般の操作をMCPサーバー経由で行う方法は、Azure MCPサーバーの記事にまとめています。本番環境に向けた権限の考え方はMCPで本番環境に接続する権限設計が扱っています。AWSで同じ線引きを組むなら、SAMのlocal invokeで検証しdeployを承認制にする記事が対応します。