Azure RBAC Role Assignment Structure: A Complete Breakdown
Every Azure RBAC role assignment is a structured object with three parts: who gets access (the security principal), what access they get (the role definition), and where that access applies (the scope). Understanding the exact structure of a role assignment — including how scope inheritance works — is essential for designing access that does what you intend and nothing more.
The anatomy of a role assignment
When you create a role assignment in Azure, whether through the portal, the CLI, or Terraform, Azure stores it as a resource with a specific JSON structure. Here is what that object looks like:
{
"id": "/subscriptions/aaaa-bbbb-cccc-dddd/resourceGroups/production-app/providers/Microsoft.Authorization/roleAssignments/1234-5678-90ab-cdef",
"name": "1234-5678-90ab-cdef",
"type": "Microsoft.Authorization/roleAssignments",
"properties": {
"roleDefinitionId": "/subscriptions/aaaa-bbbb-cccc-dddd/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7",
"principalId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"principalType": "User",
"scope": "/subscriptions/aaaa-bbbb-cccc-dddd/resourceGroups/production-app",
"createdOn": "2026-01-15T09:32:11.456Z",
"updatedOn": "2026-01-15T09:32:11.456Z",
"createdBy": "9988-7766-5544-3322",
"updatedBy": "9988-7766-5544-3322",
"condition": null,
"conditionVersion": null,
"description": null
}
}Let’s go through each field:
id
The full ARM resource ID of this role assignment. The format is always: {scope}/providers/Microsoft.Authorization/roleAssignments/{roleAssignmentGUID}. The GUID at the end is unique to this specific assignment. If you delete and recreate the same assignment, it gets a new GUID.
name
Just the GUID portion of the id. This is the identifier you use when deleting a specific role assignment directly.
type
Always Microsoft.Authorization/roleAssignments. Role assignments are ARM resources just like VMs or storage accounts.
properties.roleDefinitionId
The full ARM resource ID of the role definition being assigned. This is a GUID-based path under the subscription or provider namespace. The GUID acdd72a7-3385-48ef-bd42-f606fba81ae7 in the example above is the built-in Reader role — this GUID is the same across all Azure tenants. Built-in role GUIDs are stable and documented; custom role GUIDs are generated per tenant.
properties.principalId
The object ID of the security principal. This is the Microsoft Entra ID object ID of the user, group, service principal, or managed identity receiving the role.
properties.principalType
The type of principal: User, Group, ServicePrincipal, or ForeignGroup. Azure uses this to look up the principal in the correct directory namespace.
properties.scope
The ARM resource ID of the scope where this assignment applies. This is the key field for understanding what the assignment covers. See the scope hierarchy section below for how this works.
properties.condition and conditionVersion
Optional fields for RBAC conditions — attribute-based expressions that add extra constraints to when the role applies. See RBAC conditions for details. When not using conditions, these are null.
The scope hierarchy and inheritance
Azure organizes resources in a four-level hierarchy. A role assignment at any level flows downward to all levels beneath it:
Management Group
└── Subscription A
│ ├── Resource Group: prod-web
│ │ ├── Resource: app-service-prod
│ │ └── Resource: sql-database-prod
│ └── Resource Group: prod-data
│ └── Resource: storage-account-prod
└── Subscription B
└── Resource Group: dev-web
└── Resource: app-service-devIf you assign Contributor to a user at Subscription A scope, that assignment applies to:
- Everything in
prod-webresource group - Everything in
prod-dataresource group - The
app-service-prod,sql-database-prod, andstorage-account-prodresources individually
It does not apply to anything in Subscription B, even if both subscriptions are under the same management group — unless the assignment is made at the management group level.
If you assign Reader at the management group level, that Reader assignment flows down to Subscription A, Subscription B, and every resource group and resource within both.
A visual example: subscription-level Contributor
Subscription A [Contributor assigned here for user@company.com]
│
├── Resource Group: prod-web ← Inherits: Contributor
│ ├── app-service-prod ← Inherits: Contributor
│ └── sql-database-prod ← Inherits: Contributor
│
└── Resource Group: prod-data ← Inherits: Contributor
└── storage-account-prod ← Inherits: Contributor
Subscription B ← No access (different subscription)
└── Resource Group: dev-web ← No accessThe inherited assignments are not new role assignment objects — Azure derives them from the single assignment at the subscription. You cannot remove the inherited assignment from prod-web alone. To stop a user from having Contributor on prod-web, you either remove the subscription-level assignment (losing access everywhere) or create an explicit deny assignment at the resource group level (which is uncommon and requires Azure PIM or Blueprints).
This is why the principle of least privilege emphasizes scoping assignments as narrowly as the job requires. Broad assignments create inherited permissions that are hard to reason about.
Scope ARM resource ID formats
The scope field in a role assignment must be a valid ARM resource ID. Here are the exact formats for each level:
Management group scope
/providers/Microsoft.Management/managementGroups/{managementGroupId}Subscription scope
/subscriptions/{subscriptionId}Resource group scope
/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}Resource scope
/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{providerNamespace}/{resourceType}/{resourceName}For example, a specific storage account:
/subscriptions/aaaa-bbbb-cccc-dddd/resourceGroups/production-app/providers/Microsoft.Storage/storageAccounts/myprodstorageaccountYou can look up the ARM resource ID of any resource in the portal by going to the resource and clicking Properties, or with the CLI:
az storage account show \
--name myprodstorageaccount \
--resource-group production-app \
--query id \
--output tsvRole definition IDs for common built-in roles
Built-in role definition GUIDs are constant across all Azure tenants. This is useful when working with Terraform or ARM templates where you need the exact ID. Here are the most commonly used built-in roles and their GUIDs:
Owner: 8e3af657-a8ff-443c-a75c-2fe8c4bcb635
Contributor: b24988ac-6180-42a0-ab88-20f7382dd24c
Reader: acdd72a7-3385-48ef-bd42-f606fba81ae7
User Access Administrator: 18d7d88d-d35e-4fb5-a5c3-7773c20a72d9
Storage Blob Data Reader: 2a2b9908-6ea1-4ae2-8e65-a410df84e7d1
Storage Blob Data Contributor: ba92f5b4-2d11-453d-a403-e96b0029c9fe
Storage Blob Data Owner: b7e6dc6d-f1e8-4753-8033-0f276bb0955b
Key Vault Secrets User: 4633458b-17de-408a-b874-0445c86b69e6
Key Vault Secrets Officer: b86a8fe4-44ce-4948-aee5-eccb2c155cd7
Key Vault Reader: 21090545-7ca7-4776-b22c-e363652d74d2
Virtual Machine Contributor: 9980e02c-c2be-4d73-94e8-173b1dc7cf3cTo get the role definition ID for any role by name:
az role definition list \
--name "Contributor" \
--query "[0].id" \
--output tsvIn Terraform, you should use data “azurerm_role_definition” or data “azurerm_builtin_role_definition” to look up role IDs by name rather than hard-coding GUIDs. This makes your code more readable and resilient to future changes. See managing RBAC with Terraform for the complete pattern.
Reading role assignment data from the CLI
Understanding the structure helps you write better queries. Here are practical CLI commands for reading role assignment data:
List all assignments in a subscription
az role assignment list \
--subscription "YOUR_SUBSCRIPTION_ID" \
--output jsonList assignments at a specific scope and all inherited assignments
az role assignment list \
--scope "/subscriptions/YOUR_SUB_ID/resourceGroups/production-app" \
--include-inherited \
--output tableThe —include-inherited flag is important: without it, you only see assignments made directly at that scope, not the inherited ones from parent scopes. For a complete picture of what a resource group’s effective permissions are, always include this flag.
List all assignments for a specific user
az role assignment list \
--assignee "user@company.com" \
--all \
--include-inherited \
--output tableThe —all flag searches across all subscriptions the current account has access to, not just the default subscription.
Show the full JSON for a specific assignment
az role assignment list \
--assignee "user@company.com" \
--resource-group "production-app" \
--output json | jq '.[0]'This shows the complete role assignment object with all the fields described earlier in this page, which is useful for auditing and for feeding into scripts that need the role definition ID or scope value.
When auditing a production environment, always run az role assignment list with —include-inherited and filter for any assignments with Owner or Contributor at subscription scope. These are the highest-risk assignments and should be reviewed first. You can also check the Azure portal’s Access Control (IAM) tab on the subscription — it shows both direct and inherited assignments in a single view.
Common structural mistakes in role assignments
- Confusing NotActions with deny assignments. NotActions in a role definition excludes actions from that role — it does not prevent someone from getting those actions through a different role assignment. If you want to truly prevent an action, you need a deny assignment (rare and typically managed through Azure Policy or Blueprints). The built-in vs custom roles page covers this distinction in detail.
- Hard-coding role definition GUIDs in scripts. GUIDs are opaque. Use role names or data source lookups in Terraform and ARM templates so your code is readable. Exception: in performance-critical scripts, name-based lookups require an extra API call, so the GUID can be preferable there.
- Not understanding the scope of an existing assignment. Before creating a new assignment, check whether the principal already has access through an inherited assignment at a higher scope. Creating a second assignment does not harm anything, but it adds clutter that makes audits harder.
- Assigning roles at resource scope instead of resource group scope. Resource-level scope makes sense for truly granular access (e.g., one specific storage account out of many in a resource group). For general team access, resource group scope is easier to manage. Resource-level assignments can become unmanageable as resources are created and deleted.
Summary
- A role assignment is an ARM resource with three key properties: principalId (who), roleDefinitionId (what), and scope (where).
- Scope follows a four-level hierarchy: management group → subscription → resource group → resource. Assignments at higher scopes inherit downward.
- Inherited assignments cannot be removed at the lower scope — you must remove them at the scope where they were created.
- Built-in role definition GUIDs are stable across all Azure tenants, making them safe to use in scripts and infrastructure code.
- Use
—include-inheritedwithaz role assignment listto see the full effective access at any scope. - The scope field in a role assignment is the full ARM resource ID of the management group, subscription, resource group, or resource.
Frequently asked questions
What is the ARM resource ID format for a role assignment scope?
The scope is the full ARM resource ID of the container. For a subscription it is /subscriptions/{subscriptionId}. For a resource group it is /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}. For a specific resource it is /subscriptions/{subscriptionId}/resourceGroups/{rg}/providers/{provider}/{resourceType}/{resourceName}.
How do inherited role assignments differ from direct role assignments?
A direct assignment is one made explicitly at a given scope. An inherited assignment comes from a higher scope — for example, if you assign Contributor at the subscription level, every resource group in that subscription inherits that Contributor assignment. You cannot remove inherited assignments at the lower scope; you must remove them at the scope where they were originally created.
Can I see the role assignment JSON for an existing assignment in Azure?
Yes. Use az role assignment list --output json to see full role assignment objects. You can filter by scope, assignee, or role name. Each object includes the roleAssignmentId, principalId, roleDefinitionId, and scope fields.