Claude Media
Claude CodeでAzure RBACの最小権限を作る — OwnerとContributorの置き換え

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つのロールの中身を押さえます。どちらも許可する操作は*、つまり全リソースの全操作です。違いは除外のほうにあります。

ロールActionsNotActions
OwnerActions*NotActionsなし
ContributorActions*NotActionsMicrosoft.Authorization/*/Write、Microsoft.Authorization/*/Delete、elevateAccess、サブスクリプションの取り消し・再有効化など11件

Contributorは「ほぼ全部できるが、ロールの割り当てと一部の管理操作だけはできない」ロールです。したがってOwnerをContributorに替えるだけでは、最小権限にはなりません。削れたのは権限の付与だけで、リソースの作成と削除は全種類に残ります。

置き換えの発想は2段です。

  1. スコープを狭める。サブスクリプション全体への割り当てを、必要なリソースグループへ移す
  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. 1

    新しいロールを先に付ける

    広い割り当ては残したまま、置き換え先を追加します。

  2. 2

    実際に動かす

    デプロイやバッチなど、その主体が担う処理を一通り走らせます。

  3. 3

    失敗を拾う

    権限不足の失敗が出たら、アクティビティログで拒否された操作を確認し、ロールに足します。

  4. 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)があります。特権の保有時間を絞り、利用状況をレポートやアラートで追えるようにする仕組みです。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/permissions
  2. learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-lis…
  3. learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privi…
  4. learn.microsoft.com/en-us/azure/role-based-access-control/custom-roles
  5. learn.microsoft.com/en-us/azure/role-based-access-control/custom-roles-cli
  6. learn.microsoft.com/en-us/azure/role-based-access-control/best-practices
  7. learn.microsoft.com/en-us/azure/azure-monitor/essentials/activity-log
この記事を共有:XはてブLinkedIn