Home / Azure

AzureRunbookAdvanced

Migrating Azure Key Vault from access policies to RBAC without a 403 outage

How to move a Key Vault from the access policy model to Azure RBAC safely - inventory every access policy, map it to built-in roles, pre-create role assignments, switch, test and roll back if apps get 403 Forbidden.

Deepak Bhardwaj5 min read

Applies toAzure Key Vault (Standard/Premium), Azure RBAC, Azure CLI 2.x

What goes wrong

Someone flips the vault's Permission model from Vault access policy to Azure role-based access control. Within minutes:

  • App Service shows Key Vault references as unresolved.
  • Pipelines fail with Forbidden ... Caller is not authorized to perform action on resource ... Action: 'Microsoft.KeyVault/vaults/secrets/getSecret/action'.
  • Disk encryption sets, Application Gateway or SQL TDE with customer-managed keys can't reach their key.

Access policies are ignored once RBAC is on. Anyone who only had an access policy has lost access.

Step 1: inventory the access policies

az keyvault show -n kv-prod-01 --query "properties.accessPolicies[].{objectId:objectId, secrets:permissions.secrets, keys:permissions.keys, certs:permissions.certificates}" -o json > kv-prod-01-policies.json

Resolve each object ID to a name so you know what it is:

for id in $(jq -r '.[].objectId' kv-prod-01-policies.json); do
  az ad sp show --id "$id" --query "{name:displayName,type:servicePrincipalType}" -o tsv 2>/dev/null \
    || az ad user show --id "$id" --query userPrincipalName -o tsv 2>/dev/null \
    || az ad group show --group "$id" --query displayName -o tsv 2>/dev/null \
    || echo "$id: not found (stale policy)"
done

Stale object IDs (deleted identities) can be dropped.

Step 2: map permissions to roles

Access policy permissions Built-in role
Secrets: Get, List Key Vault Secrets User
Secrets: all Key Vault Secrets Officer
Keys: Get, WrapKey, UnwrapKey Key Vault Crypto Service Encryption User
Keys: Sign, Verify, Encrypt, Decrypt Key Vault Crypto User
Keys: all Key Vault Crypto Officer
Certificates: all Key Vault Certificates Officer
Get on secrets for a certificate's private key Key Vault Secrets User (certificates are backed by secrets)
Everything (admins) Key Vault Administrator
List only (portal browsing) Key Vault Reader (metadata only, no values)

Grant at the vault scope, not the resource group, unless you really mean every vault in it. For high-value secrets you can grant on a single secret (.../vaults/kv-prod-01/secrets/db-password).

Step 3: create the role assignments before switching

Role assignments can be created while the vault is still on access policies. They have no effect yet, so this is safe.

KV_ID=$(az keyvault show -n kv-prod-01 --query id -o tsv)

az role assignment create --assignee-object-id <app-mi-object-id> \
  --assignee-principal-type ServicePrincipal \
  --role "Key Vault Secrets User" --scope "$KV_ID"

az role assignment create --assignee-object-id <des-identity-object-id> \
  --assignee-principal-type ServicePrincipal \
  --role "Key Vault Crypto Service Encryption User" --scope "$KV_ID"

Check the result:

az role assignment list --scope "$KV_ID" --query "[].{who:principalName,role:roleDefinitionName}" -o table

Tip

RBAC changes can take several minutes to take effect. Create the assignments the day before the switch, not seconds before.

Step 4: switch the permission model

az keyvault update -n kv-prod-01 --enable-rbac-authorization true

In recent API versions, changing the permission model needs a role that can write role assignments (Owner or User Access Administrator), not just Key Vault Contributor. Use PIM to get it for the change.

Step 5: test every consumer

# As the app identity (or from the app's Kudu/console)
az keyvault secret show --vault-name kv-prod-01 -n db-password --query id -o tsv
  • App Service: Configuration blade, Key Vault references should show a green tick. Restart the app to force a refresh.
  • Disk encryption set / CMK services: check the resource's health and the vault's diagnostic logs.

Watch for denials in the vault logs:

AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT" and TimeGenerated > ago(1h)
| where httpStatusCode_d == 403
| summarize count() by identity_claim_oid_g, OperationName, bin(TimeGenerated, 5m)

The object ID tells you exactly which identity is missing a role.

Rollback

az keyvault update -n kv-prod-01 --enable-rbac-authorization false

The access policies stay stored on the vault while RBAC is on, so switching back restores them. Confirm with az keyvault show --query properties.accessPolicies before you rely on it.

Common 403 causes after the switch

Symptom Cause Fix
App gets 403 on getSecret Only had an access policy Secrets User on the vault
Portal shows "You are unauthorized to view these contents" Key Vault Reader or Contributor only Contributor is control plane only; add a data-plane role
CMK disk or storage fails to unwrap Crypto role missing for the service identity Crypto Service Encryption User
ARM template reference() to a secret fails Deploying identity lacks deploy action or the vault lacks enabledForTemplateDeployment Check both on the vault
Still 403 after granting Propagation delay, or the vault firewall Wait and retry; check networkAcls

Prevent it next time

  • Create new vaults with RBAC from day one (--enable-rbac-authorization true) and enforce it with Azure Policy.
  • Grant roles to groups or managed identities, never to individual users.
  • Related: App Service Key Vault reference not resolved.