Claude CodeでAzure RBACの最小権限を作る — OwnerとContributorの置き換え
az role assignment listでOwnerとContributorの割り当てを洗い出し、スコープを狭めて、必要な操作だけのカスタムロールへ置き換える流れ。Claude Codeに読み取りだけを許可する設定も示します。
サブスクリプション全体にOwnerやContributorが付いたままのサービスプリンシパルは、棚卸しの最初の標的になります。ただ、何を使っているか分からないまま外すと、デプロイが止まります。Claude Codeに任せられるのは、割り当ての一覧を読み、役割ごとに「スコープを狭める」「組み込みロールに替える」「カスタムロールを作る」のどれが合うかを下書きさせるところまでです。
この記事は、az role assignment listの出力から置き換え案を作らせる手順と、Claude Code側に許可する操作の範囲を扱います。実行して割り当てを変えるのは人間、という分担で組みます。
OwnerとContributorは何が違うのか
置き換えの前に、2つのロールの中身を押さえます。どちらも許可する操作は*、つまり全リソースの全操作です。違いは除外のほうにあります。
| ロール | Actions | NotActions |
|---|---|---|
| Owner | Actions* | NotActionsなし |
| Contributor | Actions* | NotActionsMicrosoft.Authorization/*/Write、Microsoft.Authorization/*/Delete、elevateAccess、サブスクリプションの取り消し・再有効化など11件 |
Contributorは「ほぼ全部できるが、ロールの割り当てと一部の管理操作だけはできない」ロールです。したがってOwnerをContributorに替えるだけでは、最小権限にはなりません。削れたのは権限の付与だけで、リソースの作成と削除は全種類に残ります。
置き換えの発想は2段です。
- スコープを狭める。サブスクリプション全体への割り当てを、必要なリソースグループへ移す
- ロールを絞る。
*を、必要な操作だけを並べたロールに替える
1は同じロールのまま割り当て先を変えるだけなので、影響の見積もりが簡単です。先にこちらを済ませると、2で扱う対象が減ります。
Claude Codeに許可する操作を決める
割り当ての一覧を読むのは安全な作業ですが、同じazコマンドで割り当ての作成や削除もできてしまいます。そこで、読み取り系だけを許可し、書き込み系を拒否します。ルールは拒否、確認、許可の順に評価され、最初に一致したものが結果になります。
{
"permissions": {
"allow": [
"Bash(az role assignment list *)",
"Bash(az role definition list *)",
"Bash(az monitor activity-log list *)",
"Bash(az account show)"
],
"deny": [
"Bash(az role assignment create *)",
"Bash(az role assignment delete *)",
"Bash(az role definition create *)",
"Bash(az role definition update *)",
"Bash(az role definition delete *)"
]
}
}許可ルールがなければ、azコマンドは確認が出る扱いになります。末尾の*の前にあるスペースは、ルールの一部です。az role assignment list *はサブコマンドで範囲を限っているので、az role assignment createには一致しません。
この設定には限界があります。Bashのルールは、Claudeが書いたコマンドの文字列に一致するかどうかで判定します。/usr/local/bin/az role assignment deleteのようにパスを付けた形や、bash -cで包んだ形は、拒否ルールに一致しません。拒否ルールは、Claudeが通常書く形を止めるもので、セキュリティの境界ではありません。
境界は、Azure側に作ります。調査用のセッションには、Readerのような読み取り専用のロールを持つ資格情報だけを渡してください。書き込み権限のないアカウントでは、拒否ルールをすり抜けても、実行時にAzureが断ります。
パイプでつないだコマンドは、すべての部分が許可されていないと確認が出ます。jqなどを後ろに付けず、--query(JMESPath)で絞る書き方に揃えると、確認の回数が減ります。
手順1: 広い割り当てを一覧にする
最初に、Ownerなどが付いている割り当てを全部拾います。az role assignment listは、既定では現在のサブスクリプションの割り当てだけを返します。サブスクリプションとその配下も見るには--all、継承された割り当ても見るには--include-inheritedを付けます。
az role assignment list --all --output json \
--query "[?roleDefinitionName=='Owner' || roleDefinitionName=='Contributor'].{principalName:principalName, roleDefinitionName:roleDefinitionName, scope:scope}"--queryの条件は一例で、用途に合わせて変えます。出力の項目principalName、roleDefinitionName、scopeは、Azureのドキュメントの例と同じ並びです。
この出力をファイルに保存させ、次のように指示します。
assignments.json を読み、スコープごとに次の表を作って。
- 割り当て先(ユーザー / グループ / サービスプリンシパル)の別が名前から読み取れるか
- スコープがサブスクリプション直下か、リソースグループか、リソースか
- 同じロール・同じスコープで複数の主体に付いている割り当て
推測で用途を断定せず、判断材料が足りない行は「要確認」と書くこと。「要確認」を許す指示がないと、名前から用途を推測して埋めた表が返ってきます。サービスプリンシパル名から用途が読めない行は、持ち主に聞くしかありません。
手順2: 実際に使われた操作を調べる
置き換え先のロールを決める材料は、その主体が実際に何をしているかです。Azureのアクティビティログは、リソースの作成、更新、削除のような変更系の操作を記録します。読み取り操作は通常、記録されません。保持期間は90日で、それより古いイベントは消えます。
az monitor activity-log list --offset 90d --output json > activity.json--offsetは、現在から遡る期間の指定です。
ここで次の3点を、Claudeへの指示に含めてください。
- ログに出ない操作は「使われていない」ではなく「見えていない」。読み取りは、広いまま残すか、別の方法で確かめる
- 90日に1回しか動かないジョブの操作は、期間に入っていない可能性がある
- 鍵やデータベースの中身へのアクセスのようなデータプレーンの操作は、アクティビティログではなくリソースログの領域で、診断設定を入れていなければ残らない
ログの出力から主体ごとの操作名を集計する作業はClaudeに向いています。ただし、集計結果は「過去90日に変更系の操作として見えたもの」の一覧で、必要な権限の全体ではありません。
手順3: 置き換え先を決める
ここからが判断です。下の順に、上のほうが優先です。
| 優先 | 置き換え先 | 向く場面 |
|---|---|---|
| 1 | 置き換え先同じロールで、スコープを狭める | 向く場面対象のリソースグループが決まっている |
| 2 | 置き換え先職務に合った組み込みロール | 向く場面操作が1つのサービスに収まる(例: Virtual Machine Contributor) |
| 3 | 置き換え先カスタムロール | 向く場面組み込みでは広すぎるか、狭すぎる |
特権管理者ロールについて、Azureのベストプラクティスは次のように述べています。職務ロールで足りるなら特権ロールを割り当てない。どうしても必要なら、管理グループやサブスクリプションではなく、リソースグループやリソースのような狭いスコープに付ける。サブスクリプションのOwnerは最大3人に抑える。
ロールの割り当て自体を担当する人は、別の話です。割り当てを作る権限を持つロールを与えるなら、条件を付けて割り当てられるロールの範囲を絞ることが勧められています。
手順4: カスタムロールの定義を書かせる
新しく作る前に、既存のカスタムロールを一覧させます。同じ目的のロールがすでにあれば、作り足すより再利用したほうが、テナントあたりの上限にも余裕が残ります。
az role definition list --custom-role-only true --output json \
--query "[].{roleName:roleName, roleType:roleType}"組み込みロールの中身は、az role definition list --name "Contributor"のように名前を指定して読めます。除外の一覧が必要なときは、この出力のpermissionsを見ます。
組み込みロールで足りない場合は、カスタムロールのJSONを書かせます。次はAzureのドキュメントにある仮想マシン運用者の例に沿った形です。Actionsに操作を1件ずつ書き、AssignableScopesに使えるスコープを書きます。
{
"Name": "Virtual Machine Operator",
"IsCustom": true,
"Description": "Can monitor and restart virtual machines.",
"Actions": [
"Microsoft.Storage/*/read",
"Microsoft.Network/*/read",
"Microsoft.Compute/*/read",
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/restart/action",
"Microsoft.Authorization/*/read",
"Microsoft.Resources/subscriptions/resourceGroups/read"
],
"NotActions": [],
"AssignableScopes": [
"/subscriptions/{subscriptionId1}"
]
}Claudeに渡す条件は、次の点です。
Actionsにワイルドカードで全部を許可する書き方を使わない。将来追加される操作まで自動で含まれてしまう。上の例の*/readのような読み取りの束は許容しても、書き込み側は1件ずつ書く- 1つの操作名にワイルドカードは1つまで
AssignableScopesにルート(/)やワイルドカードは使えない。管理グループは1つしか書けないDataActionsを含むロールは、管理グループのスコープには割り当てられない
操作名はClaudeが記憶で書くと綴りを誤りやすい部分です。登録前に、人間がリソースプロバイダーの操作一覧と照らして確かめてください。
作成するaz role definition createは、拒否ルールの対象にしてあります。定義ファイルを読んで確認し、人間が自分の端末で実行します。
数の制約を先に知っておく
置き換えの結果として、ロールや割り当ての数が増えます。上限があるので、先に把握しておきます。
| 項目 | 上限 | 備考 |
|---|---|---|
| カスタムロール | 上限1テナントあたり5,000件 | 備考21Vianet運営のAzureでは2,000件 |
| ロール割り当て | 上限1サブスクリプションあたり5,000件 | 備考管理グループのスコープは数えない。引き上げ不可 |
| カスタムロールのAssignableScopes | 上限2,000件まで | 備考 |
割り当てを主体ごとに増やすと、5,000件に近づきます。ユーザーに直接付けず、グループに付けるのが定石です。同じロールを同じスコープで複数の主体に付けている行は、手順1のClaudeへの指示で拾わせた項目なので、グループ化の候補になります。
外す前の安全な順序
差し替えで本番が止まる事故を避ける順序です。
割り当てを入れ替える順序
- 1
新しいロールを先に付ける
広い割り当ては残したまま、置き換え先を追加します。
- 2
実際に動かす
デプロイやバッチなど、その主体が担う処理を一通り走らせます。
- 3
失敗を拾う
権限不足の失敗が出たら、アクティビティログで拒否された操作を確認し、ロールに足します。
- 4
広い割り当てを外す
問題がなければ、
az role assignment deleteで外します。人間が実行します。
3段目でロールに足すのは、実際に失敗した操作だけにします。「念のため」で足し始めると、元のContributorに近づいていきます。
Claude Code側の指示を残す
毎回同じ指示を書かなくて済むよう、リポジトリのCLAUDE.mdに方針を置きます。
## Azure RBACの棚卸し
- az は読み取り系(list / show)だけ実行する。create / update / delete は提案のみ
- 提案は「割り当て先 / 現在のロールとスコープ / 提案 / 根拠 / 要確認」の表で出す
- 用途を推測で断定しない。判断材料が無い行は「要確認」と書く
- カスタムロールの Actions にワイルドカードで全許可を書かない
- 外す順序は、新ロール追加、動作確認、旧ロール削除Azureにデプロイするコードを書かせる側の権限設計は、Bicepとwhat-ifで承認の境界を作る記事に詳しくあります。AWS側で同じ棚卸しを行う場合は、IAMの最小権限をAccess Analyzerから作る手順が比較になります。どちらも、生成された案を人間が点検してから適用する前提は同じです。
まとめ
OwnerとContributorの置き換えは、スコープを狭める作業から始めると、影響が見積もりやすくなります。Claude Codeにはaz role assignment listとaz role definition listの読み取りだけを許可し、作成と削除は人間が行います。
置き換え案の根拠になるアクティビティログは、変更系が中心で、90日で消えます。ログに出ない権限が残るため、新しいロールを先に付けて並走させ、動作を確かめてから広い割り当てを外す順序で進めると、本番を止めずに済みます。
恒常的なOwnerを減らす別の手段として、Microsoft Entra Privileged Identity Management(PIM)があります。特権の保有時間を絞り、利用状況をレポートやアラートで追えるようにする仕組みです。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。
- code.claude.com/docs/en/permissions
- learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-lis…
- learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privi…
- learn.microsoft.com/en-us/azure/role-based-access-control/custom-roles
- learn.microsoft.com/en-us/azure/role-based-access-control/custom-roles-cli
- learn.microsoft.com/en-us/azure/role-based-access-control/best-practices
- learn.microsoft.com/en-us/azure/azure-monitor/essentials/activity-log