Managing Azure RBAC with the Azure CLI

The Azure CLI gives you precise, scriptable control over every aspect of RBAC management. Knowing the right commands and flags for listing, creating, and removing role assignments — and understanding the output — makes access management faster and less error-prone than using the portal alone. This page walks through a complete real-world RBAC workflow from start to finish.

Prerequisites

To follow the commands in this page, you need:

  • The Azure CLI installed and authenticated (az login)
  • The correct subscription set as default (az account set —subscription “MY_SUBSCRIPTION_NAME”)
  • A role that can read role assignments at the scope you are querying — Reader is sufficient for listing, but you need User Access Administrator or Owner to create or delete assignments

All commands in this page use the az role assignment and az role definition command groups. The az ad commands are used for looking up identity object IDs from Microsoft Entra ID.

Listing role assignments

The most important thing to understand about listing role assignments is the difference between direct assignments and inherited assignments. Without the —include-inherited flag, you only see assignments created directly at the scope you specify.

List all assignments in the current subscription

az role assignment list --output table

Output columns: Principal, Role, Scope. This gives a quick overview of who has what.

List assignments at a specific resource group, including inherited ones

az role assignment list \
  --resource-group "production-app" \
  --include-inherited \
  --output table

This shows both assignments made directly on production-app and assignments inherited from the subscription or management group level. Use this when you want to understand the complete access picture for a resource group.

List all assignments for a specific user

az role assignment list \
  --assignee "developer@company.com" \
  --all \
  --output table

The —all flag searches all subscriptions accessible to your account, not just the default subscription.

Filter the output to find high-privilege assignments

Use a JMESPath query to find only Owner and Contributor assignments at subscription scope:

az role assignment list \
  --subscription "YOUR_SUBSCRIPTION_ID" \
  --output json \
  --query "[?scope == '/subscriptions/YOUR_SUBSCRIPTION_ID' && (roleDefinitionName == 'Owner' || roleDefinitionName == 'Contributor')]" \
  | jq '[.[] | {principal: .principalName, role: .roleDefinitionName, scope: .scope}]'

This is a useful audit command for finding the broadest and most sensitive assignments. These should be reviewed regularly. The principle of least privilege guide covers what to look for during an access review.

List role assignments for a service principal by app ID

# Get the service principal's object ID first
SP_OBJECT_ID=$(az ad sp show \
  --id "00000000-0000-0000-0000-000000000000" \
  --query id \
  --output tsv)

# List their assignments
az role assignment list \
  --assignee "$SP_OBJECT_ID" \
  --all \
  --output table

Creating role assignments

The az role assignment create command requires three inputs: the assignee, the role, and the scope. There are multiple ways to specify each.

Assigning a role to a user at resource group scope

az role assignment create \
  --assignee "developer@company.com" \
  --role "Contributor" \
  --resource-group "staging-app"

Using —resource-group is shorthand for providing the full scope ID. The CLI expands it to the full ARM resource ID for you.

Assigning a role using the full scope ID

az role assignment create \
  --assignee "developer@company.com" \
  --role "Reader" \
  --scope "/subscriptions/aaaa-bbbb-cccc-dddd/resourceGroups/production-app"

Assigning a role at subscription scope

SUBSCRIPTION_ID=$(az account show --query id --output tsv)

az role assignment create \
  --assignee "security-team@company.com" \
  --role "Reader" \
  --scope "/subscriptions/$SUBSCRIPTION_ID"

Assigning a role to a group

First get the group’s object ID:

GROUP_OBJECT_ID=$(az ad group show \
  --group "DevOps Team" \
  --query id \
  --output tsv)

az role assignment create \
  --assignee-object-id "$GROUP_OBJECT_ID" \
  --assignee-principal-type "Group" \
  --role "Contributor" \
  --resource-group "dev-infrastructure"

When assigning to a group, use —assignee-object-id and —assignee-principal-type together. This avoids an extra identity lookup and prevents ambiguity.

Assigning a role to a managed identity

IDENTITY_OBJECT_ID=$(az identity show \
  --name "myapp-identity" \
  --resource-group "production-app" \
  --query principalId \
  --output tsv)

az role assignment create \
  --assignee-object-id "$IDENTITY_OBJECT_ID" \
  --assignee-principal-type "ServicePrincipal" \
  --role "Storage Blob Data Reader" \
  --scope "/subscriptions/aaaa-bbbb-cccc-dddd/resourceGroups/production-app/providers/Microsoft.Storage/storageAccounts/myprodstorage"

Managed identity object IDs are accessed via principalId on the identity resource. The —assignee-principal-type is ServicePrincipal for both service principals and managed identities.

Complete workflow: check → assign → verify → remove

This section walks through the full lifecycle of a role assignment. The scenario: a new developer, newdev@company.com, needs Contributor access to the staging-webapp resource group.

Step 1: Check what the user currently has

az role assignment list \
  --assignee "newdev@company.com" \
  --all \
  --include-inherited \
  --output table

Before creating a new assignment, check whether they already have access through inheritance or an existing direct assignment. You might discover they already have Contributor through a group assignment.

Sample output (showing no existing assignments):

Principal    Role    Scope
-----------  ------  -----
(no results)

Step 2: Look up the user’s object ID

USER_OBJECT_ID=$(az ad user show \
  --id "newdev@company.com" \
  --query id \
  --output tsv)

echo "User object ID: $USER_OBJECT_ID"

Step 3: Create the role assignment

az role assignment create \
  --assignee-object-id "$USER_OBJECT_ID" \
  --assignee-principal-type "User" \
  --role "Contributor" \
  --resource-group "staging-webapp" \
  --output json

The command outputs the full role assignment JSON on success. You should see a response with principalId, roleDefinitionId, scope, and a id containing the assignment GUID. Save the assignment ID if you want to be able to delete this specific assignment later.

Sample output:

{
  "id": "/subscriptions/aaaa-bbbb-cccc-dddd/resourceGroups/staging-webapp/providers/Microsoft.Authorization/roleAssignments/1234-5678-90ab-cdef",
  "principalId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "principalType": "User",
  "roleDefinitionId": "/subscriptions/aaaa-bbbb-cccc-dddd/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",
  "scope": "/subscriptions/aaaa-bbbb-cccc-dddd/resourceGroups/staging-webapp"
}

Step 4: Verify the assignment was created

az role assignment list \
  --assignee "newdev@company.com" \
  --resource-group "staging-webapp" \
  --output table

You should now see the Contributor assignment on staging-webapp. Note that there is a short propagation delay (usually seconds, occasionally a minute or two) before the assignment takes effect across all Azure services.

Step 5: Test effective access (optional but recommended)

To verify the user can actually do what you intended, ask them to run a test command, or use the portal’s “Check access” feature on the resource group. From the CLI, you can also check effective permissions for a principal at a scope:

az role assignment list \
  --assignee "newdev@company.com" \
  --resource-group "staging-webapp" \
  --include-inherited \
  --output table

Step 6: Remove the assignment when no longer needed

Use the same parameters you used to create the assignment:

az role assignment delete \
  --assignee "newdev@company.com" \
  --role "Contributor" \
  --resource-group "staging-webapp"

Or delete by assignment ID (more precise when a user has multiple assignments at the same scope):

az role assignment delete \
  --ids "/subscriptions/aaaa-bbbb-cccc-dddd/resourceGroups/staging-webapp/providers/Microsoft.Authorization/roleAssignments/1234-5678-90ab-cdef"

After deletion, verify with another list command:

az role assignment list \
  --assignee "newdev@company.com" \
  --resource-group "staging-webapp" \
  --output table

The output should now be empty (or only show inherited assignments that remain).

Working with role definitions

You also need to query role definitions to find the right role for a given job. The az role definition command group handles this.

Find built-in roles related to a service

az role definition list \
  --query "[?contains(roleName, 'Storage')].{Name:roleName, Description:description}" \
  --output table

Show the full permission list for a specific role

az role definition list \
  --name "Storage Blob Data Contributor" \
  --output json

This shows all Actions, NotActions, DataActions, and NotDataActions for the role — essential when deciding whether a built-in role fits your needs or whether you need a custom role.

List custom roles in the current tenant

az role definition list \
  --custom-role-only true \
  --output table

For creating and managing custom roles via the CLI, see built-in vs custom roles for the complete workflow including the JSON definition format.

Useful flags and formatting tips

A few flags are worth knowing for daily use:

Output formats

  • —output table — human-readable for quick visual inspection
  • —output json — full structured output for scripting and debugging
  • —output tsv — tab-separated, ideal for capturing a single value into a variable
  • —output yaml — useful for reading nested structures more clearly than JSON

Querying with JMESPath

The —query flag accepts JMESPath expressions for filtering and reshaping output:

# Extract just the role names and scopes
az role assignment list \
  --assignee "developer@company.com" \
  --all \
  --query "[].{Role:roleDefinitionName, Scope:scope}" \
  --output table

Capturing subscription ID programmatically

SUBSCRIPTION_ID=$(az account show --query id --output tsv)
TENANT_ID=$(az account show --query tenantId --output tsv)

These are useful in scripts that need to construct full scope resource IDs without hardcoding GUIDs.

Tip

Add —output table as a default in your ~/.azure/config file to avoid typing it on every command during interactive sessions. In scripts, always specify the output format explicitly to ensure consistent parsing regardless of the user’s default configuration.

Common CLI mistakes when managing RBAC

  1. Forgetting —include-inherited when auditing. Without this flag, you see only direct assignments at the queried scope, missing any inherited permissions from higher scopes. An audit that misses inherited assignments gives you a false picture of effective access.
  2. Using —assignee with a display name instead of email or object ID. Display names are not unique — multiple people can share a display name. Always use email addresses, app IDs, or object IDs with the —assignee flag to ensure you are targeting the right principal.
  3. Not verifying the active subscription before creating assignments. If you have switched subscriptions recently, it is easy to create an assignment in the wrong subscription. Run az account show to confirm which subscription is active before any create or delete operations.
  4. Deleting assignments by role name instead of ID when duplicates exist. If a user has two Contributor assignments at the same scope (which can happen with group and direct assignments), deleting by role name deletes only one of them. Use the assignment ID for precision when you need to delete a specific assignment.
  5. Not scripting access reviews. Manual reviews in the portal miss assignments in subscriptions you forget to check. Schedule a regular az role assignment list —all and pipe the output to a file or monitoring system to catch scope creep over time.

Frequently asked questions

How do I find the object ID for a user when creating a role assignment?

Use az ad user show --id user@company.com --query id --output tsv. For service principals, use az ad sp show --id APP_ID --query id --output tsv. For managed identities, use az identity show --name IDENTITY_NAME --resource-group RG_NAME --query principalId --output tsv.

What is the difference between --assignee and --assignee-object-id in az role assignment create?

The --assignee flag accepts either an email address, a service principal app ID, or an object ID and does a lookup if needed. The --assignee-object-id flag takes only an object ID (GUID) and skips the lookup. In scripts and automation, --assignee-object-id is preferred because it is faster (one fewer API call), deterministic, and avoids ambiguity when the same email exists in multiple tenants.

How do I list role assignments for all subscriptions I have access to?

Use az role assignment list --all --include-inherited --output table. The --all flag queries across all subscriptions in the current tenant that you have access to. Without --all, the command only queries the currently active subscription set with az account set.

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