Blog Field Notes Azure Key Vault 403: Application ID Is Not the Object ID
Debug #azure#keyvault#terraform#aks#identity

Azure Key Vault 403: Application ID Is Not the Object ID

The backend returned a 403 Forbidden on every Key Vault operation. The access policy had the wrong ID — Application ID instead of Service Principal Object ID.

· Gideon Warui
ON THIS PAGE

After deploying the <service> backend to AKS, every attempt to read or write a secret in Azure Key Vault returned a 403. The access policy was configured. The managed identity existed. The vault was reachable. The error was specific enough to diagnose, but the cause wasn’t obvious from the Terraform side.


The error

{
  "name": "RestError",
  "code": "Forbidden",
  "statusCode": 403,
  "message": "The user, group or application 'appid=31a7e031-4c77-xxxx-xxxx-xxxxxxxxxxxx;oid=a34a2d97-ddbc-xxxx-xxxx-xxxxxxxxxxxx' does not have secrets set permission on key vault '<kv-name>'"
}

The error message contains two IDs:

  • appid=31a7e031-4c77-xxxx-xxxx-xxxxxxxxxxxx — the Application (client) ID
  • oid=a34a2d97-ddbc-xxxx-xxxx-xxxxxxxxxxxx — the Object ID of the service principal

The access policy in Terraform was using the Application ID:

resource "azurerm_key_vault_access_policy" "app" {
  key_vault_id = azurerm_key_vault.main.id
  tenant_id    = var.tenant_id
  object_id    = "31a7e031-4c77-xxxx-xxxx-xxxxxxxxxxxx"  # wrong — this is the App ID

  secret_permissions = ["Get", "List", "Set", "Delete"]
}

Why this happens

In Azure Active Directory, an App Registration and a Service Principal are two separate objects — even when one creates the other.

  • The App Registration (31a7e031-...) is the application definition. It has an Application ID (also called Client ID).
  • The Service Principal (a34a2d97-...) is the identity that actually authenticates and gets permissions. It has its own Object ID.

Key Vault access policies apply to the Service Principal’s Object ID, not to the Application ID. Using the wrong one creates a policy that technically exists but never matches anything — the policy lookup at runtime uses the oid field from the token, and there’s no policy for that OID.


Finding the correct Object ID

# By display name
az ad sp list --display-name "<service>-app" --query "[].id" -o tsv

# By Application (client) ID
az ad sp list --filter "appId eq '31a7e031-4c77-xxxx-xxxx-xxxxxxxxxxxx'" --query "[].id" -o tsv

Both return the Service Principal’s Object ID — a34a2d97-ddbc-xxxx-xxxx-xxxxxxxxxxxx in this case.


The fix

resource "azurerm_key_vault_access_policy" "app" {
  key_vault_id = azurerm_key_vault.main.id
  tenant_id    = var.tenant_id
  object_id    = "a34a2d97-ddbc-xxxx-xxxx-xxxxxxxxxxxx"  # Service Principal Object ID

  secret_permissions = ["Get", "List", "Set", "Delete"]
}

Applied with terraform apply -target=azurerm_key_vault_access_policy.app. The backend’s next secret operation returned 200.


The better pattern in Terraform

When creating the service principal and access policy in the same Terraform config, reference the service principal’s object_id attribute directly instead of hardcoding either ID:

resource "azuread_service_principal" "app" {
  application_id = azuread_application.app.application_id
}

resource "azurerm_key_vault_access_policy" "app" {
  key_vault_id = azurerm_key_vault.main.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = azuread_service_principal.app.object_id  # correct attribute

  secret_permissions = ["Get", "List", "Set", "Delete"]
}

azuread_service_principal.app.object_id is the SP’s Object ID. azuread_service_principal.app.application_id is the App ID. They look identical in HCL but point to different things in AAD.


What the error message is telling you

appid=31a7e031... — what you authenticated as (App Registration)
oid=a34a2d97...   — what Azure actually looked up in the access policy list

If your access policy is keyed to the appid value, it will never match the oid lookup. The error makes this visible if you read both fields — the 403 is not a permissions gap, it’s an identity mismatch.

#azure#keyvault#terraform#aks#identity