---
cuppy_hub: azure
cuppy_path: guides/identity/fix-access-denied.md
cuppy_section: docs
cuppy_doc: 5KKJ-HPJR
cuppy_doc_uuid: 5843a9a9-b714-4cb4-8af2-afc833e941e2
cuppy_permalink: https://cuppy.io/azure/d/5KKJ-HPJR
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/azure/docs/guides/identity/fix-access-denied
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 06ae5c38239c2b37c097914afec9e38b44f6a2177900c79b27e715bc5c856337
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Fix "access denied" on Azure, layer by layer

**Work outward from the token.** Most wasted time comes from adding roles when the call was made by a different identity, in a different tenant, or refused by something that isn't RBAC at all. Each step below either finds the cause or rules a layer out.

## 1. Was a token issued at all?

An `AADSTS…` code, a `CredentialUnavailableError` or `AggregateAuthenticationError` means no token: nothing about roles matters yet.

- AADSTS for an app or service principal → `reference/entra/errors.md`
- AADSTS for a user (MFA, conditional access) → `reference/entra/sign-in-errors.md`
- Federated (GitHub, Kubernetes) → `reference/entra/federated-credential-errors.md`
- SDK credential messages → `reference/azure-identity/errors.md`

## 2. Which identity, which tenant?

```
az account show --query "{user:user.name, type:user.type, tenant:tenantId, sub:id}"
az account get-access-token --scope https://management.azure.com/.default --query accessToken -o tsv \
  | node -e "const p=require('fs').readFileSync(0,'utf8').split('.')[1]; const c=JSON.parse(Buffer.from(p,'base64url')); console.log({oid:c.oid, tid:c.tid, aud:c.aud, appid:c.appid ?? c.azp, roles:c.roles, scp:c.scp})"
```

In code, log the authenticated account (`reference/azure-identity/credential-chain.md`). Compare:

- **`oid`** with the principal in your role assignment. A different `oid` means `DefaultAzureCredential` picked another credential, or a user-assigned identity wasn't selected.
- **`tid`** with the resource's tenant. `InvalidAuthenticationTokenTenant` means the token is from another tenant: sign in with `--tenant <id>`.
- **`aud`** with the service. A Resource Manager token can't call Key Vault's data plane, and vice versa.

The command prints the token's claims, not the token. Never paste a full token into a chat or a log.

## 3. Which plane refused?

| You called | The refusal looks like | Permission comes from |
|---|---|---|
| Resource Manager (`management.azure.com`: create, configure, delete, `az` resource commands, Bicep) | `AuthorizationFailed` naming an action like `Microsoft.Storage/storageAccounts/write` | Azure RBAC `Actions` |
| A service's data endpoint (`*.vault.azure.net`, `*.blob.core.windows.net`, `*.servicebus.windows.net`…) | 403 with a service code (`ForbiddenByRbac`, …) | Azure RBAC **`DataActions`**, or the service's own model (Key Vault access policies, shared keys, SAS) |
| Microsoft Graph (create apps, read users) | `Authorization_RequestDenied` / `Insufficient privileges to complete the operation` | Microsoft Entra roles or Graph application permissions with admin consent, **not** Azure RBAC |

**Owner and Contributor carry no data actions.** "I'm Owner and still get 403 reading a secret" is the expected result: assign the data role.

## 4. Is there a role at the right scope?

```
az role assignment list --assignee <oid> --all --include-inherited --include-groups \
  --query "[].{role:roleDefinitionName, scope:scope}" -o table
```

The scope must be the resource or a parent of it. If the role is there but new, wait up to about 10 minutes and get a fresh token. For a managed identity whose access comes through an Entra **group**, expect hours (`reference/managed-identity/errors.md`). Errors from creating the assignment itself: `reference/rbac/errors.md`.

## 5. Something other than RBAC

| Layer | Signal | Check |
|---|---|---|
| **Deny assignment** (deployment stack deny settings, managed apps) | 403 despite a matching role | `az rest --method get --uri "https://management.azure.com/<scope>/providers/Microsoft.Authorization/denyAssignments?api-version=2022-04-01"` |
| **Azure Policy** | `RequestDisallowedByPolicy`, or a deny/denyAction 403 naming a policy | `reference/policy/effects-and-errors.md` |
| **Resource lock** | Writes or deletes refused although the role allows them. `ReadOnly` acts like Reader; `CanNotDelete` blocks deletion only | `az lock list --resource-group <rg>` |
| **Service firewall / private endpoint** | 403 naming the client IP (`Client address … is not authorized …`) | The service's network rules (`reference/keyvault/access-and-errors.md`; storage and networking families) |
| **Service permission model** | Key Vault in access-policy mode ignores RBAC data roles; storage accounts can refuse shared-key or SAS auth | `az keyvault show -n <kv> --query properties.enableRbacAuthorization` |

## 6. Still refused

Re-run the exact call with `--debug` (CLI) or SDK logging at `info`, keep the response's correlation or request ID, and compare the action named in the error with the role definition's `Actions` / `DataActions` (`az role definition list --name "<role>" --query "[0].permissions"`). A custom role missing one action is a common last cause.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 107 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/azure/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
