Most Azure estates grow faster than anyone can review them, so the settings that matter (where data lives, whether storage is reachable from the internet, whether logs are collected) drift subscription by subscription unless something enforces them centrally. Azure Policy and management groups are the native answer: management groups give the tenant a hierarchy, and policy assignments placed on that hierarchy evaluate every resource request beneath them. This guide covers how the hierarchy and the policy engine actually behave, which effects to use for which job, how to roll out deny rules without breaking production, how exemptions and deny assignments fit, and how to measure whether the guardrails work. It is written for cloud platform engineers, security architects and auditors who own an Azure tenant with more than a handful of subscriptions.
Key takeaways
- The hierarchy is the control plane for governance. Policy and role assignments made on a management group flow to every subscription and resource below it, so the shape of the tree decides how precise your guardrails can be.
- Effects are not interchangeable in practice. Deny blocks, audit only reports, modify and deployIfNotExists change things using a managed identity, and denyAction stops deletes; pick per control, not per habit.
- Roll out in audit first. Assign with enforcementMode DoNotEnforce or the audit effect, read the compliance data, fix or exempt, then switch to deny using resource selectors to stage by region.
- Exemptions are the real attack surface. Whoever can write exemptions or edit assignments can switch your guardrails off, so treat Resource Policy Contributor and management group Owner as privileged roles under PIM.
- Policy is not authorisation. It constrains what resources can look like, not who can act on them; it complements Azure RBAC, Entra ID controls and logging rather than replacing them.
How management groups structure an Azure tenant
Every Entra ID tenant has one root management group, shown as Tenant root group, whose ID equals the tenant ID. It cannot be moved or deleted, and by default nobody has access to it: a Global Administrator must elevate to the User Access Administrator role on the root to manage it. Microsoft documents these limits: 10,000 management groups per directory, up to six levels of depth (not counting the root or the subscription level), and exactly one parent for every management group or subscription.
| Level | Typical content | What to assign here |
|---|---|---|
| Tenant root group | Nothing directly | Only "must have" items; Microsoft's landing zone guidance limits root assignments to keep inherited policy easy to debug |
| Intermediate root (prefixed, e.g. "contoso") | All governed subscriptions | Organisation-wide guardrails: allowed regions, diagnostic settings, security baseline initiative |
| Platform (management, connectivity, identity, security) | Shared hub, logging and identity subscriptions | Platform-specific controls, tightly scoped RBAC |
| Landing zones, then Corp / Online / Local | Workload subscriptions | Network rules that differ for private versus internet-facing workloads |
| Sandbox | Experimentation subscriptions | Looser policies, no connectivity to corporate networks |
| Decommissioned | Cancelled subscriptions awaiting deletion | Deny-all style controls |
The Cloud Adoption Framework recommends keeping the tree flat (ideally three to four levels), not mirroring the organisation chart, and not creating management groups for production, test and development or for regions unless a regulatory requirement is location-based. Separate environments by subscription inside the same management group instead. See the Cloud Landing Zone Security Guide for the wider landing zone design.
Protect the hierarchy itself
- Default management group: new subscriptions land under the root unless you set
defaultManagementGroupin the root's hierarchy settings. Point it at a Sandbox or quarantine group so unmanaged subscriptions inherit restrictive policy from day one. - Require authorisation: by default any principal in the tenant can create management groups. Set
requireAuthorizationForGroupCreationto true so only principals with write on the root can create them. Both settings needMicrosoft.Management/managementgroups/settings/write, which the built-in Hierarchy Settings Administrator role carries. - Moves are privileged: moving a subscription needs write and role assignment write on the child plus write on both the current and target parent. A subscription moved to a looser branch silently loses its guardrails, which is why ATT&CK tracks T1666 Modify Cloud Resource Hierarchy as a defence impairment technique.
- Roles: Management Group Contributor can create, rename, move and delete groups but cannot assign access or policy; Owner can do both. Platform teams should hold management group roles through just-in-time activation. See the Privileged Identity Management (PIM) Guide.
Azure Policy building blocks
- Policy definition: a JSON rule (an
ifcondition and atheneffect) plus parameters. ModeAllevaluates every resource type;Indexedevaluates only types that support tags and location. Resource Provider modes such asMicrosoft.Kubernetes.Datahand evaluation to the provider. - Initiative (policy set): a group of definitions assigned together, with shared parameters and
policyDefinitionReferenceIdvalues for each member. Regulatory compliance initiatives set category "Regulatory Compliance" and group policies into controls marked Customer, Microsoft or Shared responsibility. - Assignment: binds a definition or initiative to a scope (management group, subscription, resource group or resource) with parameter values. Key properties:
enforcementMode,notScopes,nonComplianceMessages,resourceSelectors,overrides, and a managedidentityfor effects that change resources. - Exemption: removes a resource or scope from one assignment (or selected definitions in an initiative) with a category of Waiver or Mitigated and an optional
expiresOndate. Exempt resources remain visible with the compliance state Exempt. - Remediation task: applies a modify or deployIfNotExists policy to resources that already existed before the assignment.
When several assignments apply to one resource, Microsoft describes the result as "cumulative most restrictive": each assignment is evaluated independently, and any deny wins. A child scope cannot loosen a deny inherited from a parent; only an exemption or a notScopes entry on the parent assignment can.
Choosing the right effect
Microsoft lists eleven effects. They are evaluated in a fixed order on a create or update request: disabled first, then append and modify (which can alter the request), then deny, audit, manual, auditIfNotExists and finally denyAction. After the resource provider returns success, auditIfNotExists and deployIfNotExists check related resources.
| Effect | What it does | Use it for | Watch out for |
|---|---|---|---|
| deny | Rejects a non-compliant create or update request | Hard guardrails: allowed locations, no public network access on storage or Key Vault, approved SKUs | Breaks pipelines that have not been tested; existing resources are only flagged, not changed |
| audit | Allows the request, marks the resource non-compliant | Discovery, early rollout, controls you cannot yet enforce | Easy to leave in audit for years |
| modify | Adds, updates or removes properties (often tags) during the request | Tag inheritance, enforcing a property value such as minimum TLS | Needs a managed identity with write roles |
| append | Adds fields to the request | Legacy patterns; prefer modify where supported | Can mask a deny because it runs first |
| auditIfNotExists | Checks a related child or extension resource after deployment | "Is diagnostic logging configured?", "Is the agent installed?" | Evaluation is delayed, not real-time |
| deployIfNotExists | Deploys an ARM template when the related resource is missing | Diagnostic settings to a central workspace, Defender plans, backup | Its identity often needs broad roles; protect it as a privileged NHI |
| denyAction | Blocks an action on matching resources; today only delete | Protecting production databases, log workspaces and key vaults from deletion | Does not stop deletion via subscription removal or parent cascade |
| manual | Compliance set by attestation | Controls that cannot be checked from resource properties | Attestations go stale |
| disabled | Rule not evaluated | Parameterised switch to turn one policy off inside an initiative | Silent; review overrides that set it |
| mutate, addToNetworkGroup | Kubernetes mutation; adding resources to Azure Virtual Network Manager network groups | Specialised scenarios | Scope-specific behaviour |
Deny versus deployIfNotExists: deny keeps the estate clean but shifts work to deployment teams; deployIfNotExists fixes things automatically but relies on a managed identity that can write across the scope, and drift between deployment and remediation is visible to attackers too. A sound pattern is deny for exposure and location controls, deployIfNotExists for logging and agent coverage, and audit only while a control is being rolled out.
denyAction details: a blocked delete returns HTTP 403. To keep a resource group delete from removing protected resources, the definition must use mode Indexed with cascadeBehaviors set to deny for resource groups. Policy assignments, deny assignments, locks, deployment stacks and subscriptions are exempt from denyAction to prevent lockout.
Deny assignments, locks and where policy stops
Azure Policy and Azure RBAC answer different questions. Policy evaluates the content of a request; RBAC decides whether the caller may make it. Deny assignments sit on the RBAC side: they block named principals from specific actions even when a role assignment grants them, can exclude principals, and can be prevented from applying to child scopes with DoNotApplyToChildScopes.
| Control | Question it answers | Who creates it | Typical use |
|---|---|---|---|
| Azure Policy deny | Is this resource configuration allowed? | Anyone with policy assignment rights | Configuration guardrails at scale |
| Azure Policy denyAction | May this resource be deleted? | Anyone with policy assignment rights | Deletion protection by tag or type |
| Role assignment (RBAC) | May this principal perform this action? | Owner or User Access Administrator | Granting access |
| Deny assignment | Is this principal blocked from this action regardless of roles? | Azure only (for example deployment stacks with deny settings); customer-managed deny assignments are in private preview | Protecting stack-managed resources from change |
| Resource lock | Can this resource be deleted or changed at all? | Owner or User Access Administrator | Simple protection for individual critical resources |
You cannot author your own deny assignments in general availability. If you need "nobody may do X except the platform team", the options are deployment stacks with deny settings, denyAction for deletes, or tightly scoped role design. For the access side, see the Azure RBAC and Service Principals Guide.
Azure Policy for Kubernetes
Azure Policy extends Gatekeeper v3, the Open Policy Agent admission controller, to AKS clusters (as an add-on) and Arc-enabled Kubernetes clusters (as an extension). Definitions use the Microsoft.Kubernetes.Data mode and support audit, deny, disabled and mutate. The add-on pulls assignment changes every 15 minutes, runs a full cluster scan every 15 minutes, and reports violations from the last 45 minutes.
- Exclusions: the AKS add-on automatically excludes
kube-systemandgatekeeper-system; Microsoft also recommends excludingazure-arcthrough the namespace exclusions parameter on Arc clusters. - Limits to know: 10,000 pods per cluster, 500 non-compliant records per policy per cluster, Linux node pools only, and no support for a separately installed Gatekeeper.
- Test first: assign with enforcement disabled to get reporting without denying admission requests.
For admission control design beyond what Azure Policy covers, see the Kubernetes Admission Control Guide.
How to implement: a phased rollout
- Fix the hierarchy first. Create the intermediate root and the platform, landing zone, sandbox and decommissioned groups; set the default management group; enable require authorisation; move subscriptions in. Keep root assignments empty.
- Assign a baseline in audit. Assign the Microsoft cloud security benchmark or a regulatory initiative at the intermediate root, plus your own initiative of organisation rules (allowed locations, required tags, diagnostic settings). Use audit effects or
enforcementMode: DoNotEnforce; note that DoNotEnforce writes no activity log entries for would-be denials, so read compliance results instead. - Read the evidence. A new assignment starts evaluating about five minutes after creation; the standard cycle re-evaluates every 24 hours, and you can force a scan with
az policy state trigger-scanorStart-AzPolicyComplianceScan. Triage every non-compliant resource into fix, exempt (Waiver or Mitigated, with an expiry date) or change the rule. - Test in a pilot ring. Switch one initiative to deny on a sandbox or pilot management group. Run real infrastructure-as-code pipelines against it and capture every failure. Add
nonComplianceMessagesthat tell engineers what to change and where to ask for an exemption. - Stage enforcement. Use
resourceSelectorswithresourceLocationto enable deny region by region, oroverridesof kindpolicyEffectto flip effects for selected definitions without editing them. Microsoft supports up to 10 selectors and 10 overrides per assignment. - Remediate existing resources. For modify and deployIfNotExists, create remediation tasks (one policy per task). Grant the assignment identity only the roles listed in
roleDefinitionIds, and grant manually any roles needed outside the assignment scope. - Protect from deletion. Add denyAction policies for tagged production data stores and log workspaces, with resource group cascade set to deny.
- Roll back cleanly. Keep every definition, initiative and assignment in source control and deploy through a pipeline. Rollback is a change of effect parameter or
enforcementModeback to DoNotEnforce, not deletion of the assignment, so compliance history and exemptions survive. See the Policy-as-Code for Authorisation Guide and the Infrastructure as Code Security Guide.
Decision guidance by organisation size
- Small estates (a few subscriptions, one platform team): one intermediate root plus Sandbox is enough. Assign one built-in security initiative and a short custom initiative with five to ten deny rules (locations, public network access on storage, SQL and Key Vault, required tags). Skip deployIfNotExists until logging is centralised.
- Large or regulated estates: adopt the landing zone hierarchy, separate policy authors from exemption approvers, require an expiry on every Waiver, use regulatory compliance initiatives for evidence, and run policy changes through the same pipeline and peer review as code. Use subscription vending so workload teams never need management group roles.
- Built-in versus custom definitions: built-ins are maintained by Microsoft and support
definitionVersionoverrides; custom definitions let you match your own naming and tagging standards but you own their testing. Wrap both in your own initiatives so assignment counts stay manageable. - notScopes versus exemptions:
notScopesremoves a scope from evaluation entirely and leaves no compliance record; an exemption keeps the resource visible as Exempt and can expire. Prefer exemptions for anything an auditor might ask about.
Where identity fits
Every assignment that uses modify or deployIfNotExists needs a managed identity, and that identity performs ARM deployments across the whole assignment scope. At an intermediate root, that is a non-human identity with write access to every subscription. Prefer user-assigned identities you can inventory, grant only the roles the definition needs, and review them alongside other service principals. Equally, the humans and pipelines that can write Microsoft.Authorization/policyAssignments or policyExemptions (Resource Policy Contributor, Security Admin, Owner) can disable controls, so they belong in privileged access reviews. See the Cloud Workload Identity Guide.
Detection and measurement
- Activity log: management groups appear in Azure Monitor activity logs, so role and policy assignment changes at management group scope are visible; route them with diagnostic settings to Log Analytics or a SIEM. Alert on writes and deletes of policy assignments, exemptions, hierarchy settings and subscription moves.
- Compliance data: Azure Policy compliance states (compliant, non-compliant, exempt, unknown) per assignment, queryable through the Policy Insights API and Azure Resource Graph.
- Elevation: alert when a Global Administrator elevates to User Access Administrator at root, and review who did it.
| Metric | What good looks like |
|---|---|
| Subscriptions outside the governed intermediate root | Zero, checked weekly |
| Share of baseline controls enforced with deny or deployIfNotExists rather than audit | Rising every quarter, with a dated plan for the remainder |
| Non-compliant resources against the security initiative | Falling trend; new non-compliance fixed within an agreed SLA |
| Exemptions without an expiry date, and exemptions past expiry | Zero Waivers without expiresOn |
| Policy assignment or exemption changes not made through the pipeline | Zero; every one investigated |
| Mean time from new deny rule to production enforcement | Tracked so audit phases do not stall |
For log retention and forwarding, see the Cloud Logging and Audit Trail Guide.
Common failure patterns
- Guardrails that never leave audit: the dashboard shows hundreds of non-compliant storage accounts that still allow public network access. In the Storm-2949 Azure attack, the intruder changed storage network access settings, pulled account keys with
listkeysand wrote SQL server firewall rules; deny rules on public network access and shared key access raise the bar for exactly those moves, although a principal who can also edit policy can still remove them. - Everything at the root: assignments on the tenant root group are hard to debug, cannot be scoped away from break-glass or platform subscriptions cleanly, and every new subscription inherits them before anyone has reviewed it.
- Open exemption rights: engineers given Resource Policy Contributor to "unblock themselves" create permanent Waivers. Approvals should come from a separate group and every Waiver should expire.
- Over-privileged remediation identities: a deployIfNotExists identity with Owner at the intermediate root is one of the most powerful identities in the tenant and is rarely reviewed.
- Assuming policy fixes existing resources: deny only affects new requests; resources created before the assignment remain until remediated or rebuilt.
- Unprotected hierarchy settings: leaving the default management group as root and group creation open lets new subscriptions and groups appear outside the governed branch. See the Cloud Misconfiguration Guide.
Framework mapping
| Framework | Reference | How Azure Policy and management groups support it |
|---|---|---|
| NIST CSF 2.0 | PR.PS-01, GV.PO-01, DE.CM-09 | Configuration baselines applied at scale; documented policy; continuous evaluation of resources |
| NIST SP 800-53 Rev. 5 | CM-2, CM-6, CM-7, CM-3, CA-7, AC-6 | Baselines and settings as definitions; least functionality via deny; change control through pipelines; continuous compliance data; least privilege for policy and remediation identities |
| CIS Controls v8.1 | Control 4 (Secure Configuration of Enterprise Assets and Software) | Secure configuration process enforced as policy |
| ISO/IEC 27001:2022 | Annex A 8.9 Configuration management | Defined, enforced and monitored configurations |
| MITRE ATT&CK v20 | T1666, T1578, T1535, T1485 | Detect hierarchy moves; deny unapproved compute changes and unused regions via allowed locations; denyAction against deletion of protected data stores |
Practitioner checklist
- Create an intermediate root and keep the tenant root group free of routine assignments.
- Set
defaultManagementGroupto a restricted group and enablerequireAuthorizationForGroupCreation. - Assign a security baseline initiative and an organisation initiative at the intermediate root, starting in audit or DoNotEnforce.
- Move exposure, location and encryption controls to deny through a pilot ring and region-staged resource selectors.
- Add denyAction with resource group cascade deny for tagged production data stores and log workspaces.
- Run remediation tasks for modify and deployIfNotExists, using least-privilege user-assigned identities.
- Require an expiry and a named approver for every Waiver exemption; review expired and open-ended exemptions monthly.
- Put management group, policy and exemption write roles behind PIM, and alert on root elevation and subscription moves.
- Manage definitions, initiatives and assignments as code with peer review, and roll back by effect or enforcement mode, not deletion.
Standards and references
- Microsoft Learn: What are Azure management groups?
- Microsoft Learn: Azure Policy definitions effect basics
- Microsoft Learn: Azure Policy assignment structure
- Microsoft Learn: Azure Policy exemption structure
- Microsoft Learn: Understand Azure deny assignments
- Microsoft Learn: Understand Azure Policy for Kubernetes clusters
- Microsoft Cloud Adoption Framework: Management groups design area
- NIST: SP 800-53 Rev. 5
Related NHI Mgmt Group resources: Microsoft Azure Security Guide · Active Directory and Entra ID Hardening Guide · AWS Organizations and SCPs Guide · Google Cloud Organization Policy Guide · Cloud Security Guide