8. Interview Questions
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
audience — https://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.net — not 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_eachover a keyed map, nevercountover a list. Withcount, 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, notid, for container-scoped assignments; theidis 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.
assignableScopescan'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.
- Which identity is this? Not who they are — which token was used.
DefaultAzureCredentialmay 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 theoidclaim. - Which plane? Read the hostname in the error.
*.blob.core.windows.netmeansdataActions, and Owner has none. They needStorage Blob Data Reader. This is the answer ~80% of the time. - Right audience? Hand-rolled token acquisition asking for
management.azure.comand callingblob.core.windows.netfails signature-valid/audience-invalid, which reads like a permissions error. - 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. - 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.
- Is it a policy denial?
RequestDisallowedByPolicy— different system, no role will help. - Is it a lock?
ScopeLockedis aCanNotDeleteorReadOnlylock, possibly inherited from the resource group. Not RBAC. - 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:
- Disable the principal. This stops new tokens immediately.
az ad sp update --id <appId> --set accountEnabled=false - Revoke existing sessions/refresh tokens, so cached credentials die rather than expiring.
- 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.
- Then remove role assignments, and check
--allacross the estate rather than the one scope you know about:az role assignment list --assignee <oid> --all -o table - 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/writemay 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) - 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.
- 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 →