Azure 権限周りの全体像
権限管理構造
ユーザー / アプリ / VM / Functions
↓
Microsoft Entra ID
↓
認証
↓
Azure RBAC
↓
認可
↓
Azure Resource / Data Plane
権限の階層構造
Tenant
↓
Management Group
↓
Subscription
↓
Resource Group
↓
Resource
tenant
最上位。
Azureを契約すると作られる組織そのもの。
イメージ:株式会社A
中身
ユーザー
グループ
認証
MFA
Entra ID
Management Group
Subscriptionをまとめる箱
- 共通ポリシー適用
東京リージョンのみ
Public IP禁止 - 共通RBAC適用
全SubscriptionにReader付与
Subscription
課金単位
イメージ
株式会社A(tenant)
├ 案件A(Manegemnt Group)
└開発環境(Subscription)
└本番環境(Subscription)
└検証環境(Subscription)
├ 案件B(Manegemnt Group)
└開発環境(Subscription)
└本番環境(Subscription)
└検証環境(Subscription)
Resource Group
リソースをまとめる箱
開発環境(Subscription)
├rag-dev-rg(Resource Group)
├ OpenAI(Resource)
├ AI Search(Resource)
├ Storage(Resource)
├ Function(Resource)
└ Key Vault(Resource)
├function-dev-rg(Resource Group)
├ Azure Functions(Resource)
└ S3(Resource)
Resource
AWSのサービス
Azure OpenAI
Storage
VM
AKS
AI Search
Key Vault
Azure Resource Manager
Azureの司令塔
構造
Portal
CLI
Terraform
Bicep
↓
ARM
↓
Azure Resource
役割
RBAC評価
Resource作成
Resource削除
Provider呼び出し
RBAC (Role-Based Access Control)
Roleベースで権限を管理する仕組み
実際の構成は
RBAC
├ Role Definition
└ Role Assignment
├ Principal
├ Role
└ Scope
Role Assignment
実際の権限付与
Principal(付与対象)
+
Role(権限セット)
+
Scope(どこまでの権限か)
RBACとRoleAssignmentの関係
RBAC
├ Role Definitions
│ ├ Reader
│ ├ Contributor
│ └ Owner
│
└ Role Assignments
├ Alice(Principal) → Contributor(Role) → RG-A(Scope)
├ Bob(Principal) → Reader(Role) → Subscription(Scope)
└ SP(Principal) → Owner(Role) → RG-B(Scope)
実行順序
1. Role Assignment検索
↓
2. AliceはContributor
↓
3. ContributorのRole Definition取得
↓
4. VM作成権限あり
↓
5. 実行
ARMとRBACとRole Assignmentの関係
Azure CLI
↓
ARM
↓
RBAC確認
↓
Role Assignment検索
↓
許可なら実行
↓
VM作成
Azure Policy
Role内で大きく分けられたOwner/Contributor/Readerに対して、細かい権限を付与する
Role = 権限
Policy = 制約
ARMとRBACとRole AssignmentとAzure Policyの関係
Azure CLI
↓
ARM
↓
RBAC確認
↓
Role Assignment検索
↓
許可
↓
Policy確認
↓
ルール違反なし
↓
VM作成
Role Assignmentの中身
Principal
- 権限を与える対象
User
Group
Service Principal
Managed Identity
Service Principal
アプリ用アカウント
- 用途
Terraform
GitHub Actions
Python
CI/CD
Managed Identity
Azureリソース専用IAM
Function App
↓
Managed Identity
↓
Storageアクセス
Role
- Reader
閲覧のみ - Contributor
作成
更新
削除 - Owner
作成
更新
削除
権限付与 - User Access Administrator
RBAC付与
RBAC削除
専用。
Scope
RBACの適用範囲
イメージ
誰に
何を
どこまで
の
どこまで
部分。
Provider
サービスのAPI名前空間
AI SearchはMicrosoft.Searchの配下
OpenAIはMicrosoft.CognitiveServicesの配下
他にも
Microsoft.Storage
Microsoft.Compute
Microsoft.Web
Microsoft.KeyVault
ARMとRBACとRole AssignmentとAzure PolicyとProviderの関係
Azure CLI
↓
ARM
↓
RBAC確認
↓
Role Assignment検索
↓
許可
↓
Policy確認
↓
ルール違反なし
↓
Provider(Microsoft.Compute)
↓
VM作成
EntraID
Azureにおける「認証」の中心です。
誰がログインするか
誰がアプリとして動くか
どのテナントに属するか
MFAを使うか
条件付きアクセスを使うか
Key Vault
秘密情報管理サービス。
保存対象
APIキー
DBパスワード
証明書
暗号鍵
接続文字列
Management Plane(管理プレーン)
リソースを管理するAPI
Storage作成
Storage削除
OpenAI作成
AI Search作成
操作元
Portal
CLI
Terraform
ARM
Data Plane(データプレーン)
実データ操作
例1 Storage
- 管理プレーン
Storage作成 - データプレーン
Blob読む
Blob書く
例2 OpenAI
- 管理プレーン
OpenAI作成 - データプレーン
client.chat.completions.create()
Conditional Access
ログイン制御。
RBACとは別。
例
MFA必須
日本国外禁止
管理者は社給PC限定
危険サインイン遮断
理解
Conditional Access
=
ログイン可能か
RBAC
=
ログイン後に何ができるか
PIM(Privileged Identity Management)
-
目的
管理者権限を常時持たせない。 -
例
普段 Reader
必要時だけ
Owner
Contributor
User Access Administrator
を有効化。
実践
構成図
Tenant (Entra ID)
│
├─ Human Principal(人間)
│ │
│ ├─ Developers Group
│ │ └─ Alice
│ │
│ └─ Operators Group
│ └─ Bob
│
├─ Application Principal(アプリ)
│ │
│ └─ Service Principal
│ └─ Terraform / GitHub Actions
│
└─ Resource Principal(Azureリソース)
│
└─ Function Managed Identity
└─ Function App専用ID
Management Group
│
└─ Azure Policy
├─ Japan Eastのみ
└─ Public IP禁止
Subscription
│
└─ azure-rbac-lab-rg
│
├─ Storage Account
│
└─ Function App
│
└─ Managed Identity
──────────────────────────────────────────
RBAC
──────────────────────────────────────────
Role Assignments
│
├─ Human Principal
│ │
│ ├─ Developers
│ │ └─ Reader
│ │ └─ Resource Group
│ │
│ └─ Operators
│ └─ Contributor
│ └─ Resource Group
│
├─ Application Principal
│ │
│ └─ Service Principal
│ └─ Contributor
│ └─ Resource Group
│
└─ Resource Principal
│
└─ Function Managed Identity
└─ Storage Blob Data Contributor
└─ Storage Account
──────────────────────────────────────────
Management Plane
──────────────────────────────────────────
Alice/Bob/Service Principal
Storage Account作成
Storage Account削除
Function App作成
Function App削除
──────────────────────────────────────────
Data Plane
──────────────────────────────────────────
Function App
│
▼
Managed Identity
│
Storage Blob Data Contributor
│
Storage Account
│
Blob読込
Blob書込
Blob削除
──────────────────────────────────────────
実行結果
──────────────────────────────────────────
Alice
↓
Developers
↓
Reader
↓
Storage作成失敗
Bob
↓
Operators
↓
Contributor
↓
Storage作成成功
az cli
↓
Service Principal
↓
Contributor
↓
Storage作成成功
Function App
↓
Managed Identity
↓
Storage Blob Data Contributor
↓
Blob読込/書込成功
共通変数
az login
TENANT_ID=$(az account show --query tenantId -o tsv)
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
MGMT_GROUP_ID="test-manager-group"
RG_NAME="test-resource-group"
LOCATION="japaneast"
STORAGE_ACCOUNT="teststorage$RANDOM"
FUNCTION_APP="test-func-$RANDOM"
AliceとBob
# Developers グループ作成
# Microsoft Entra ID(旧 Azure AD)にグループを作成するコマンド
# Display Name
# Mail Nickname
# Object ID
# を持っており、Display Nameは重複可能だが、Mail Nicknameは一意のものを指定しなければならない
az ad group create \
--display-name "Developers" \
--mail-nickname "developers"
DEVELOPERS_GROUP_ID=$(az ad group show --group "Developers" --query id -o tsv)
# Operators グループ作成
az ad group create \
--display-name "Operators" \
--mail-nickname "operators"
OPERATORS_GROUP_ID=$(az ad group show --group "Operators" --query id -o tsv)
# domain取得
DOMAIN=$(az rest \
--method GET \
--url "https://graph.microsoft.com/v1.0/domains" \
--query "value[?isDefault==\`true\`].id | [0]" \
-o tsv)
# Alice ユーザー作成
az ad user create \
--display-name "Alice" \
--user-principal-name "alice@$DOMAIN" \
--password "P@ssw0rd1234!" \
--force-change-password-next-sign-in true
ALICE_ID=$(az ad user show --id "alice@$DOMAIN" --query id -o tsv)
# Bob ユーザー作成
az ad user create \
--display-name "Bob" \
--user-principal-name "bob@$DOMAIN" \
--password "P@ssw0rd1234!" \
--force-change-password-next-sign-in true
BOB_ID=$(az ad user show --id "bob@$DOMAIN" --query id -o tsv)
# グループへメンバー追加
az ad group member add --group "Developers" --member-id $ALICE_ID
az ad group member add --group "Operators" --member-id $BOB_ID
Service Principal 作成(Application Principal)
# Azure Resource Group(リソースグループ)を作成するコマンド
az group create --name $RG_NAME --location $LOCATION
# Terraform/GitHub Actions 用 Service Principal(アプリ用のAzureログインID を作っています)
az ad sp create-for-rbac \
--name "sp-terraform-github-actions" \
--role "Contributor" \
--scopes "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME" \
--years 1
# 出力される appId / password / tenant を控える
SP_APP_ID="<出力されたappId>"
SP_OBJECT_ID=$(az ad sp show --id $SP_APP_ID --query id -o tsv)
Management Group と Azure Policy 設定
# Management Group 作成
az account management-group create \
--name $MGMT_GROUP_ID \
--display-name "Azure RBAC Lab MG"
# サブスクリプションをMGに紐付け
az account management-group subscription add \
--name $MGMT_GROUP_ID \
--subscription $SUBSCRIPTION_ID
# ポリシー1: Japan East のみ許可(許可されたロケーション)
az policy assignment create \
--name "allowed-japaneast" \
--display-name "Allowed Locations - Japan East Only" \
--scope "/providers/Microsoft.Management/managementGroups/$MGMT_GROUP_ID" \
--policy "e56962a6-4747-49cd-b67b-bf8b01975c4c" \
--params '{ "listOfAllowedLocations": { "value": ["japaneast"] } }'
# ポリシー2: パブリックIP禁止(Public IP の作成を拒否)
az policy assignment create \
--name "deny-public-ip" \
--display-name "Deny Public IP Address Creation" \
--scope "/providers/Microsoft.Management/managementGroups/$MGMT_GROUP_ID" \
--policy "83a86a26-fd1f-447c-b59d-e51f44264114"
*e56962a6-4747-49cd-b67b-bf8b01975c4cや83a86a26-fd1f-447c-b59d-e51f44264114 は組み込みの "Public IP addresses should be reused" 等、環境によっては該当するDeny系ビルトインポリシー(例: "Network interfaces should not have public IPs")のIDに置き換えてください。完全な禁止には Deny effect を持つカスタムポリシーまたは適切な組み込みポリシー定義IDを選定する必要があります。
RBAC ロール割り当て(Human / Application(Service) Principal)
# Developers グループ → Reader (Resource Group)
az role assignment create \
--assignee-object-id $DEVELOPERS_GROUP_ID \
--assignee-principal-type Group \
--role "Reader" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME"
# Operators グループ → Contributor (Resource Group)
az role assignment create \
--assignee-object-id $OPERATORS_GROUP_ID \
--assignee-principal-type Group \
--role "Contributor" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME"
# Service Principal → Contributor (Resource Group)
# ※ create-for-rbac 時に --scopes で割り当て済みの場合は不要
az role assignment create \
--assignee-object-id $SP_OBJECT_ID \
--assignee-principal-type ServicePrincipal \
--role "Contributor" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME"
結果確認
結果確認の前に重要なコマンドを実行
- 下記を実行しておかないと、Storage作成の際、SubscriptionNotFoundエラーが返る
az provider register --namespace Microsoft.Storage
az provider register --namespace Microsoft.Web
- 理由
新しく作成されたサブスクリプション、あるいは特定の操作(VM作成、Storage作成など)を一度も行っていないサブスクリプションでは、よく使うプロバイダー(Microsoft.Compute, Microsoft.Storage, Microsoft.Networkなど)は通常Azure Portal経由の操作で自動登録されますが、CLIだけで操作している環境やクリーンな新規サブスクリプションでは未登録のままということがあります。なぜエラーメッセージが SubscriptionNotFound になるのか(誤解しやすい部分)
本来、Azureが正確にエラーを返すなら、MissingSubscriptionRegistration
「このサブスクリプションでMicrosoft.Storageは登録されていません」という分かりやすいエラーになるべきです。しかし、Microsoft.Storage の checkNameAvailability という特定のAPI(ストレージアカウント名が使用可能か事前確認するAPI)については、APIの実装やバージョンによって、プロバイダー未登録時に「サブスクリプションそのものが見つからない」という、より汎用的な404エラー(SubscriptionNotFound)を返してしまうことがある、というのが今回のケースです。これは「本当はサブスクリプションが存在しない」のではなく、「Microsoft.Storage機能の文脈において、このサブスクリプションは認識されていません」という意味合いの404であり、エラーメッセージとしては不正確・誤解を招くエラーが返ってくる
Alice失敗
# Alice として az login
az login
# ブラウザでAliceのメアドとパスワードでログイン
# Storage Account 作成試行 → Reader 権限のため失敗
az storage account create \
--name $STORAGE_ACCOUNT \
--resource-group $RG_NAME \
--location $LOCATION \
--sku Standard_LRS
# => エラー: AuthorizationFailed (Reader権限のため作成不可)
Bob 成功
# Bob として az login
az login
# ブラウザでAliceのメアドとパスワードでログイン
# Storage Account 作成 → 成功
az storage account create \
--name $STORAGE_ACCOUNT \
--resource-group $RG_NAME \
--location $LOCATION \
--sku Standard_LRS \
service principal 成功
# Service Principal でログイン
az login --service-principal \
-u $SP_APP_ID \
-p "rMQ8Q~pSlt13P19eAriOWTNHW2WroGL6Eft3wcWN" \
--tenant $TENANT_ID
# Storage Account 作成 → 成功
az storage account create \
--name $STORAGE_ACCOUNT \
--resource-group $RG_NAME \
--location $LOCATION \
--sku Standard_LRS \
--allow-blob-public-access false
# (今回はいらない)Function Appを動かすための料金プラン/実行基盤作成
az functionapp plan create \
--name $APP_PLAN_NAME \
--resource-group $RG_NAME \
--location $LOCATION \
--sku B1 \
--is-linux
# (今回はいらない)実際の Function App 本体
az functionapp create \
--name $FUNCTION_APP \
--resource-group $RG_NAME \
--plan $APP_PLAN_NAME \
--storage-account $STORAGE_ACCOUNT \
--runtime python \
--runtime-version 3.11 \
--functions-version 4 \
--os-type Linux
# Flex Consumptionは直接作る
az functionapp create \
--resource-group $RG_NAME \
--name $FUNCTION_APP \
--storage-account $STORAGE_ACCOUNT \
--flexconsumption-location $LOCATION \
--runtime python \
--runtime-version 3.11
Managed Identity の設定と結果確認
Function App
│
└─ Managed Identity
│
└─ Storage Blob Data Contributor
│
└─ Storage Account
設定
# Managed Identity を有効化Function App専用のService Principalを自動作成
az functionapp identity assign \
--name $FUNCTION_APP \
--resource-group $RG_NAME
# Principal ID取得
FUNC_PRINCIPAL_ID=$(az functionapp identity show \
--name $FUNCTION_APP \
--resource-group $RG_NAME \
--query principalId -o tsv)
# Storage AccountのResource ID取得
STORAGE_ACCOUNT_ID=$(az storage account show \
--name $STORAGE_ACCOUNT \
--resource-group $RG_NAME \
--query id -o tsv)
# Storage権限付与
az role assignment create \
--assignee-object-id $FUNC_PRINCIPAL_ID \
--assignee-principal-type ServicePrincipal \
--role "Storage Blob Data Contributor" \
--scope $STORAGE_ACCOUNT_ID
結果
下記のコードを対象のFunctionにデプロイしてブラウザアクセスで確認する
from azure.identity import ManagedIdentityCredential
from azure.storage.blob import BlobServiceClient
account_name = "<STORAGE_ACCOUNT>"
credential = ManagedIdentityCredential()
blob_service_client = BlobServiceClient(
account_url=f"https://{account_name}.blob.core.windows.net",
credential=credential
)
containers = list(blob_service_client.list_containers())
return {
"container_count": len(containers)
}
mkdir mi-storage-test
cd mi-storage-test
cat > requirements.txt << 'EOF'
azure-functions
azure-identity
azure-storage-blob
EOF
cat > host.json << 'EOF'
{
"version": "2.0"
}
EOF
cat > function_app.py << EOF
import os
import azure.functions as func
from azure.identity import ManagedIdentityCredential
from azure.storage.blob import BlobServiceClient
app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)
@app.route(route="mi-storage-test")
def mi_storage_test(req: func.HttpRequest) -> func.HttpResponse:
account_name = os.environ["STORAGE_ACCOUNT_NAME"]
credential = ManagedIdentityCredential()
client = BlobServiceClient(
account_url=f"https://{account_name}.blob.core.windows.net",
credential=credential
)
containers = [c["name"] for c in client.list_containers()]
return func.HttpResponse(
f"Managed Identity OK. containers={containers}",
status_code=200
)
EOF
zip -r app.zip .
# Function Appに環境変数を設定
az functionapp config appsettings set \
--name $FUNCTION_APP \
--resource-group $RG_NAME \
--settings STORAGE_ACCOUNT_NAME=$STORAGE_ACCOUNT
# CLIでデプロイ
az functionapp deployment source config-zip \
--name $FUNCTION_APP \
--resource-group $RG_NAME \
--src app.zip
# URLを取得して実行確認
az functionapp function show \
--name $FUNCTION_APP \
--resource-group $RG_NAME \
--function-name mi_storage_test \
--query invokeUrlTemplate -o tsv
curl "https://$FUNCTION_APP.azurewebsites.net/api/mi-storage-test"
