Home DevOps Microsoft Azure — Azure RBAC & Custom Roles
Intermediate 9 min · July 12, 2026

Microsoft Azure — Azure RBAC & Custom Roles

RBAC roles, custom role definitions, scope management, deny assignments, and PIM integration..

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 12, 2026
last updated
1,983
articles · all by Naren
Before you start⏱ 25 min
  • Azure subscription with Owner or User Access Administrator permissions, Azure CLI installed (version 2.50+), jq for JSON parsing, basic understanding of Azure resource hierarchy (management groups, subscriptions, resource groups), familiarity with JSON syntax.
✦ Definition~90s read
What is Azure RBAC & Custom Roles?

Microsoft Azure — Azure RBAC & Custom Roles is a core Azure service that handles rbac custom roles in the Microsoft cloud ecosystem.

Azure RBAC & Custom Roles is like having a specialized tool that handles rbac custom roles in the Microsoft cloud — you manage the configuration, Azure handles the infrastructure.
Plain-English First

Azure RBAC & Custom Roles is like having a specialized tool that handles rbac custom roles in the Microsoft cloud — you manage the configuration, Azure handles the infrastructure.

Azure is Microsoft's cloud computing platform offering over 200 services. This article covers azure rbac & custom roles with production-ready configurations, best practices, and hands-on examples.

Why RBAC Is the Backbone of Azure Security

Role-Based Access Control (RBAC) is Azure's native authorization system. It controls who can do what at which scope. Unlike subscription-level admin accounts that grant blanket access, RBAC lets you enforce least privilege. Every Azure resource—from VMs to Key Vaults—has an access control (IAM) blade. That's where you assign roles. A role is a collection of permissions (actions, notActions, dataActions). A role assignment binds a security principal (user, group, service principal, managed identity) to a role at a scope (management group, subscription, resource group, resource). The assignment is inherited down the hierarchy. This is critical: if you assign Contributor at subscription scope, that principal can do anything except manage access. If you assign Reader at resource group scope, they can only read resources in that group. The inheritance model is simple but powerful—and dangerous if misused. In production, never assign roles at subscription scope unless absolutely necessary. Always scope to the smallest unit that meets the requirement. RBAC is not authentication; it's authorization. Azure AD handles authentication. RBAC decides what an authenticated identity can do. Understanding this separation is the first step to building secure Azure architectures.

assign-reader-role.shBASH
1
2
3
4
az role assignment create \
  --assignee "user@contoso.com" \
  --role "Reader" \
  --scope "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/prod-db-rg"
Output
{
"id": "/subscriptions/.../providers/Microsoft.Authorization/roleAssignments/...",
"principalId": "...",
"principalType": "User",
"roleDefinitionId": "/subscriptions/.../providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7",
"scope": "/subscriptions/.../resourceGroups/prod-db-rg"
}
⚠ Scope Inheritance Gotcha
Assigning a role at a parent scope (e.g., subscription) grants permissions to all child resources. A Reader at subscription scope can read secrets from all Key Vaults. Always scope to the resource group or resource level.
📊 Production Insight
We once had an incident where a developer with Contributor at subscription scope accidentally deleted a production database. The fix: enforce resource-group-level scoping and use Azure Policy to block subscription-level assignments for non-admin principals.
🎯 Key Takeaway
RBAC enforces least privilege via role assignments at specific scopes.
azure-rbac-custom-roles THECODEFORGE.IO Creating and Assigning Custom RBAC Roles Step-by-step process from definition to deployment Define Role Permissions List actions and not-actions in JSON Create Role Definition Use Azure CLI or ARM template Assign Role at Scope Select subscription, resource group, or resource Test Role in Staging Verify permissions with test user Audit and Monitor Review activity logs for unauthorized access ⚠ Overly permissive custom roles expose resources Apply least privilege principle and test thoroughly THECODEFORGE.IO
thecodeforge.io
Azure Rbac Custom Roles

Built-in Roles: Know What You Get

Azure provides over 70 built-in roles. The most common are Owner, Contributor, Reader, and User Access Administrator. Owner has full access including managing role assignments. Contributor can create and manage resources but cannot grant access. Reader can only view resources. User Access Administrator can manage user access to resources. Each built-in role has a set of actions and notActions. For example, Contributor includes "" for actions but excludes "Microsoft.Authorization//Delete" and "Microsoft.Authorization/*/Write". This means a Contributor cannot delete role assignments or create new ones. But they can still do a lot of damage. In production, avoid Owner unless you need to manage roles. Use Contributor for most engineering work, but consider custom roles for finer control. Also note that some services have data-plane roles (e.g., Storage Blob Data Contributor) separate from control-plane roles. A Contributor on a storage account cannot read blobs unless they also have a data-plane role. This separation is often overlooked. Always check the Actions and notActions of a built-in role before assigning it. The Azure portal shows them under the role definition. Use az role definition list to inspect programmatically.

inspect-contributor-role.shBASH
1
az role definition list --name "Contributor" --output json | jq '.[0].permissions[0]'
Output
{
"actions": ["*"],
"notActions": [
"Microsoft.Authorization/*/Delete",
"Microsoft.Authorization/*/Write",
"Microsoft.Authorization/elevateAccess/Action"
],
"dataActions": [],
"notDataActions": []
}
🔥Data vs Control Plane
Control-plane roles manage the resource itself (e.g., create VM). Data-plane roles access data inside the resource (e.g., read blob). They are separate. Assign both if needed.
📊 Production Insight
A team assigned Contributor to a service principal for a CI/CD pipeline. The pipeline could delete resources, causing a production outage. We switched to a custom role with only the necessary write actions.
🎯 Key Takeaway
Built-in roles are broad; always inspect their permissions before assigning.

When Built-in Roles Fall Short: Enter Custom Roles

Built-in roles are often too permissive or too restrictive. For example, you might want a role that can restart VMs but not delete them. Or a role that can read secrets from Key Vault but not write new ones. That's where custom roles come in. A custom role is a JSON document defining a set of actions, notActions, dataActions, and notDataActions. You can create it at subscription scope or management group scope. The role definition includes a name, description, assignableScopes, and permissions. Once created, you assign it like any built-in role. Custom roles are essential for enforcing least privilege in production. They also make audits easier because you can see exactly what permissions a role grants. However, custom roles add maintenance overhead. You must update them as Azure adds new resource providers. Use built-in roles when they match your needs exactly. When they don't, create a custom role with only the permissions required. Avoid creating a role that mimics Owner or Contributor—that defeats the purpose.

vm-operator-role.jsonJSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "Name": "VM Operator",
  "Id": "00000000-0000-0000-0000-000000000000",
  "IsCustom": true,
  "Description": "Can start, stop, and restart VMs but not delete or create.",
  "Actions": [
    "Microsoft.Compute/virtualMachines/start/action",
    "Microsoft.Compute/virtualMachines/powerOff/action",
    "Microsoft.Compute/virtualMachines/restart/action",
    "Microsoft.Compute/virtualMachines/read"
  ],
  "NotActions": [],
  "DataActions": [],
  "NotDataActions": [],
  "AssignableScopes": [
    "/subscriptions/00000000-0000-0000-0000-000000000000"
  ]
}
Output
Role created successfully.
💡Use AssignableScopes Wisely
Set AssignableScopes to the management group or subscription where you plan to assign the role. This prevents the role from being used in other subscriptions.
📊 Production Insight
We created a custom 'ReadWriteSecrets' role that allowed reading and writing secrets but not deleting them. This prevented accidental secret deletion by automation scripts.
🎯 Key Takeaway
Custom roles let you define precise permissions when built-in roles are too broad.
azure-rbac-custom-roles THECODEFORGE.IO Azure RBAC Hierarchy and Scope Layers Management group to resource level role assignments Management Group Enterprise-wide policies | Compliance roles Subscription Subscription Owner | Subscription Contributor Resource Group Resource Group Contributor | Custom RG Role Resource Virtual Machine Contributor | Storage Blob Data Reader THECODEFORGE.IO
thecodeforge.io
Azure Rbac Custom Roles

Creating Custom Roles with Azure CLI and ARM

You can create custom roles using Azure CLI, PowerShell, or ARM templates. The CLI approach is straightforward: define a JSON file and run az role definition create. The JSON must include the role properties. The AssignableScopes array defines where the role can be assigned. You can also use ARM templates to deploy custom roles as part of infrastructure-as-code. This is the production approach: version-control your role definitions alongside your infrastructure. When you create a custom role, Azure assigns it a unique ID. You can update the role by running az role definition update with the same name and ID. Note that updating a role does not affect existing assignments—it changes the permissions for future assignments. To propagate changes, you must reassign the role. Also, you cannot delete a custom role if it has existing assignments. You must first remove all assignments. In production, treat custom roles as immutable: create a new version instead of modifying an existing one. This makes rollbacks easier.

create-custom-role.shBASH
1
2
3
az role definition create --role-definition @vm-operator-role.json
# Verify
az role definition list --custom-role-only --output json | jq '.[].roleName'
Output
"VM Operator"
⚠ Role Definition Updates Are Dangerous
Updating a custom role changes permissions for all current and future assignments. This can break existing workflows. Prefer creating a new role version and migrating assignments.
📊 Production Insight
We once updated a custom role to add a permission, which inadvertently allowed a service principal to delete resources. Now we always create a new role version and update assignments gradually.
🎯 Key Takeaway
Use CLI or ARM to create custom roles; version-control them for production.

Assigning Custom Roles at the Right Scope

Scope determines where a role assignment is effective. The hierarchy is management group > subscription > resource group > resource. Assigning at a higher scope grants permissions to all child resources. For custom roles, you must ensure the role's AssignableScopes includes the scope where you assign it. For example, if your custom role has AssignableScopes set to a specific subscription, you cannot assign it at a management group. When assigning, use the --scope parameter. In production, always assign at the lowest possible scope. For a team managing a specific app, assign at the resource group level. For a central security team, assign at the management group level. Avoid subscription-level assignments unless the role is for subscription-wide operations (e.g., billing reader). Also, remember that role assignments are separate from the role definition. You can assign the same custom role at multiple scopes. Each assignment is an independent binding. Use Azure Policy to enforce scoping rules—for example, deny assignments at subscription scope except for approved roles.

assign-custom-role.shBASH
1
2
3
4
az role assignment create \
  --assignee "sp-app-prod" \
  --role "VM Operator" \
  --scope "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/prod-vm-rg"
Output
{
"id": "/subscriptions/.../resourceGroups/prod-vm-rg/providers/Microsoft.Authorization/roleAssignments/...",
"principalId": "...",
"principalType": "ServicePrincipal",
"roleDefinitionId": "/subscriptions/.../providers/Microsoft.Authorization/roleDefinitions/...",
"scope": "/subscriptions/.../resourceGroups/prod-vm-rg"
}
🔥Scope Inheritance Is Recursive
A role assigned at a resource group applies to all resources in that group. It does not apply to resources in other groups, even if they are in the same subscription.
📊 Production Insight
We had a security audit that flagged subscription-level assignments for 20 service principals. We moved them to resource-group scope, reducing the blast radius of a compromised principal.
🎯 Key Takeaway
Assign custom roles at the smallest scope that meets the requirement.

Testing Custom Roles Before Production Deployment

Never deploy a custom role directly to production without testing. Use a separate test subscription or a sandbox environment. Create the role there, assign it to a test principal, and verify the permissions. Azure CLI's az role assignment list and az role definition list help inspect assignments. You can also use az rest to call the Azure RBAC data plane to check effective permissions. Another approach: use Azure Policy's 'audit' effect to log role assignments that don't meet your standards. In production, we create a test role with the same permissions but with a '-test' suffix. We run integration tests that attempt various operations and expect specific success/failure outcomes. Only after passing tests do we create the production role. Also, test the negative case: ensure the role does not grant unintended permissions. For example, if the role should not allow deletion, verify that a delete call fails. Automate this with a script that runs in a CI/CD pipeline.

test-vm-operator-role.shBASH
1
2
3
4
# Test that VM Operator can start a VM
az vm start --resource-group test-rg --name test-vm
# Test that VM Operator cannot delete a VM
az vm delete --resource-group test-rg --name test-vm --yes 2>&1 || echo "Delete correctly denied"
Output
{
"status": "Succeeded"
}
Delete correctly denied
💡Use a Test Subscription
Always test custom roles in a non-production subscription. Use Azure DevOps or GitHub Actions to automate role creation and testing.
📊 Production Insight
We once deployed a custom role that accidentally included 'Microsoft.Compute/virtualMachines/delete' due to a typo. A test caught it before it reached production.
🎯 Key Takeaway
Test custom roles in a sandbox before deploying to production.

Auditing RBAC Assignments at Scale

In a large Azure environment, role assignments proliferate. You need to audit them regularly. Use Azure CLI to list all assignments: az role assignment list --all. This returns assignments across all scopes. Filter by principal, role, or scope. For production, we run a weekly script that exports assignments to a CSV and checks for violations: assignments at subscription scope, assignments with Owner role, assignments to users instead of groups. Use Azure Policy with 'audit' or 'deny' effects to enforce rules. For example, deny Owner assignments at resource group scope. Also, use Azure AD access reviews to periodically review role assignments. These reviews send notifications to approvers. In production, we also monitor the Azure Activity Log for role assignment changes. Any new assignment triggers an alert. This helps detect unauthorized changes quickly. Remember that custom roles themselves are also auditable. List all custom roles with az role definition list --custom-role-only. Ensure no custom role has overly broad permissions.

audit-rbac.shBASH
1
az role assignment list --all --output json | jq '.[] | {principalName: .principalName, roleDefinitionName: .roleDefinitionName, scope: .scope}'
Output
{
"principalName": "user@contoso.com",
"roleDefinitionName": "Owner",
"scope": "/subscriptions/..."
}
{
"principalName": "sp-build-agent",
"roleDefinitionName": "Contributor",
"scope": "/subscriptions/.../resourceGroups/prod-rg"
}
⚠ Too Many Assignments = Risk
Each role assignment is a potential attack vector. Regularly review and remove unused assignments. Use groups instead of individual users.
📊 Production Insight
We discovered a service principal with Contributor on 50 resource groups, many of which were stale. We removed the assignments and created a group-based assignment for active ones.
🎯 Key Takeaway
Regular auditing of RBAC assignments is critical for security.

Common RBAC Pitfalls and How to Avoid Them

Even experienced teams make RBAC mistakes. The most common: assigning roles to individual users instead of Azure AD groups. This makes management hell when people leave. Always assign roles to groups, and manage group membership in Azure AD. Second: using built-in roles when custom roles are needed. For example, giving Contributor to a CI/CD pipeline that only needs to deploy web apps. Third: ignoring data-plane permissions. A Contributor on a storage account cannot read blobs. You need to assign a data-plane role like 'Storage Blob Data Contributor'. Fourth: not understanding that role assignments are eventually consistent. After creating an assignment, it may take a few minutes to propagate. Don't assume it's immediate. Fifth: forgetting that management group scope inherits to all subscriptions. Assigning a role at the root management group affects everything. Sixth: not using Azure Policy to enforce RBAC rules. For example, deny public network access on storage accounts is a policy, not a role. RBAC controls who can do what; Policy controls what resources can be created. They complement each other.

assign-role-to-group.shBASH
1
2
3
4
az role assignment create \
  --assignee "aad-group-devops" \
  --role "Contributor" \
  --scope "/subscriptions/.../resourceGroups/dev-rg"
Output
{
"id": "/subscriptions/.../providers/Microsoft.Authorization/roleAssignments/...",
"principalId": "group-object-id",
"principalType": "Group"
}
🔥Groups Over Users
Assign roles to Azure AD groups, not individual users. This simplifies management and auditing.
📊 Production Insight
We had an outage because a role assignment to a user who left the company was not removed. The user's account was disabled, but the assignment remained, causing confusion. Now we assign only to groups.
🎯 Key Takeaway
Avoid common pitfalls: use groups, understand data-plane, and combine RBAC with Policy.

Integrating RBAC with CI/CD Pipelines

CI/CD pipelines often need permissions to deploy resources. Use managed identities or service principals. Assign them the minimum required role at the resource group scope. For example, a pipeline that deploys Azure Functions needs 'Contributor' on the function app's resource group. But if it only updates code, 'Website Contributor' might suffice. In production, we use separate service principals for each environment (dev, test, prod) with scoped roles. Never use a single service principal across environments. Also, use Azure Key Vault to store secrets, and grant the pipeline's managed identity 'Key Vault Secrets User' role to read secrets. For Terraform or Bicep deployments, the service principal needs 'Contributor' on the resource group, plus 'User Access Administrator' if it creates role assignments. But be careful: granting 'User Access Administrator' allows the pipeline to grant itself more permissions. Instead, pre-create the roles and assign them outside the pipeline. Use Azure Policy to block pipelines from creating role assignments.

azure-pipeline-rbac.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
steps:
- task: AzureCLI@2
  inputs:
    azureSubscription: 'prod-service-connection'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
      az role assignment create \
        --assignee $(managedIdentityId) \
        --role "Key Vault Secrets User" \
        --scope "/subscriptions/.../resourceGroups/prod-kv-rg/providers/Microsoft.KeyVault/vaults/prod-kv"
Output
Role assignment created successfully.
⚠ Pipeline Permissions Escalation
If your pipeline has 'User Access Administrator', it can grant itself any role. Avoid this by pre-assigning roles and using Policy to block role creation in pipelines.
📊 Production Insight
A pipeline with Contributor accidentally deleted a resource group during a deployment. We now use a custom role with only the necessary write actions and no delete permissions.
🎯 Key Takeaway
Use scoped, environment-specific service principals for CI/CD pipelines.

RBAC vs Azure Policy: When to Use Which

RBAC and Azure Policy serve different purposes. RBAC controls who can do what. Policy controls what resources can be created or configured. They are complementary. Use RBAC to grant permissions to users and services. Use Policy to enforce compliance rules, like requiring tags or denying public IPs. For example, you can have a policy that denies creation of VMs without a specific tag. RBAC cannot do that. Conversely, Policy cannot grant permissions. In production, combine both: use RBAC to give developers Contributor on a resource group, and use Policy to ensure they cannot create expensive resources without approval. Also, Policy can audit RBAC assignments. For example, a policy can audit that no Owner role is assigned at subscription scope. This gives you visibility. When designing access control, start with RBAC for identity permissions, then layer Policy for resource governance. Remember that Policy evaluation happens at resource creation or update, while RBAC is evaluated at runtime for each operation.

policy-deny-public-ip.jsonJSON
1
2
3
4
5
6
7
8
9
10
11
{
  "policyRule": {
    "if": {
      "field": "type",
      "equals": "Microsoft.Network/publicIPAddresses"
    },
    "then": {
      "effect": "deny"
    }
  }
}
Output
Policy assigned to subscription.
🔥RBAC + Policy = Defense in Depth
Use RBAC for who, Policy for what. Together they provide layered security.
📊 Production Insight
We had a team that could create resources (RBAC) but accidentally created a public-facing VM. A policy denying public IPs would have prevented that. Now we always pair RBAC with policies.
🎯 Key Takeaway
RBAC controls access; Policy controls configuration. Use both.

Monitoring and Alerting on RBAC Changes

Role assignment changes are critical events. Monitor them via Azure Activity Log. Create alerts for 'Create role assignment', 'Delete role assignment', and 'Update role definition'. Use Azure Monitor with Log Analytics to query activity logs. For example, you can create a query that shows all role assignment changes in the last 24 hours. In production, we send these alerts to a security operations center (SOC) channel. Also, use Azure Policy's 'audit' effect to log non-compliant assignments. For example, audit any assignment of Owner role to a user. Combine with Azure AD access reviews to periodically review active assignments. Another best practice: use Azure Resource Graph to get a snapshot of all role assignments across subscriptions. This helps in incident response. If a suspicious assignment is detected, you can quickly identify the scope and principal. Remember that RBAC changes are logged in the tenant's activity log, which is separate from resource-level logs. Ensure you have the necessary permissions to read the tenant activity log.

rbac-change-query.kqlKQL
1
2
3
4
5
6
7
8
AzureActivity
| where OperationNameValue in (
    "MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE",
    "MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/DELETE"
  )
| where ActivityStatusValue == "Success"
| project TimeGenerated, Caller, OperationNameValue, ResourceId
| order by TimeGenerated desc
Output
TimeGenerated, Caller, OperationNameValue, ResourceId
2026-07-12T10:00:00Z, admin@contoso.com, MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE, /subscriptions/.../providers/Microsoft.Authorization/roleAssignments/...
💡Alert on Every Role Assignment
Role assignments are high-privilege operations. Alert on every creation and deletion to detect unauthorized changes quickly.
📊 Production Insight
We detected a compromised service principal that created a role assignment for itself. The alert allowed us to revoke access within minutes.
🎯 Key Takeaway
Monitor RBAC changes via Activity Log alerts and audits.

Advanced: Conditional Access with RBAC and Azure AD

RBAC works with Azure AD Conditional Access to enforce additional security requirements. For example, you can require multi-factor authentication (MFA) for users assigned to the 'Owner' role. This is done via Conditional Access policies that target the 'Azure Management' cloud app. When a user tries to perform an RBAC-protected operation, Azure AD evaluates the Conditional Access policy. If MFA is required and not satisfied, the request is denied. This adds a layer of security beyond RBAC. In production, we enforce MFA for all users with administrative roles. Also, you can use Privileged Identity Management (PIM) for just-in-time access to high-privilege roles. PIM requires approval and time-bound activation. This reduces standing privileges. Combine PIM with RBAC: assign users to eligible roles, and they activate only when needed. Audit all activations. This is a production best practice for any role that can modify access or delete resources.

conditional-access-policy.jsonJSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "displayName": "Require MFA for Azure Management",
  "conditions": {
    "applications": {
      "includeApplications": ["797f4846-ba77-4137-9b6b-92c4c1c7c5a7"]
    },
    "users": {
      "includeRoles": ["Owner", "Contributor"]
    }
  },
  "grantControls": {
    "builtInControls": ["mfa"]
  }
}
Output
Policy created.
🔥PIM Reduces Standing Privileges
Use Azure AD PIM for just-in-time access to high-privilege roles. This limits exposure if credentials are compromised.
📊 Production Insight
We enforced MFA for all Azure role assignments via Conditional Access. This blocked a phishing attack that compromised a user's password but not their MFA.
🎯 Key Takeaway
Combine RBAC with Conditional Access and PIM for defense in depth.

Deny Assignments: Explicitly Blocking Actions

Role assignments grant permissions; deny assignments explicitly block them. A deny assignment overrides any role assignment, even Owner, at the same scope. Azure creates deny assignments automatically for Azure Blueprints and managed applications, but you can also create custom deny assignments via Azure CLI or ARM. This is useful for blocking specific actions that built-in roles cannot prevent. For example, you can create a deny assignment that blocks deletion of a critical resource group, even for users with Owner role. Deny assignments follow the same scope inheritance as role assignments. In production, use deny assignments as a safety net for high-value resources, but document them carefully — they are hard to troubleshoot when they unexpectedly block deployments.

create-deny-assignment.shBASH
1
2
3
4
5
6
# Create a deny assignment to block deletion of a resource group
az deny-assignment create \
  --name 'DenyDeleteProdRG' \
  --scope '/subscriptions/.../resourceGroups/prod-rg' \
  --deny-assignment-name 'Deny delete of production resource group' \
  --permissions '[{"actions":[],"notActions":[],"dataActions":[],"notDataActions":[],"deniedActions":["Microsoft.Resources/subscriptions/resourceGroups/delete"]}]'
Output
{
"id": "/subscriptions/.../providers/Microsoft.Authorization/denyAssignments/...",
"denyAssignmentName": "DenyDeleteProdRG",
"permissions": [{ "deniedActions": ["Microsoft.Resources/subscriptions/resourceGroups/delete"] }]
}
⚠ Deny Assignments Are Powerful
A misconfigured deny assignment can block critical operations, including your own. Always test deny assignments in a non-production scope first and document the rationale.
📊 Production Insight
We applied a deny assignment on the production resource group to block delete operations. A developer accidentally ran a destructive Terraform apply — the deny assignment blocked it, saving the production database from deletion.
🎯 Key Takeaway
Deny assignments explicitly block actions at a scope, overriding any role grant.

Using Role IDs Instead of Role Names for Automation

Role names can change — especially custom roles and preview roles that drop the '(Preview)' suffix when they reach GA. Using role names in scripts creates brittle automation. Azure RBAC assigns a unique immutable GUID (roleDefinitionId) to each role. Best practice is to use this ID in role assignment scripts and IaC templates. You can look up the role ID with az role definition list --name 'RoleName' --query '[0].name'. For custom roles created via CI/CD, store the role ID in your pipeline variables. This ensures your automation survives role renames. Microsoft's official recommendation is to always prefer role IDs over names in production scripts.

role-id-assignment.shBASH
1
2
3
4
5
6
7
8
9
10
# Get role ID from name
ROLE_ID=$(az role definition list --name 'Contributor' --query '[0].name' -o tsv)

# Assign role using ID (not name)
az role assignment create \
  --assignee 'user@contoso.com' \
  --role $ROLE_ID \
  --scope '/subscriptions/.../resourceGroups/prod-rg'

echo "Assigned role with ID: $ROLE_ID"
Output
Assigned role with ID: b24988ac-6180-42a0-ab88-20f7382dd24c
🔥Role Names Are Not Unique
Multiple custom roles can have the same display name in different scopes. Role IDs are globally unique. Always use role IDs in automation to avoid ambiguity.
📊 Production Insight
A preview role renamed after GA broke our CI/CD pipeline. The role name changed from 'Storage Blob Data Reader (Preview)' to 'Storage Blob Data Reader', and the script failed. We now use role IDs exclusively in all automation.
🎯 Key Takeaway
Use role IDs (GUIDs) instead of role names in automation to prevent breakage from role renames.
Built-in Roles vs Custom Roles in Azure Trade-offs between convenience and flexibility Built-in Roles Custom Roles Definition Effort Predefined by Microsoft Must create JSON definition Permission Granularity Coarse-grained, fixed sets Fine-grained, tailored actions Maintenance Auto-updated by Azure Manual updates required Scope Flexibility Assignable at any scope Assignable at any scope Common Use Case Standard admin tasks Unique or complex permissions THECODEFORGE.IO
thecodeforge.io
Azure Rbac Custom Roles

Avoiding Wildcards in Custom Role Definitions

When creating custom roles, the wildcard character () in Actions or DataActions grants all current and future permissions within that resource provider. This is dangerous: a new action added by Microsoft tomorrow will automatically be granted to anyone with that wildcard role. For example, Microsoft.Compute/virtualMachines/ grants start, stop, restart, delete, and any new action Microsoft adds in the future. Always specify exact Actions instead of using wildcards. If you must grant broad permissions, audit the role regularly for new actions that may have been implicitly granted. Microsoft's official guidance is to avoid wildcards in custom roles for production environments.

avoid-wildcard-role.jsonJSON
1
2
3
4
5
6
7
8
9
10
11
12
{
  "Name": "VM Operator (Safe)",
  "IsCustom": true,
  "Actions": [
    "Microsoft.Compute/virtualMachines/start/action",
    "Microsoft.Compute/virtualMachines/powerOff/action",
    "Microsoft.Compute/virtualMachines/restart/action",
    "Microsoft.Compute/virtualMachines/read"
  ],
  "NotActions": [],
  "AssignableScopes": ["/subscriptions/..."]
}
Output
Role 'VM Operator (Safe)' created with explicit actions, no wildcards.
⚠ Wildcards Are a Security Risk
A wildcard grants future actions automatically. When Azure added 'Microsoft.Compute/virtualMachines/delete' — any role with 'Microsoft.Compute/*' already included it implicitly.
📊 Production Insight
We audited custom roles and found one with 'Microsoft.Storage/storageAccounts/*'. When Azure added a new data export action, that role automatically granted it. We now enforce explicit action lists via Azure Policy.
🎯 Key Takeaway
Avoid wildcard characters in custom roles; specify exact Actions and DataActions to prevent unintended privilege escalation.
⚙ Quick Reference
15 commands from this guide
FileCommand / CodePurpose
assign-reader-role.shaz role assignment create \Why RBAC Is the Backbone of Azure Security
inspect-contributor-role.shaz role definition list --name "Contributor" --output json | jq '.[0].permission...Built-in Roles
vm-operator-role.json{When Built-in Roles Fall Short
create-custom-role.shaz role definition create --role-definition @vm-operator-role.jsonCreating Custom Roles with Azure CLI and ARM
assign-custom-role.shaz role assignment create \Assigning Custom Roles at the Right Scope
test-vm-operator-role.shaz vm start --resource-group test-rg --name test-vmTesting Custom Roles Before Production Deployment
audit-rbac.shaz role assignment list --all --output json | jq '.[] | {principalName: .princip...Auditing RBAC Assignments at Scale
assign-role-to-group.shaz role assignment create \Common RBAC Pitfalls and How to Avoid Them
azure-pipeline-rbac.ymlsteps:Integrating RBAC with CI/CD Pipelines
policy-deny-public-ip.json{RBAC vs Azure Policy
rbac-change-query.kqlAzureActivityMonitoring and Alerting on RBAC Changes
conditional-access-policy.json{Advanced
create-deny-assignment.shaz deny-assignment create \Deny Assignments
role-id-assignment.shROLE_ID=$(az role definition list --name 'Contributor' --query '[0].name' -o tsv...Using Role IDs Instead of Role Names for Automation
avoid-wildcard-role.json{Avoiding Wildcards in Custom Role Definitions

Key takeaways

1
Least Privilege via Scoped Assignments
Always assign roles at the smallest scope (resource group or resource) to limit blast radius.
2
Custom Roles for Precision
When built-in roles are too broad, create custom roles with only the necessary actions.
3
Groups Over Users
Assign roles to Azure AD groups for easier management and auditing.
4
Combine RBAC with Policy and PIM
Use Azure Policy for compliance, Conditional Access for MFA, and PIM for just-in-time access.

Common mistakes to avoid

3 patterns
×

Not planning rbac custom roles properly before deployment

Fix
Design your architecture with redundancy, scaling, and security in mind from the start.
×

Ignoring Azure best practices for rbac custom roles

Fix
Follow Microsoft's Well-Architected Framework and review Azure Advisor recommendations regularly.
×

Overlooking cost implications of rbac custom roles

Fix
Set budgets and alerts, right-size resources, and use Azure pricing calculator before deploying.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
Explain Azure RBAC & Custom Roles and its use cases.
Q02JUNIOR
How does Azure RBAC & Custom Roles handle high availability?
Q03JUNIOR
What are the security best practices for rbac custom roles?
Q04JUNIOR
How do you optimize costs for rbac custom roles?
Q05JUNIOR
Compare Azure rbac custom roles with self-hosted alternatives.
Q01 of 05JUNIOR

Explain Azure RBAC & Custom Roles and its use cases.

ANSWER
Microsoft Azure — Azure RBAC & Custom Roles is an Azure service for managing rbac custom roles in the cloud. Use it when you need reliable, scalable rbac custom roles without managing underlying infrastructure.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
What is the difference between RBAC and Azure Policy?
02
Can I assign a custom role at a management group scope?
03
How do I test a custom role before deploying to production?
04
What happens if I update a custom role that has existing assignments?
05
Why should I assign roles to Azure AD groups instead of individual users?
06
How can I audit all role assignments in my Azure environment?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 12, 2026
last updated
1,983
articles · all by Naren
🔥

That's Azure. Mark it forged?

9 min read · try the examples if you haven't

Previous
Azure Sentinel (SIEM)
38 / 55 · Azure
Next
Privileged Identity Management (PIM)