Azure RBAC
Volume 4 — Azure Foundations
Azure RBAC (Role-Based Access Control) governs who can do what to which resources across an Azure subscription. Nearly every "Forbidden" or "does not have authorization" error traces back to a missing or misscoped role assignment — this chapter covers the model well enough to diagnose those quickly.
1. The Core Model: Three Parts
Every permission in Azure RBAC comes from a role assignment, which is always exactly three things bound together:
Role Assignment = Principal + Role Definition + Scope
| Part | What it is |
|---|---|
| Principal | Who — a user, a group, a service principal, or a managed identity |
| Role Definition | What they can do — a named set of permitted actions (e.g. "Contributor", "Reader") |
| Scope | Where — which resource, resource group, subscription, or management group this applies to |
There's no such thing as a role granted without a scope — "Pete has Contributor" is an incomplete sentence; the real statement is always "Pete has Contributor on the traxs-prod resource group."
2. The Scope Hierarchy
Role assignments inherit downward — a role granted at a subscription level applies to every resource group and resource beneath it, automatically. A role granted at a specific resource group applies only within that group.
This is the most common cause of "why does this person have access I didn't explicitly grant them" — trace upward through the hierarchy, not just at the specific resource, when auditing who has access to something.
az role assignment list --scope "/subscriptions/<sub-id>/resourceGroups/traxs-prod" --include-inherited
3. Built-In Roles
| Role | Grants |
|---|---|
| Owner | Full control, including the ability to grant access to others |
| Contributor | Full control over resources, but cannot grant access to others |
| Reader | View-only, no changes of any kind |
| User Access Administrator | Can manage role assignments specifically, without other resource permissions |
Beyond these broad ones, Azure ships hundreds of narrowly-scoped built-in roles for specific services:
| Role | Grants |
|---|---|
| SQL DB Contributor | Manage SQL databases, but not the underlying server's firewall/security config |
| Storage Blob Data Contributor | Read/write blob data specifically — distinct from managing the storage account itself |
| Website Contributor | Manage App Service apps, without broader resource group control |
Principle of least privilege: reach for the narrowest role that actually covers what's needed, rather than defaulting to Contributor or Owner because it's simpler to grant once. A CI/CD service principal that only ever deploys to one App Service has no legitimate reason to hold Contributor across the entire resource group.
4. Control-Plane vs. Data-Plane Access
This distinction causes real confusion because it's easy to assume RBAC governs everything, when for some services it only governs half the picture.
| Control plane | Data plane | |
|---|---|---|
| Governs | Managing the resource itself — creating, deleting, configuring an Azure SQL server | Accessing the data/content inside it — querying tables in that SQL database |
| Azure SQL example | Azure RBAC role (e.g. SQL DB Contributor) controls who can manage the server/database resource | Actual query access is controlled by SQL logins/Azure AD database users, configured inside SQL itself, separate from Azure RBAC entirely |
| Storage example | Azure RBAC controls who can manage the storage account resource | Blob data access can be controlled by RBAC (Storage Blob Data Contributor) or by SAS tokens/connection strings, depending on configuration |
The practical trap: someone can hold full Owner-level Azure RBAC on a SQL Server resource and still be completely unable to run a query against the database inside it, because query access is a data-plane concern governed by SQL's own authentication, not by the Azure RBAC role assignment at all.
5. Principals: Who Can Hold a Role
| Principal type | What it represents |
|---|---|
| User | An individual person's Entra ID identity |
| Group | An Entra ID group — assigning a role to a group applies it to every current and future member |
| Service principal | An application's identity, typically used for CI/CD pipelines or external integrations (see the Microsoft Entra ID chapter) |
| Managed identity | An identity automatically tied to an Azure resource itself (like an App Service), with no credentials to manage at all (see the Microsoft Entra ID chapter) |
Assigning roles to groups rather than individual users is the standard practice for anything beyond a single-person exception — it means onboarding/offboarding someone is a group membership change, not a hunt through every resource's individual role assignments.
6. Assigning Roles
Via CLI:
az role assignment create \
--assignee "user@traxsgroup.com" \
--role "Contributor" \
--scope "/subscriptions/<sub-id>/resourceGroups/traxs-prod"
Listing existing assignments — the first move when diagnosing a permission issue:
az role assignment list --assignee "user@traxsgroup.com" --all --output table
--all matters here — without it, the command only shows assignments at the exact scope you specify, missing anything inherited from a parent scope higher in the hierarchy.
7. Custom Roles
When no built-in role matches the exact set of permissions needed, a custom role definition can be created — a named set of specific Actions/NotActions bound to a set of allowed scopes:
{
"Name": "Traxs Deploy Only",
"Description": "Restart and view App Service, nothing else",
"Actions": [
"Microsoft.Web/sites/restart/action",
"Microsoft.Web/sites/read"
],
"NotActions": [],
"AssignableScopes": [
"/subscriptions/<sub-id>/resourceGroups/traxs-prod"
]
}
Custom roles add maintenance overhead (they need to be kept in sync as needs evolve) — worth reaching for only once a genuinely recurring, narrowly-scoped need shows up repeatedly, not for a one-off situation a built-in role covers well enough.
8. Propagation Delay
Role assignment changes are not always instantaneous — Azure documents propagation that can take up to several minutes in some cases, occasionally longer during high load. A permission that "should" now work but still returns a 403 immediately after the assignment was created is often just propagation lag, not a misconfiguration — worth a short wait and retry before assuming the assignment itself is wrong.
9. Troubleshooting Playbook
| Symptom | Likely cause | What to check |
|---|---|---|
403 Forbidden / "does not have authorization to perform action" | No role assignment grants this specific action at this scope | az role assignment list --assignee <principal> --all to see everything actually granted, at every inherited scope |
| Permission works for one resource but not a near-identical one | Role assigned at the specific-resource level, not inherited from the resource group | Check scope on the existing assignment — it may be narrower than assumed |
Just assigned a role, still getting 403 | Propagation delay | Wait a few minutes and retry before troubleshooting further |
| Have Owner/Contributor on a SQL Server resource, still can't query the database | Confusing control-plane and data-plane access | Data-plane query access needs a SQL login or Azure AD database user configured inside SQL itself — Azure RBAC alone doesn't grant it |
| Service principal works locally but fails identically-configured in a pipeline | Different service principal being used than assumed, or role assignment scoped to the wrong resource group | Confirm exactly which principal the pipeline authenticates as, and check its role assignments specifically, not the interactive user's |
| Someone has access nobody remembers granting | Inherited from a broader scope (subscription or management group) rather than the resource itself | Trace the scope hierarchy upward with --include-inherited |
10. Quick Reference
| Category | Item | Detail |
|---|---|---|
| Model | Role Assignment | Principal + Role Definition + Scope, always all three |
| Scope | Inheritance | Flows downward: management group → subscription → resource group → resource |
| Role | Owner | Full control, can grant access to others |
| Role | Contributor | Full control, cannot grant access to others |
| Role | Reader | View-only |
| Concept | Control plane | Managing the resource itself |
| Concept | Data plane | Accessing data/content inside the resource — often governed separately from RBAC |
| Principal | Managed identity | Auto-tied to an Azure resource, no credentials to manage |
| Command | az role assignment list --all | List all assignments, including inherited |
| Command | az role assignment create | Grant a role |
Part of the Traxs Engineering Handbook — Volume 4: Azure Foundations. Companion chapters in this volume: Azure CLI, Azure Networking, Azure Storage, Azure Key Vault, Microsoft Entra ID.