The management group hierarchy is the first IaC file you commit to a landing zone repository. It is also the decision that is hardest to change later. Moving subscriptions between management groups after workloads are deployed triggers policy re-evaluation and potential compliance violations across every resource in those subscriptions. Get it right the first time.
The placement of a subscription in the hierarchy determines which policies it inherits, which RBAC assignments cascade down to it, and how costs roll up for reporting. A hierarchy designed without this context ends up as a flat list of subscriptions under the Tenant Root — which is structurally identical to having no hierarchy at all.
By the end of this guide, you will:
- Implement the standard CAF management group hierarchy.
- Structure subscriptions for production, non-production, and sandbox environments.
- Enforce naming conventions as IaC.
- Deploy the hierarchy using Terraform AVM and Bicep AVM.
This is Post 1 in the Azure Platform Engineering series. Every subsequent article builds on the hierarchy deployed here.
The CAF Management Group Hierarchy
The Cloud Adoption Framework (CAF) defines a standard hierarchy built around governance and connectivity requirements, not org chart reporting lines. The structure under the Tenant Root:
graph TD
Root[Tenant Root Group] --> Platform[Platform MG]
Root --> LZ[Landing Zones MG]
Root --> Sandbox[Sandbox MG]
Root --> Decomm[Decommissioned MG]
Platform --> Connectivity[Connectivity Sub]
Platform --> Identity[Identity Sub]
Platform --> Management[Management Sub]
LZ --> Corp[Corp MG - Hybrid]
LZ --> Online[Online MG - Public]
Corp --> Spoke_HR[HR Workload Sub]
Online --> Spoke_Web[Web App Sub]
style Root fill:#f96,stroke:#333,stroke-width:4px
style Platform fill:#dfd,stroke:#333
style LZ fill:#dfd,stroke:#333
style Sandbox fill:#eee,stroke:#333
style Decomm fill:#eee,stroke:#333
Notes:
- Tenant Root Group is the apex for all global policies.
- Platform MG isolates shared services from workload traffic.
- Landing Zones MG is subdivided by network requirements (Corp vs. Online).
- Policies assigned at the Landing Zones level automatically cascade to all workload subscriptions.
Platform Tier — Shared Services
The Platform management group holds three subscriptions owned by the platform team. No workloads deploy here.
- Management: Hosts centralized observability (Log Analytics).
- Connectivity: Hosts the hub networking stack (Azure Firewall, Bastion, DNS).
- Identity: Hosts hybrid identity components (AD DS) if required.
Landing Zones Tier — Workload Isolation
- Corp: Subscriptions for workloads requiring hybrid connectivity to on-premises networks.
- Online: Internet-facing workloads with no on-premises dependency.
Sandbox and Decommissioned Tiers
- Sandbox: A space for experimentation with mandatory budget alerts but fewer restrictive policies.
- Decommissioned: A parking area for subscriptions being retired. A
Denypolicy on all write operations ensures no new resources are created during the archival process.
Subscription Design and Naming
Subscriptions are the primary blast-radius boundary in Azure. One production workload per subscription is the recommended pattern for enterprises.
Naming Conventions
Management group IDs are immutable. Use descriptive IDs that reflect the tier’s purpose.
| Resource | Pattern | Example |
|---|---|---|
| Management group ID | <org>-<tier>[-<subtier>] | contoso-landingzones-corp |
| Subscription name | <org>-<workload>-<env> | contoso-payroll-prod |
Deploying the Hierarchy with Terraform AVM
The AVM pattern module Azure/avm-ptn-alz/azurerm deploys the full hierarchy. Note that modern versions require the alz provider.
providers.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
alz = {
source = "azure/alz"
version = "~> 0.20"
}
}
}
provider "alz" {
# The alz provider handles policy and management group logic
}
main.tf
module "alz" {
source = "Azure/avm-ptn-alz/azurerm"
version = "0.19.0"
management_group_name = var.org_prefix
enable_telemetry = false
}
# Subscription placement
resource "azurerm_management_group_subscription_association" "connectivity" {
management_group_id = "/providers/Microsoft.Management/managementGroups/${var.org_prefix}-platform-connectivity"
subscription_id = "/subscriptions/${var.subscription_ids.connectivity}"
depends_on = [module.alz]
}
Deploying the Hierarchy with Bicep AVM
Bicep uses Deployment Stacks for lifecycle management. Use detachAll for the initial deployment to prevent accidental deletion of existing resources.
main.bicep
targetScope = 'tenant'
module alz 'br/public:avm/ptn/alz/alz:0.1.0' = {
name: 'alzHierarchyDeploy'
params: {
managementGroupName: orgPrefix
subscriptionPlacement: {
management: subscriptionIds.management
connectivity: subscriptionIds.connectivity
}
}
}
Deployment Command
az stack tenant create \
--name "alz-hierarchy" \
--template-file main.bicep \
--parameters parameters.json \
--action-on-unmanage detachAll \
--location eastus
Best Practices
- Avoid Environment-based Management Groups: Do not create
ProductionandDevelopmentMGs. Use subscriptions for environments so that a single policy (like “Require Tags”) assigned at theCorpMG covers both. - Limit Depth: Keep the hierarchy to four or five levels maximum. Deeper structures make policy troubleshooting difficult.
- Test Policy Inheritance: Create a test resource group without required tags immediately after deployment to confirm the
Denypolicy triggers.
Troubleshooting
“Management group will be destroyed and recreated”
The name (ID) field changed. IDs are immutable. If a rename is necessary, you must manually move subscriptions and policies to a new group before deleting the old one. Use terraform state list to identify the exact resource addresses before refactoring.
“Forbidden error during deployment”
The deploying identity needs Management Group Contributor at the Tenant Root scope (/). Standing Global Administrator should be removed after the initial bootstrap in favor of PIM.
Sources
- CAF Resource Organization — Management Groups
- AVM — ALZ Terraform Module
- Azure Subscription and Management Group Service Limits
With the hierarchy deployed, move to Post 2: Hub-and-Spoke Networking. The Connectivity subscription created here becomes the home for the hub VNet and Azure Firewall.
