Design Your Azure Management Group and Subscription Hierarchy

min read

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 Deny policy 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.

ResourcePatternExample
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 Production and Development MGs. Use subscriptions for environments so that a single policy (like “Require Tags”) assigned at the Corp MG 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 Deny policy 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

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.