Background
Sections
IntroductionFoundations1. Resource Hierarchy2. Resource Manager3. Identity and RBAC4. Regions and Availability5. Naming and TaggingVirtual Machines1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetVirtual Network1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetBlob Storage1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure SQL Database1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Kubernetes Service1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Container Registry1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetMicrosoft Entra ID1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure RBAC1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Functions1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAPI Management1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure App Configuration1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Machine Learning1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure Monitor1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and CheatsheetAzure AI Foundry1. What and Why2. Core Concepts3. Architecture4. Getting Started5. Deployment6. Integrations7. Production8. Interview Questions9. Glossary and Cheatsheet

8. Interview Questions

18 min read

Three tiers, from warm-up to design. Answer keys are collapsed — read the question, answer it out loud, then open the block. Reading the answer first feels like learning and isn't.

RBAC comes up in almost every Azure interview, including for roles that aren't security-focused, because it's a fast way to find out whether someone has actually operated an estate or only followed tutorials. The tells interviewers listen for: whether you distinguish control plane from data plane unprompted, whether you know there's no deny, and whether you say "group" without being asked.


Tier 1 — Conceptual

1. What is Azure RBAC and what problem does it solve?

Answer

Azure RBAC is the authorisation system built into Azure Resource Manager. It decides whether a given identity may perform a given operation on a given resource, by evaluating role assignments — a triple of principal + role definition + scope.

The problem: Azure's original model had three subscription-level administrator roles and nothing narrower, which forced a choice between "everyone is a co-administrator" and "nobody has access and deployment is a ticket queue". RBAC lets a grant be narrow in three independent dimensions at once — who, what, and where — so a team can own its resource group without being able to touch the shared network.

Worth adding unprompted: it's free, always on, and not something you deploy. It's a capability of ARM, surfaced through the Microsoft.Authorization provider.

2. What are the three parts of a role assignment?

Answer

Principal (who — a user, group, service principal, or managed identity, identified by an Entra object ID), role definition (what — a named set of permitted operations), and scope (where — a management group, subscription, resource group, resource, or sub-resource).

Nothing else. There's no policy document attached to the identity; the assignment object is the grant, and it's an ARM resource whose name is a GUID.

The follow-on worth volunteering: this is why 403 debugging is a three-item checklist. Exactly one of those three is wrong — or the token is stale.

3. Explain the scope hierarchy and how inheritance works.

Answer

Root → management group (nestable) → subscription → resource group → resource → sub-resource. Assignments inherit downward and only downward. A Reader at the subscription is a Reader on every resource group and resource inside it, including ones created tomorrow.

The critical property: you cannot subtract at a lower scope, because there's no deny. An over-broad assignment at the subscription can't be narrowed at the resource group — it has to be removed where it was made. That asymmetry is why "assign at the narrowest scope that works" is a rule rather than a preference.

Strong answers mention sub-resource scopes (a single blob container, a single secret) as the underused narrow option, and management-group assignments as the common surprise in "why can this person see production".

4. What's the difference between an Entra directory role and an Azure RBAC role?

Answer

Different systems, different stores, almost entirely disjoint effects.

Directory roles (Global Administrator, User Administrator, Application Administrator) govern the tenant: users, groups, app registrations, licences. Assigned through Entra ID, stored in the directory, evaluated by Microsoft Graph.

Azure roles (Owner, Contributor, Reader, Storage Blob Data Reader) govern Azure resources. Assigned through ARM at a resource scope, inherit down the hierarchy.

So: a Global Administrator has no access to any virtual machine by default, and an Owner of a subscription cannot create a user. Both surprise people.

The one bridge: a Global Administrator can elevate access, granting themselves User Access Administrator at root scope /. It's a deliberate break-glass path, it's logged in the activity log, and it should be alarmed on.

5. When would you choose Azure Policy over RBAC?

Answer

They answer different questions and aren't substitutes. RBAC asks who may act. Policy asks what may exist — allowed regions, allowed SKUs, required tags, no public IPs — and it applies regardless of who's asking. An Owner is denied by policy exactly like everyone else.

Use Policy for configuration standards, RBAC for access. Trying to express "no VMs bigger than Standard_D4s" as a custom role produces something unmaintainable that still fails, because a Contributor can create a compliant resource and then modify it.

Diagnostic tell: RequestDisallowedByPolicy names the policy assignment. No role will fix it, and granting a broader role is the classic wasted afternoon.

Bonus for mentioning that Policy at a management group is the closest Azure gets to an AWS SCP, and that Policy can enforce the RBAC model — auditing or denying Owner assignments at subscription scope.

6. What does Azure RBAC cost, and what keeps costing when nothing is happening?

Answer

RBAC itself is free — no meter, no SKU, no per-assignment charge. Custom roles and ABAC conditions are free too.

What costs money is the governance layer: PIM and access reviews need Microsoft Entra ID P2 or Entra ID Governance; Conditional Access needs P1 ⚠️ verify current mapping against current Azure docs. All licensed per user who benefits from the feature, not per tenant — which is the trap, because nothing blocks you technically and you find out at audit.

Nothing keeps costing when idle. The Log Analytics workspace holding your activity log does have an ingestion and retention bill, but administrative logs are low-volume.

The framing that lands well: PIM's cost comparison isn't "P2 for everyone", it's "P2 for the twenty people who currently hold standing Owner" — against the cost of one incident caused by one of them.


Tier 2 — Technical depth

1. Walk me through what happens when a web app reads a blob using its managed identity.

Answer

Two phases that look like one.

Token. DefaultAzureCredential calls the Instance Metadata Service at a link-local address that isn't routable from outside the host. IMDS returns a JWT signed by Entra ID, scoped to an audiencehttps://storage.azure.com, not https://management.azure.com; the wrong audience produces a 403 that looks like a permission problem. The token carries the principal's oid, the tenant tid, group claims, and an expiry. Role assignments are not in the token.

Decision. The request goes to stapp.blob.core.windows.netnot ARM. Storage validates the signature against Entra's published keys, extracts the oid, and asks whether that principal holds …/blobs/read at this scope. The evaluator walks the resource's ancestry — container, account, resource group, subscription, management groups — gathers every applicable assignment, unions their dataActions, and tests the operation string. Any ABAC condition on the matching assignment is evaluated too. Match → read proceeds. No match → 403.

The point to land: the token proves identity; authorisation is looked up fresh at the resource, by the service, against dataActions — not by ARM against actions.

2. Control plane vs. data plane: which roles govern which, and what's the classic mistake?

Answer

Control plane is management.azure.com, evaluated by ARM against a role's actions — creating, configuring, and deleting the resource. Data plane is the service's own endpoint (*.blob.core.windows.net, *.vault.azure.net), evaluated by the service against dataActions — touching the contents.

The classic mistake: Owner does not let you read a blob. Nor does Key Vault Contributor let you read a secret, nor Azure Service Bus Contributor let you send a message. The broad management roles carry no dataActions at all. You need a role with Data in the name: Storage Blob Data Reader, Key Vault Secrets User, Azure Service Bus Data Sender.

Diagnostic shortcut: look at the hostname in the error. If it isn't management.azure.com, no amount of Owner will help.

The nuance that marks a strong answer: Storage Account Contributor can read the account keys, and a key grants unrestricted data access outside RBAC entirely. So control-plane privilege is an indirect path to data privilege — which is exactly why allowSharedKeyAccess = false is a real control and not theatre.

3. There's no explicit deny in Azure RBAC. What does notActions actually do?

Answer

notActions subtracts from a single role definition, evaluated before the union across assignments. It is arithmetic inside one role, not an override across them.

It exists so Microsoft can define Contributor as "everything, minus writing role assignments" without enumerating every action in Azure. It is not a security boundary. Concretely:

Assignment A: Contributor (actions: */*, notActions: Microsoft.Authorization/roleAssignments/write)
Assignment B: User Access Administrator (actions: Microsoft.Authorization/*)
Effective:    full Contributor AND the ability to write role assignments.

A's notActions prevented nothing. The effective permission set is the pure union of every assignment at or above the scope.

The one real override is a deny assignment, which outranks role assignments — created only by the platform (Azure managed applications, formerly Blueprints). You can read them; you cannot author one.

Coming from AWS, this is the biggest single break: patterns built on explicit-deny-wins and permission boundaries don't transfer. The Azure equivalent guardrail is Azure Policy at a management group, a different system with different semantics.

4. How do you deploy role assignments as code, and what's the ordering problem?

Answer

Terraform primary (azurerm_role_assignment, plus azuread for the groups and azapi for PIM), with the plan output posted on the pull request — for RBAC that diff is a human-readable statement of who's gaining or losing which permission, which is the artefact a reviewer actually needs.

Details that show experience:

  • for_each over a keyed map, never count over a list. With count, removing an element shifts every index and Terraform destroys and recreates every assignment below it — a window with no access.
  • Set principal_type ("ServicePrincipal", "Group"). Otherwise the provider does a Graph lookup that races a just-created identity.
  • Set description. Cheapest audit improvement available, and it makes drift queryable — anything without one wasn't made by the pipeline.
  • Use resource_manager_id, not id, for container-scoped assignments; the id is a data-plane URL.
  • Remote state in an Azure Storage backend with use_azuread_auth = true, native blob-lease locking. Directory-per-environment, not workspaces — workspaces share one backend and one credential, so the identity that can apply to dev reaches prod state. When the thing being managed is access, that's the wrong trade.

The ordering problem: role assignments are eventually consistent. depends_on guarantees the order of API calls, not of effect. A resource that needs the grant to be effective at creation — AKS pulling from ACR, a Function App reading a secret at startup — fails even though the assignment exists. The standard, inelegant, correct answer is a time_sleep between them.

Bicep's version of this question is the guid() naming trick: a role assignment's name is a GUID and ARM is idempotent on name, so guid(resourceGroup().id, principalId, roleDefinitionId) makes the deployment re-runnable where a random GUID would create duplicates.

5. Which changes force replacement of a role assignment, and why does that matter?

Answer

Essentially all of them. azurerm_role_assignment has no meaningful in-place update — change the scope, the role, or the principal and Terraform destroys and recreates it.

Why it matters: between destroy and create, nobody has that access. For one assignment that's seconds. For a refactor touching fifty it's a real window, and it lands during a deploy when people are already looking at dashboards. Mitigate by splitting the change into add-then-remove across two applies, rather than letting one apply do both.

The related trap worth volunteering: a system-assigned managed identity gets a new object ID whenever its resource is replaced. The assignment then points at a dead principal — terraform apply succeeds, the pipeline goes green, and the app 403s at runtime. Use user-assigned identities for anything IaC recreates.

6. When would you write a custom role, and when is it the wrong answer?

Answer

Write one when a built-in role is genuinely too broad for a standing grant and none of the several hundred built-ins fit. The canonical case: a support team that must restart VMs but not resize or delete them. No built-in does that.

Don't write one to shave a permission off Contributor. A custom role is a maintenance commitment — it must be updated as Azure adds operations to the providers it covers, and the failure mode is a capability quietly missing months later, diagnosed as a product bug. There's also a cap per tenant ⚠️ verify current limit.

Two constraints and one anti-pattern:

  • Custom roles are tenant-level objects, so two environments applying the same module collide on the name. Define them once in a platform module and reference by ID.
  • assignableScopes can't include root / for a custom role.
  • No wildcards. Microsoft.Compute/* is a grant that widens over time as Azure adds operations — no code change, no review, more permission. Enumerate the actions.

Also worth naming the alternatives that are often better: ABAC conditions narrow an existing assignment at request time (blob tags, path prefixes) without a new role definition, and PIM narrows the time rather than the permission. Narrower, shorter, harder-to-obtain are three different levers, and reaching for a custom role first is a common mistake.


Tier 3 — Scenario and design

1. A developer says "I'm Owner on the subscription and I still get 403 reading a blob." Diagnose it.

Answer

Almost certainly the control-plane/data-plane split, but work the checklist in order rather than guessing — the order is the answer.

  1. Which identity is this? Not who they are — which token was used. DefaultAzureCredential may have picked a different source than they assume, and it can succeed locally with their own permissions and fail in Azure with the managed identity's. az account show, or decode the oid claim.
  2. Which plane? Read the hostname in the error. *.blob.core.windows.net means dataActions, and Owner has none. They need Storage Blob Data Reader. This is the answer ~80% of the time.
  3. Right audience? Hand-rolled token acquisition asking for management.azure.com and calling blob.core.windows.net fails signature-valid/audience-invalid, which reads like a permissions error.
  4. Does the scope cover the resource? az role assignment list -g rg --include-inherited. Assigning at one resource group doesn't cover a resource in a sibling.
  5. Has it propagated? Assignments are eventually consistent, and a cached token carries old claims until it expires. Wait, force a refresh, retest. Testing thirty seconds after a grant proves nothing.
  6. Is it a policy denial? RequestDisallowedByPolicy — different system, no role will help.
  7. Is it a lock? ScopeLocked is a CanNotDelete or ReadOnly lock, possibly inherited from the resource group. Not RBAC.
  8. Deny assignment? If an Owner is refused with no lock and no policy, check for one. Platform-created, outranks everything, can't be authored by you.

Steps 6 and 7 are on the list because they're the failures most often misdiagnosed as RBAC, and both waste an afternoon if you start by granting roles.

2. Design the access model for a company with three environments and eight product teams.

Answer

Boundaries first. Management group per environment tier (dev / non-prod / prod), one subscription per environment per product team or per environment with resource-group separation depending on scale. The subscription is Azure's natural blast-radius, quota, and policy boundary — this is one place the AWS account instinct transfers cleanly. Resource-group separation alone is insufficient, because any subscription-scope assignment reaches every environment inside it.

Grants. Every standing assignment targets an Entra group, at resource-group scope, using a service-specific Contributor rather than Contributor where one fits. Workload identities get dataActions roles at sub-resource scope — one container, one secret — and use user-assigned managed identities so replacement doesn't orphan them.

The environment asymmetry is the design. Dev grants standing write. Prod grants standing read and makes write a PIM activation with approval and a four-hour expiry. Same Terraform module, different .tfvars — which is what makes the model reviewable.

Platform vs. workload split. Platform team holds Reader at the tenant root management group and Contributor on shared networking subscriptions. Product teams cannot touch the shared VNet. RBAC expresses this cleanly because scope and role are independent.

Enforcement, not convention. Azure Policy at the prod management group audits or denies Owner and User Access Administrator assignments at subscription scope, and requires description on new assignments. Convention says "we don't do that"; policy makes it fail.

Repository split. Management-group and subscription-scope assignments live in a separate repo with different reviewers from workload-scope ones. Same tooling, different approval gravity.

Operations. Nightly terraform plan for drift plus a Resource Graph query for assignments outside state — plan only sees what it manages, so an undeclared portal grant is invisible to it. Access reviews recurring on every group holding a privileged role. Activity log to Log Analytics with alerts on privileged assignment and on elevate access. Two tested break-glass accounts with standing Owner at root and paging on sign-in.

And close the parallel doors: allowSharedKeyAccess = false, Key Vault RBAC mode, disableLocalAccounts on AKS, Entra-only auth on SQL. Otherwise the whole model is documentation.

3. Someone hand-edited role assignments in the portal. How do you find out, and how do you get back to a clean plan?

Answer

Detection — and the trap is that terraform plan alone doesn't catch it. Plan only reports drift in resources it manages. An assignment created by hand that your module never declared is invisible: the plan is clean and the grant is live. So you need two mechanisms.

Managed-resource drift: scheduled terraform plan -detailed-exitcode in CI, failing the job on exit code 2, running with a read-only federated credential.

Undeclared assignments: an Azure Resource Graph query across the estate. This is where setting description on every managed assignment pays off — it turns "is this ours?" into a filter:

authorizationresources
| where type =~ "microsoft.authorization/roleassignments"
| extend description = tostring(properties.description)
| where isempty(description)
| project subscriptionId, scope = tostring(properties.scope),
          principalId = tostring(properties.principalId)

Attribution: AzureActivity filtered to MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE gives you the caller, the IP, the scope, and the request body. Note that if you assign to groups, membership changes are in Entra AuditLogs, not AzureActivity — an estate monitoring only the latter has a blind spot exactly where its access model lives.

Remediation — two honest options and one wrong one.

Legitimate grant: import it. terraform import, or terraform plan -generate-config-out=drift.tf to draft the block, then a pull request that explains why the access exists.

Illegitimate: delete it, and treat it as an incident if the role was privileged.

The wrong option: adding it to state without a pull request explaining it. That launders an unreviewed grant into an approved one, which is worse than the drift.

Prevention. Remove the ability to make the change: nobody holds standing Owner or User Access Administrator, so the portal path requires a PIM activation with justification — which is itself the audit record. Alert on privileged assignment writes so a legitimate emergency grant gets same-day follow-up rather than becoming permanent by inertia.

The drill that validates all of it: delete a resource group's assignments and restore them with terraform apply. If access isn't fully restored, you have undeclared grants — and that's the finding, not the failure. Most estates fail this the first time.

4. A service principal has been compromised. Walk me through containment.

Answer

The instinct is to delete the role assignments, and that's necessary but not sufficient — which is the point of the question.

Why deleting assignments isn't enough: removal is eventually consistent in both directions, and an already-issued token carries its claims until expiry. The revoked grant keeps working for the life of the cached token plus the propagation window. For a compromise, that window is the whole problem.

Order of operations:

  1. Disable the principal. This stops new tokens immediately.
    az ad sp update --id <appId> --set accountEnabled=false
  2. Revoke existing sessions/refresh tokens, so cached credentials die rather than expiring.
  3. Remove the credentials — client secrets and certificates on the app registration. If a secret existed at all, that's the root cause and the fix is workload identity federation.
  4. Then remove role assignments, and check --all across the estate rather than the one scope you know about:
    az role assignment list --assignee <oid> --all -o table
  5. Establish blast radius from the activity log. Every action the principal took, and critically whether it wrote any role assignments — a compromised principal with Microsoft.Authorization/roleAssignments/write may have created other access, and deleting the original changes nothing about those.
    AzureActivity
    | where Caller == "<appId or oid>"
    | where ActivityStatusValue == "Success"
    | project TimeGenerated, OperationNameValue, scope = tostring(parse_json(Properties).entity)
  6. Check for data-plane access exercised outside RBAC — if the principal could read storage keys or SAS tokens, those credentials are still valid after you've removed every role. Rotate them.
  7. Check custom role definitions for changes, and deny assignments and PIM eligibility for anything added.

The structural lesson to close on: the reason roleAssignments/write is the most dangerous permission in Azure is exactly this — containment of a principal that held it is unbounded, because you have to find everything it granted. It's why the pipeline identity gets Role Based Access Control Administrator scoped to specific subscriptions rather than Owner, and why elevate access should page someone.


Next: Glossary & Cheatsheet →

← Back to the Azure RBAC overview · ← Previous: Production