In this blog post Best Practices for Azure Management Groups for Scale and Security we will share battle-tested guidance to design, secure, and automate your Azure governance at scale. Whether you run one subscription or hundreds, the right management group strategy prevents sprawl, reduces risk, and accelerates delivery.
At a high level, Azure Management Groups are folders for your subscriptions. They let you apply policies, permissions, and standards once and inherit them everywhere beneath. In this post, we’ll first explain how the technology works, then walk through a practical blueprint you can implement today—with CLI and Bicep examples along the way.

How Azure Management Groups work under the hood
Management groups sit above subscriptions in the Azure Resource Manager (ARM) hierarchy. ARM evaluates access (RBAC) and governance (Policy) from the top down. Anything you assign at a higher scope—tenant root, management group—flows down to child management groups, subscriptions, resource groups, and resources unless explicitly overridden.
Key building blocks:
- Hierarchy — A tree of management groups with a single tenant root. Keep it shallow and intentional.
- RBAC — Roles assigned at a management group apply to all child scopes. Use it for platform guardrails, not day-to-day app ownership.
- Azure Policy — The policy engine evaluates resources against rules (deny/audit/append/modify/deployIfNotExists). Assign policy or initiatives at a management group to make standards the default.
- Inheritance — Lower scopes inherit from higher ones. Exemptions can carve out controlled exceptions without weakening your baseline.
- Automation — ARM/Bicep, Azure CLI, and Git-based workflows let you define governance as code and promote changes safely.
Design a clear and scalable hierarchy
Your hierarchy should mirror how you govern, not how teams are organized today. A proven pattern looks like this:
- Root — The tenant root management group. Keep it minimal and tightly controlled.
- Platform — Shared services like networking, identity, logging, security tooling.
- Landing Zones — Where apps live. Segment by environment and risk: Prod, NonProd, Sandbox.
- Special Purpose — Optional groups for Research, Partner, or Quarantine/Decommissioned.
Why this works: platform teams own the Platform branch with stricter controls; app teams operate within Landing Zones with guardrails but more freedom. This keeps security and cost hygiene centralized without handcuffing delivery teams.
Naming and tagging conventions
- Use consistent names like
mg-plat,mg-lz-prod,mg-lz-nonprod,mg-sandbox. - Tag at subscription/resource levels (e.g.,
Environment,CostCenter,DataSensitivity). Use policy to enforce required tags.
Core governance controls to apply at management groups
1) Access control with RBAC
- Assign Reader broadly; restrict powerful roles.
- Use Owner sparingly; prefer Contributor + scoped custom roles.
- Enable Privileged Identity Management (PIM) for time-bound elevation on management group roles.
# Example: Assign Reader at a management group
az role assignment create \
--assignee <aadObjectId-or-upn> \
--role "Reader" \
--scope "/providers/Microsoft.Management/managementGroups/mg-lz-prod"
2) Policy baseline
- Security: Deny public IP on PaaS where not needed, enforce private endpoints, restrict open RDP/SSH, require Defender for Cloud plans.
- Compliance: Restrict allowed regions and SKUs, enforce encryption, require specific tags, ensure resource locks where appropriate.
- Operations: Deploy diagnostic settings to Log Analytics, enforce Azure Monitor Agent, standardize resource naming and backup policies.
# Example: Assign 'Allowed locations' at a management group
az policy assignment create \
--name restrict-locations \
--display-name "Restrict allowed locations" \
--policy /providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c \
--params '{"listOfAllowedLocations": {"value": ["australiaeast","australiasoutheast"]}}' \
--scope /providers/Microsoft.Management/managementGroups/mg-lz-prod
3) Cost guardrails
- Apply budgets at management group or subscription scope.
- Use policy to restrict expensive SKUs in NonProd and Sandbox.
Policy at scale patterns that work
Deny by default, allow via exception
Start with deny-or-restrict policies for high-risk items (public exposure, unapproved regions). Then grant policy exemptions for specific scopes with justifications and expiry dates. This maintains a strong baseline while unblocking edge cases.
DeployIfNotExists for shared services
Use DeployIfNotExists (DINE) policies to automatically deploy agents or settings—diagnostic settings, Defender plans, Azure Monitor Agent. Assign DINE at the management group with a managed identity that has rights in child subscriptions, and use remediation tasks to backfill drift.
Append/Modify to shape configuration
Append and Modify can inject tags, TLS settings, or network rules without breaking developer workflows. They’re excellent for progressive standardization.
Automation and “governance as code”
Put your hierarchy, RBAC, and policy assignments into Git. Use Azure DevOps or GitHub Actions to validate and deploy changes via pull requests. This gives you history, reviews, and the ability to promote from NonProd to Prod safely.
Bicep examples
Create management groups and assign policy at the management group scope. Deploy at the tenant scope for management group creation, and at the management group scope for policy assignment.
// File: mg-hierarchy.bicep
// Deploy at tenant scope
// az deployment tenant create --template-file mg-hierarchy.bicep
targetScope = 'tenant'
@description('Parent management group ID (e.g., the root)')
param parentMgId string
resource lzNonProd 'Microsoft.Management/managementGroups@2021-04-01' = {
name: 'mg-lz-nonprod'
properties: {
displayName: 'Landing Zone - NonProd'
details: {
parent: {
id: '/providers/Microsoft.Management/managementGroups/${parentMgId}'
}
}
}
}
resource lzProd 'Microsoft.Management/managementGroups@2021-04-01' = {
name: 'mg-lz-prod'
properties: {
displayName: 'Landing Zone - Prod'
details: {
parent: {
id: '/providers/Microsoft.Management/managementGroups/${parentMgId}'
}
}
}
}
// File: policy-assignments.bicep
// Deploy at management group scope
// az deployment mg create --management-group-id mg-lz-prod --template-file policy-assignments.bicep
targetScope = 'managementGroup'
@description('Allowed locations policy definition id')
param allowedLocationsDefId string
var allowedRegions = [ 'australiaeast', 'australiasoutheast' ]
resource allowedLocationsAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = {
name: 'restrict-locations'
properties: {
displayName: 'Restrict allowed locations'
policyDefinitionId: allowedLocationsDefId
parameters: {
listOfAllowedLocations: {
value: allowedRegions
}
}
enforcementMode: 'Default'
}
}
Baseline scripts with Azure CLI
# Create a management group
az account management-group create \
--name mg-lz-nonprod \
--display-name "Landing Zone - NonProd"
# Move a subscription into the group
az account management-group subscription add \
--name mg-lz-nonprod \
--subscription <subscriptionId>
# Assign a built-in initiative (example: audit VMs without backup)
az policy assignment create \
--name audit-vm-backup \
--display-name "Audit VMs without backup" \
--policy-set-definition /providers/Microsoft.Authorization/policySetDefinitions/1f3afdf9-2bca-4215-8d5e-7d0df97ca9f0 \
--scope /providers/Microsoft.Management/managementGroups/mg-lz-nonprod
Security operations at the management group level
- PIM everywhere: Require just-in-time elevation for Contributor/Owner at management group scopes. Approval + MFA for production branches.
- Break-glass accounts: Keep emergency access accounts with strong controls and monitor their use.
- Centralized diagnostics: Use policy to push diagnostic settings for critical services into a central Log Analytics workspace and storage account.
- Defender for Cloud: Assign plan enablement via policy and remediate non-compliant subscriptions regularly.
Handling exceptions without losing control
Real life needs exceptions. Use policy exemptions—not manual role changes—to authorize deviations. Include:
- Business justification and risk owner
- Scope limited to the minimum necessary
- Expiry date with review reminders
Monitoring, drift, and continuous improvement
- Track compliance posture with Azure Policy compliance reports at the management group scope.
- Schedule remediation tasks for DINE policies to converge legacy subscriptions.
- Log policy changes via Activity Log and route to SIEM.
- Review hierarchy and controls quarterly as your org evolves.
Common pitfalls and how to avoid them
- Deep or irregular hierarchies: Keep it simple—Platform and Landing Zones. Too many levels complicate troubleshooting and exceptions.
- Overusing Owner: Prefer least privilege and PIM. Owners everywhere = audit headaches.
- Policies that block delivery: Start in Audit mode, measure impact, then switch to Deny with clear comms and exemptions.
- Ignoring non-production: Apply guardrails in NonProd and Sandbox too, with looser SKUs and budgets but firm security controls.
- One-off manual fixes: Everything should be code-driven and repeatable. If you click it, script it.
- Relying on deprecated tooling: Favor Azure Policy + Bicep/ARM + Template Specs over legacy Blueprints approaches.
A practical rollout plan
- Inventory: List subscriptions, owners, environments, region usage, and critical controls you already have.
- Design: Agree on the hierarchy (Root > Platform > LZ-Prod/NonProd/Sandbox), naming, and tag standards.
- Bootstrap: Create management groups and move subscriptions. Assign Reader to a broad audience; lock down elevated roles with PIM.
- Baseline policies: Start with audit-only for 1–2 weeks. Review impact; then enable deny for must-have controls.
- Operations: Turn on DINE policies for diagnostics and agents. Create remediation tasks.
- Automate: Store Bicep and CLI scripts in Git; deploy via pipelines; protect with PR reviews.
- Refine: Add cost budgets, SKU limits in NonProd, and a clean exception process with expiry.
Key takeaways
- Design a clear, shallow hierarchy that mirrors governance needs.
- Push RBAC and Policy to management groups; inherit everywhere.
- Use deny-by-default for high-risk items; manage exceptions with time-bound exemptions.
- Automate everything with Bicep/CLI and Git workflows.
- Measure, remediate, and iterate—governance is continuous.
If you’d like help tailoring this approach to your environment, the CloudProinc.com.au team can guide you from assessment to automated landing zones—without slowing your builders down.
Discover more from CPI Consulting -Specialist Azure Consultancy
Subscribe to get the latest posts sent to your email.