The goal
A teammate needs access to a resource group, or a script fails with AuthorizationFailed. Azure role-based access control (RBAC) answers one question: who can do what, and where? This post shows you how to read and change that safely.
The three parts
Every role assignment joins three things:
| Part | Question | Examples |
|---|---|---|
| Security principal | Who? | User, group, service principal, managed identity |
| Role | What? | Reader, Contributor, Storage Blob Data Reader |
| Scope | Where? | Management group, subscription, resource group, resource |
Scopes are inherited downward. Reader on a subscription gives Reader on every resource group and resource inside it.
Management group
└─ Subscription
└─ Resource group
└─ Resource
The roles you'll use first
| Role | Can do | Can't do |
|---|---|---|
| Reader | View resources | Change anything |
| Contributor | Create, change and delete resources | Grant access to others |
| Owner | Everything, including granting access | Nothing is held back |
| User Access Administrator | Manage role assignments | Manage the resources themselves |
Those are control plane roles. They manage the resource, not always the data inside it. A Contributor on a storage account can't necessarily read blobs with Entra ID sign-in. That takes a data role such as Storage Blob Data Reader. Key Vault works the same way, with roles like Key Vault Secrets User.
Step 1: see what you already have
az ad signed-in-user show --query userPrincipalName -o tsv
az role assignment list --assignee "$(az ad signed-in-user show --query id -o tsv)" \
--all --include-inherited --include-groups \
--query "[].{role:roleDefinitionName, scope:scope}" -o table
--include-groups matters here, because most access should arrive through group membership.
Step 2: find the right role
Search the built-in roles before you reach for Contributor:
az role definition list --query "[?contains(roleName, 'Virtual Machine')].{name:roleName, description:description}" -o table
See exactly what a role allows:
az role definition list --name "Virtual Machine Contributor" \
--query "[].permissions[].{actions:actions, notActions:notActions, dataActions:dataActions}" -o json
Step 3: grant access
Use a group rather than a person where you can. Then you add and remove people without touching RBAC again.
GROUP_ID=$(az ad group show --group "app-team-readers" --query id -o tsv)
RG_ID=$(az group show --name rg-app-prod --query id -o tsv)
az role assignment create \
--assignee-object-id "$GROUP_ID" \
--assignee-principal-type Group \
--role "Reader" \
--scope "$RG_ID"
To grant a data role on one storage account:
STORAGE_ID=$(az storage account show -g rg-app-prod -n stappprod01 --query id -o tsv)
az role assignment create \
--assignee-object-id "$GROUP_ID" \
--assignee-principal-type Group \
--role "Storage Blob Data Reader" \
--scope "$STORAGE_ID"
Passing --assignee-principal-type also helps avoid a lookup failure right after an identity is created.
Warning
Assign at the smallest scope that works. Owner on a subscription lets someone grant that same power to anyone else. If someone needs it only for a while, consider Privileged Identity Management. See PIM and break-glass accounts.
Step 4: check who has access
List the assignments on a scope, including inherited ones:
az role assignment list --scope "$RG_ID" --include-inherited \
--query "[].{who:principalName, type:principalType, role:roleDefinitionName, scope:scope}" -o table
If you don't have permission to read Entra ID, principalName may be blank. You can still see principalId.
In the portal, open the resource, then Access control (IAM) and Check access. It shows the combined result for one person, including anything coming through a group.
Step 5: remove access
az role assignment delete \
--assignee "$GROUP_ID" \
--role "Reader" \
--scope "$RG_ID"
Check the list again afterward. If the principal still has access, it's probably coming from a higher scope, or from another group.
Why isn't it working yet?
| Symptom | Likely cause | Fix |
|---|---|---|
Just granted, still AuthorizationFailed | Changes can take a few minutes, and tokens are cached | Wait, then run az logout and az login |
| Can see the storage account, can't read blobs | No data role | Add a Storage Blob Data role |
Forbidden when creating an assignment | You lack roleAssignments/write | Ask an Owner or User Access Administrator |
RoleAssignmentExists | The same assignment is already there | Nothing to do |
| Removed access, it's still there | Inherited from a higher scope or another group | List with --include-inherited --include-groups |
| Owner, but the action is still denied | A policy, lock or deny assignment | See policy deny and resource locks |
RBAC is additive: your effective access is the sum of your assignments. The main exception is a deny assignment, which is created by Azure features rather than by you directly, and it takes priority.
For help with a specific failure, use the AuthorizationFailed cheat sheet.
Find out who changed access
Role assignment changes are recorded in the Activity Log:
az monitor activity-log list \
--resource-group rg-app-prod \
--offset 7d \
--query "[?contains(operationName.value, 'roleAssignments')].{when:eventTimestamp, who:caller, what:operationName.value, status:status.value}" \
-o table
See who changed or deleted it for more.
Prevent it next time
- Grant to groups, not individuals.
- Pick the narrowest role and scope, and avoid Owner except where it's truly needed.
- For apps, use a managed identity, not a stored secret.
- Review assignments on production subscriptions every quarter, and remove the ones nobody can explain.