1. What and Why
One sentence. Azure RBAC is the always-on, free authorisation system built into Azure Resource Manager that decides whether a given identity may perform a given operation on a given resource, by evaluating role assignments — a triple of principal, role definition, and scope.
Analogy. Think of a large office building. Microsoft Entra ID is the pass office: it knows every employee and contractor, issues the badges, and decides how hard a badge is to obtain. Azure RBAC is the set of card readers on individual doors. Each reader has been programmed with a rule of the shape "badges belonging to this group may open this door, and every door inside it." The pass office manager can print any badge in the building and still can't open the server room, because nobody programmed that reader to accept her badge. Nothing about being able to make identities implies being able to use them.
Technically. Every request to management.azure.com carries an OAuth 2.0 bearer token signed by
Entra ID. ARM extracts the principal's object ID from the oid claim, resolves the group memberships
in the token, gathers every role assignment applying at or above the target resource's scope, unions
the operation strings those roles permit, and tests the requested operation against that set. Allowed
requests proceed to the resource provider. Denied ones return HTTP 403 with an AuthorizationFailed
body naming the principal, the action, and the scope evaluated.
[Image Prompt: 2D minimalistic diagram showing where Azure RBAC sits between upstream identity sources — users, groups, service principals, and managed identities from Microsoft Entra ID — and downstream Azure resources across management groups, subscriptions, resource groups, and individual resources, with the role assignment shown as the connecting object, flat design, clean vector art style, white background, labelled boxes and arrows]
The problem before it existed
Azure's original model was the classic or "Azure Service Management" one, and it had exactly three roles at the subscription level: Account Administrator, Service Administrator, and Co-Administrator. That's it. There was no way to say "this team may manage the web tier and nothing else", so every engineer who needed to deploy anything became a Co-Administrator of the whole subscription.
The two failure modes that produces are both bad and both common:
Everyone is an administrator. Someone runs a cleanup script against the wrong subscription and deletes production. The postmortem action item is "be more careful", because there is no technical control available to write down.
Nobody has access. The organisation reacts to the first outcome by removing everyone and routing all changes through a small platform team. Deployment becomes a ticket queue, lead times go from minutes to days, and engineers start keeping a shadow subscription on a personal credit card.
RBAC dissolves the choice by making a grant narrow along three axes at once, independently:
| Axis | Question | Mechanism |
|---|---|---|
| Who | Which identity holds this? | A security principal — and ideally a group, so membership is the thing that changes |
| What | Which operations does it permit? | A role definition — hundreds of built-in ones, or a custom one |
| Where | Over which resources? | A scope — management group, subscription, resource group, or a single resource |
The important property is that these compose. "The payments on-call group may restart virtual
machines, but only in rg-payments-prod" is one assignment, expressible without writing a policy
document, without a naming convention, and without trusting anyone to be careful.
Where it sits in the Azure catalogue
Azure RBAC is not a service you deploy. There is nothing to provision, no SKU to choose, and no
meter attached to it. It is a capability of ARM itself, surfaced through the
Microsoft.Authorization resource provider and visible in the portal as the Access control (IAM)
blade that appears on every scope. Its assignments are ARM resources, which is the whole reason
Terraform and Bicep can manage them.
Four neighbours account for nearly all the confusion. Separating them is the most useful thing this page does.
Entra directory roles — a different system with similar names
Global Administrator, User Administrator, and Application Administrator are directory roles. They govern the tenant: users, groups, app registrations, licences, domains, tenant-wide settings. They are assigned through Entra ID, stored in the directory, and evaluated by Microsoft Graph — not ARM.
Azure roles (Owner, Contributor, Reader, Storage Blob Data Reader) govern Azure
resources. They are assigned through ARM at a resource scope.
The two sets are almost entirely disjoint. Use directory role and Azure role as distinct terms and refuse to say "Azure AD role", which means neither reliably. Full comparison in Core Concepts.
Azure Policy — asks what, not who
RBAC decides whether you may create a virtual machine. Azure Policy decides whether that virtual machine is permissible: which sizes, which regions, which tags, whether a public IP may be attached. Policy applies regardless of the caller's identity — an Owner is denied by policy exactly like everyone else.
The practical tell: a policy denial returns RequestDisallowedByPolicy and names the policy
assignment. No role will fix it, and granting a broader role is the classic wasted afternoon.
They are complements, not alternatives. RBAC without Policy means a Contributor can build anything, anywhere, untagged. Policy without RBAC means the shape is constrained but anyone can change anything.
Resource locks — blunt, and they outrank Owner
A lock (CanNotDelete or ReadOnly) applied to a resource, resource group, or subscription blocks
the corresponding operations for every principal, Owner included. It's the right tool for "this
one production database must not be deleted by accident", because RBAC's only equivalent would be
removing delete permission from the whole team.
Locks inherit downward like assignments do, and a lock on a resource group is a common source of
mysterious failures in resources inside it. The error is ScopeLocked, which reads like a
permissions problem and isn't one.
Data-plane authorisation — the parallel system that routes around you
Many services shipped their own authorisation model before RBAC reached their data plane, and most still support both:
| Service | The RBAC path | The parallel path that bypasses it |
|---|---|---|
| Storage | Storage Blob Data Reader and friends |
Account keys, SAS tokens |
| Key Vault | Key Vault Secrets User (RBAC permission model) |
Vault access policies (the legacy model) |
| Azure SQL | Entra authentication + database roles | SQL logins and passwords |
| Service Bus | Azure Service Bus Data Sender |
Shared Access Signature policies |
| AKS | Azure RBAC for Kubernetes authorisation | Kubernetes RBAC with the cluster admin kubeconfig |
Where a service offers a switch — allowSharedKeyAccess = false on a storage account, RBAC rather
than access policy on a Key Vault, disableLocalAccounts on AKS — flipping it is what makes your
RBAC design load-bearing rather than aspirational. Otherwise you have written a careful permission
model and left the side door unlocked.
The AWS analogue, and where it breaks
Rough analogue: AWS IAM policies plus IAM's evaluation logic.
What transfers. Principals. Permission strings that name a service and an operation. The idea that a grant can be limited to specific resources. Short-lived credentials as the correct pattern for workloads.
Where it breaks — four places, each of which will bite:
1. There is no explicit deny. This is the big one. AWS evaluation is deny-by-default with
explicit-deny-wins, which makes patterns like "Administrator except for these tagged resources"
natural. Azure RBAC computes the pure union of every role assignment at or above the scope.
Nothing subtracts. notActions exists and looks like a deny, but it only removes permissions from
that one role definition — a second assignment granting the same action wins. Platform deny
assignments exist, are created by Azure managed applications, and cannot be authored by you.
The design consequence: breadth is permanent until removed at its source. You cannot compensate for an over-broad assignment further down the tree.
2. There are no permission boundaries or SCPs at the RBAC layer. AWS uses SCPs at the
organisation level as a ceiling on what accounts can do. Azure's equivalent guardrail is Azure
Policy assigned at a management group — a different system with different semantics
(deny effects on request shapes, not permission subtraction). If you're looking for "the SCP
equivalent", it's Policy, not RBAC.
3. The grant is an object, not a document attached to an identity. An IAM policy hangs off a user,
group, or role. An Azure role assignment is a standalone ARM resource with its own GUID name, living
at the scope, referencing the principal and the role. So the question "what can this person do" is
answered by querying assignments across the tree (az role assignment list --assignee <oid> --all),
not by reading an attached document. And deleting a principal leaves its assignments behind as
orphans pointing at a dead object ID.
4. The control plane and the data plane are authorised separately, by different evaluators. In
AWS, s3:CreateBucket and s3:GetObject live in the same policy, evaluated by the same engine. In
Azure, managing a storage account (actions, evaluated by ARM) and reading a blob (dataActions,
evaluated by blob.core.windows.net) are separate concerns, and most built-in management roles carry
only the former. Owner does not let you read a blob. Nothing in an AWS mental model prepares you
for this, and it is the most frequent 403 in Azure.
When NOT to reach for RBAC
To enforce a configuration standard. Region restrictions, required tags, allowed SKUs, "no public IPs" — all Azure Policy. Attempting them as permissions produces a custom role that has to be maintained forever as Azure adds operations, and it still fails, because a Contributor can create a compliant resource and then modify it.
To protect one resource from deletion. Use a CanNotDelete lock. Removing delete permission via
RBAC removes it from the whole team for the whole scope.
To express "everyone except X". No deny, no exceptions. Reshape the grant to be narrow from the outset.
As the only control while a key-based path exists. Disable shared-key access, migrate Key Vault to the RBAC permission model, disable AKS local accounts. Until then, RBAC is one of two doors.
As a substitute for network controls. RBAC governs who is authorised. Private endpoints and firewall rules govern who can reach the endpoint at all. An estate needs both; neither implies the other.
To achieve per-object granularity in services that don't support conditions. ABAC conditions on blob index tags and path prefixes are real and useful, but coverage is service-by-service and incomplete ⚠️ verify current coverage against current Azure docs. Where conditions don't reach, splitting the data across containers or accounts so the scope boundary matches the permission boundary is usually the honest answer — proliferating custom roles is not.
For standing highly-privileged access, in any form. Not because RBAC can't express it, but because it shouldn't be the answer. The number of accounts that need permanent Owner is close to zero; the rest should activate through PIM with justification and an expiry, or be a documented, alarmed break-glass account. Production covers both.
What you should be able to say after this page
- Azure RBAC authorises operations on Azure resources; Entra directory roles authorise operations on the tenant. Global Administrator and Owner are different jobs.
- A grant is exactly principal + role + scope, and it is an ARM resource.
- Assignments inherit downward and union with no deny, so scope choice is consequential and irreversible from below.
- RBAC and Azure Policy answer different questions; locks outrank everyone; and the data plane is authorised separately from the control plane.
Next: Core Concepts →