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-dev

If you assign Contributor to a user at Subscription A scope, that assignment applies to:

  • Everything in prod-web resource group
  • Everything in prod-data resource group
  • The app-service-prod, sql-database-prod, and storage-account-prod resources 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 access

The 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/myprodstorageaccount

You 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 tsv

Role 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-173b1dc7cf3c

To get the role definition ID for any role by name:

az role definition list \
  --name "Contributor" \
  --query "[0].id" \
  --output tsv

In 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 json

List assignments at a specific scope and all inherited assignments

az role assignment list \
  --scope "/subscriptions/YOUR_SUB_ID/resourceGroups/production-app" \
  --include-inherited \
  --output table

The —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 table

The —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.

Tip

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.

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.

Last verified: 19 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.