Claude Media
Claude CodeでBicepを書きAzureにデプロイ — what-ifを承認の境界に

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-ifAzureへの影響なし(予測のみ)権限の扱いallow
反映する主なコマンドaz deployment group createAzureへの影響あり権限の扱い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. スコープごとに1行ずつ書く。 Bash(az deployment * what-if *)のように*をサブコマンドの位置に置くと、allowルールは起動時に警告の対象になります。ワイルドカードは、what-ifの後ろに置きます。
  2. ワイルドカードの前の空白は意味を持つ。 what-if *と書けば、引数なしのwhat-ifにも一致します。
  3. askはallowより優先される。ルールの評価順はdeny、ask、allowの順で、最初に一致したものが結果を決めます。後から「今後確認しない」を選んで広いallowが増えても、askに書いたcreateは確認が残ります。
  4. 書いていないものは確認される。 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.bicepparam

what-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を承認制にする記事が対応します。

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