02 · Management Groups & Governance at Scale¶
Level 4, Module 01 built the management group tree; this module goes deeper on the mechanics of governing through it: how policy and RBAC actually inherit, how to model exceptions without weakening the baseline, and how to keep hundreds of subscriptions provably compliant rather than compliant-by-hope.
Policy inheritance and evaluation order¶
Policies assigned at a higher scope apply to everything below it; a resource can be subject to policies from the tenant root, every management group above its subscription, the subscription, and the resource group — all simultaneously.
az policy assignment list-resources \
--name deny-public-blob-access \
--resource-group rg-corp-app1
When multiple assignments conflict (one Audit, one Deny on the same
condition), Deny always wins regardless of assignment order or
scope depth — there's no "closer scope overrides" rule for effect severity,
only for whether a policy applies at all (via exclusions).
Gotcha: a resource group nested three management groups deep can be
non-compliant against a policy assigned at the tenant root without anyone
at the resource group level realizing it — always check compliance with
az policy state list scoped to what you're accountable for, not just the
policies you personally assigned.
Exemptions vs. exclusions¶
- An exclusion (
--not-scopeson assignment) removes a scope from the assignment entirely — the policy doesn't evaluate there at all. - A policy exemption lets a specific resource be non-compliant with a specific assignment for a documented, time-boxed reason, while the assignment still applies (and still evaluates) everywhere else.
az policy exemption create \
--name exempt-legacy-vm-encryption \
--policy-assignment "/subscriptions/xxxx/providers/Microsoft.Authorization/policyAssignments/require-disk-encryption" \
--exemption-category Waiver \
--expires-on 2026-12-31T00:00:00Z \
--scope "/subscriptions/xxxx/resourceGroups/rg-legacy/providers/Microsoft.Compute/virtualMachines/vm-legacy-app" \
--display-name "Legacy app pending decommission Q4"
Gotcha: exclusions are invisible in compliance reporting (the resource just never shows up as evaluated), while exemptions show up as "Exempt" with a reason and expiry — prefer exemptions for anything you'll need to justify in an audit; broad exclusions accumulate into governance blind spots nobody remembers creating.
RBAC at management group scope¶
az role assignment create \
--assignee "group-platform-engineers@example.com" \
--role "Contributor" \
--scope "/providers/Microsoft.Management/managementGroups/alz-landingzones"
Gotcha: granting Owner or Contributor at a management group scope
grants it to every subscription under that scope, present and future —
a role assigned at alz-landingzones automatically applies to a brand new
subscription vended into alz-corp a year later, with nobody explicitly
re-granting it. This is powerful for platform teams and dangerous if
assigned too broadly; always scope role assignments to the narrowest level
that satisfies the actual need, and prefer custom roles over Owner.
Custom roles¶
az role definition create --role-definition '{
"Name": "Landing Zone Network Operator",
"Description": "Manage VNets and peerings, no RBAC or policy changes",
"Actions": [
"Microsoft.Network/virtualNetworks/*",
"Microsoft.Network/virtualNetworks/peer/action"
],
"NotActions": [
"Microsoft.Authorization/*/write"
],
"AssignableScopes": [
"/providers/Microsoft.Management/managementGroups/alz-landingzones"
]
}'
NotActions subtracts from Actions for the purposes of what the role
grants, but does not work as a deny — if the same identity has a
different role assignment granting Microsoft.Authorization/*/write
through some other path, that grant still applies. NotActions only
narrows this specific role definition, it isn't a security boundary on its
own.
Compliance reporting at scale¶
az policy state summarize --management-group alz
az policy state list \
--management-group alz \
--filter "ComplianceState eq 'NonCompliant'" \
--query "[].{resource:resourceId, policy:policyDefinitionName}" \
-o table
Gotcha: az policy state summarize reflects the last evaluation
cycle (policy evaluates on a roughly 24-hour cycle plus on resource
create/update events) — a resource fixed five minutes ago can still show
NonCompliant until the next cycle runs; trigger an on-demand scan with
az policy state trigger-scan when you need current results for a change
you just made, rather than assuming the dashboard is live.
Governance mechanisms compared¶
| Mechanism | Scope | Purpose | Auditable? |
|---|---|---|---|
| Policy assignment | Any scope | Enforce/audit a rule | Yes — compliance state |
| Policy exemption | Specific resource | Document a justified exception | Yes — visible as Exempt |
Assignment exclusion (--not-scopes) |
Sub-scope | Remove scope from evaluation | No — invisible in reports |
| RBAC role assignment | Any scope | Grant access to act | Yes — via az role assignment list |
| Custom role | Any scope | Narrow a grant beyond built-ins | Yes, but NotActions isn't a deny |
How It Actually Works¶
Management groups exist purely as a scoping node in ARM's authorization
hierarchy — creating one doesn't provision any resource, it inserts an
entry into your tenant's management-group tree that RBAC role assignments
and Azure Policy assignments can target, and ARM's authorization check
(from Level 1's capstone) walks up this tree from a resource through its
resource group, subscription, and every management group above it when
evaluating whether a caller's role assignment or a deny-effect policy
applies — this upward walk, not any per-resource configuration, is the
actual mechanism behind policy/RBAC inheritance. Azure Policy's
DeployIfNotExists and Modify effects, when assigned at a management
group, are evaluated per-resource at the resource's own scope during the
same compliance-scan cycle from Level 3's IaC module, but the remediation
deployment they trigger runs under a managed identity created for the
policy assignment at the management-group scope — which is why that
identity needs RBAC roles granted explicitly at each subscription it must
remediate resources in, even though the policy assignment itself lives
several levels above.
Blueprints/deployment stacks vs. Policy vs. RBAC are three different enforcement points in that same request pipeline: RBAC gates who can call an operation, Policy gates what a request's properties are allowed to be (evaluated inline, before the resource provider processes it), and a deployment stack (or Blueprint) additionally tracks a set of resources as a unit so it can apply lifecycle protections (deny-delete on managed resources) and detect drift against the originally deployed template — the three mechanisms compose because they intercept the request lifecycle at genuinely different stages, not because one supersedes another.
Cheat sheet¶
| Command | Purpose |
|---|---|
az policy assignment list-resources |
See what a specific assignment applies to. |
az policy exemption create --expires-on |
Grant a documented, time-boxed compliance waiver. |
az role assignment create --scope <mg-id> |
Grant RBAC inherited by everything under a management group. |
az role definition create |
Define a custom role narrower than built-ins. |
az policy state summarize --management-group |
Get an aggregate compliance view. |
az policy state trigger-scan |
Force an on-demand compliance re-evaluation. |
Exercise¶
- Assign a policy at a management group scope and confirm (via
list-resources) it applies to a subscription two levels below. - Create a policy exemption with an expiry date for one resource, and
contrast how it appears in
az policy state listversus how an exclusion would (not appear at all). - Create a custom role with
NotActionsremoving one permission, assign it at a management group, and verify what it actually restricts (and what it doesn't, if another role grants the same action). - Run
az policy state trigger-scanafter fixing a non-compliant resource and confirm the compliance state updates without waiting for the next cycle. - Clean up any test policy assignments, exemptions, and role assignments when finished.