Microsoft Azure — Azure RBAC & Custom Roles
RBAC roles, custom role definitions, scope management, deny assignments, and PIM integration..
20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.
- ✓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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
| File | Command / Code | Purpose |
|---|---|---|
| assign-reader-role.sh | az role assignment create \ | Why RBAC Is the Backbone of Azure Security |
| inspect-contributor-role.sh | az 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.sh | az role definition create --role-definition @vm-operator-role.json | Creating Custom Roles with Azure CLI and ARM |
| assign-custom-role.sh | az role assignment create \ | Assigning Custom Roles at the Right Scope |
| test-vm-operator-role.sh | az vm start --resource-group test-rg --name test-vm | Testing Custom Roles Before Production Deployment |
| audit-rbac.sh | az role assignment list --all --output json | jq '.[] | {principalName: .princip... | Auditing RBAC Assignments at Scale |
| assign-role-to-group.sh | az role assignment create \ | Common RBAC Pitfalls and How to Avoid Them |
| azure-pipeline-rbac.yml | steps: | Integrating RBAC with CI/CD Pipelines |
| policy-deny-public-ip.json | { | RBAC vs Azure Policy |
| rbac-change-query.kql | AzureActivity | Monitoring and Alerting on RBAC Changes |
| conditional-access-policy.json | { | Advanced |
| create-deny-assignment.sh | az deny-assignment create \ | Deny Assignments |
| role-id-assignment.sh | ROLE_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
Common mistakes to avoid
3 patternsNot planning rbac custom roles properly before deployment
Ignoring Azure best practices for rbac custom roles
Overlooking cost implications of rbac custom roles
Interview Questions on This Topic
Explain Azure RBAC & Custom Roles and its use cases.
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.
That's Azure. Mark it forged?
9 min read · try the examples if you haven't