Design Your Azure Management Group and Subscription Hierarchy

Apr 2, 2026 min read

Six months after the initial landing zone deployment, a new VP reorganizes the business units. Your management group tree — which you built to mirror the org chart — is now wrong. Every policy assignment, every RBAC scope, every cost report that referenced “BU-Finance” and “BU-Operations” needs to move. You spend two sprints refactoring infrastructure that was working fine. The reorganization didn’t break the business; your hierarchy design broke your platform.

This is the Management Group trap. The hierarchy is the first thing you build in a landing zone, and it is the hardest thing to change later. Management Groups (MGs) sit above the subscription level, letting you apply Azure Policies and RBAC roles that cascade down to every child resource. A well-designed tree governs 1,000 subscriptions with the same effort as one. A poorly designed tree — typically one that mirrors a fluctuating org chart — means two sprints of refactoring every time the business restructures.

Design for Functional Longevity instead. Your hierarchy should represent the “Job to be Done” — Connectivity, Production Workloads, Sandboxes — not which department is currently paying the bill. Here’s how to deploy the standard Cloud Adoption Framework (CAF) hierarchy using Azure Verified Modules (AVM) and wire up the governance scaffold for your entire Azure estate.

1. The Enterprise-Scale Hierarchy

The standard blueprint starts with a single “Intermediate” Management Group below the Tenant Root. This group represents your organization and serves as the boundary for all shared enterprise policies.

Enterprise-Scale Management Group Hierarchy

Tena[ntIn[[RtoeP---S(orlaTtmaCIMneetodadmGdfnenbprionnaooaretgxWutmcieopettm]r]iyekMvn)Gitt]y(Contoso)[[--(LDCaCOelnoncedrloaipimnnnmugeipsZsPoianotenhse)d]]

1.1: Functional Segments

  • Platform: Home to the shared service subscriptions. No application code runs here.
  • Landing Zones: The production and non-production environments for applications.
    • Corp: Workloads requiring internal connectivity (VPN/ER).
    • Online: Public-facing workloads with direct internet ingress.
  • Sandbox: A “Free Fire” zone for experimentation. Policies here are loose (Audit-only) and resources are often auto-deleted after 30 days.
  • Decommissioned: The “Parking Lot” for subscriptions slated for deletion. A single Deny All policy here ensures no new costs are incurred while waiting for the purge cycle.

2. Policy and RBAC Cascading

The value of the hierarchy is that you define a control once and it applies everywhere below it — no per-subscription busywork.

Policy and RBAC Cascading

[--[--[[--MPRCIIR(RBMAoBHnnELElaNlAIhhSoSonAiCLeeOcOcaGc:DrrUaUkgEyiiRlReeM:PSttCCddElUssECENDaBofbTetSDAGn]rynfCedRtoGyoRnmOrmPRrIyiUilOPmPnPbhaUuTPuatPbAIoR]tvfldOlBoio]imNiArnrcicCgmn]yRIsBPAPAud(CbmO)liwinncserI)P

3. Deploying with Terraform AVM

The avm-ptn-alz module is the 2026 standard for deploying the CAF hierarchy. It replaces the legacy caf-enterprise-scale module with a flatter, more maintainable structure — fewer nested modules, fewer surprises when you run terraform plan.

# Deploy the MG Hierarchy
module "alz" {
  source  = "Azure/avm-ptn-alz/azurerm"
  version = "~> 0.10"

  management_group_name = "contoso"
  
  # Standard CAF archetypes are built-in
  enable_telemetry = false
}

# Placing a subscription into the hierarchy
resource "azurerm_management_group_subscription_association" "management" {
  management_group_id = "/providers/Microsoft.Management/managementGroups/contoso-platform-management"
  subscription_id     = "/subscriptions/00000000-0000-0000-0000-000000000000"
  depends_on          = [module.alz]
}

Pro Tip: Management Group IDs must be globally unique within your tenant and can only contain letters, digits, and hyphens. Once created, an MG ID is immutable. You can change the Display Name, but changing the ID requires deleting and recreating the group.

4. Deploying with Bicep Deployment Stacks

If your team is on Bicep, use Deployment Stacks for tenant-scoped deployments. The key benefit: when you remove an MG from your code, the stack handles cleanup in Azure automatically. Without stacks, deleted resources linger until someone notices.

targetScope = 'tenant'

module alz 'br/public:avm/ptn/lz/alz:0.1.0' = {
  name: 'alzHierarchyDeploy'
  params: {
    managementGroupName: 'contoso'
    subscriptionPlacement: {
      management: '00000000-0000-0000-0000-000000000000'
      # ... other subscriptions ...
    }
  }
}

Deploy the stack via the Azure CLI:

az stack tenant create \
  --name "alz-foundation" \
  --template-file main.bicep \
  --location eastus \
  --action-on-unmanage deleteAll

5. Subscription Placement and Moving State

Workloads evolve. A sandbox project gets promoted to production, or a Corp workload gets restructured to go public-facing. You will need to move subscriptions between groups, and that means your Terraform state needs to move too — without destroying and recreating anything.

Terraform Pattern: Use the moved block to rename resources in your state file without triggering a physical resource recreation. Skip this step and Terraform will try to delete the old association and create a new one, briefly removing the subscription from its governance scope.

moved {
  from = azurerm_management_group_subscription_association.old_path
  to   = azurerm_management_group_subscription_association.new_path
}

Key Takeaways

  1. Job-Based, Not Org-Based: Design your MGs around connectivity and security requirements, not your company’s department list.
  2. Intermediate MG is Mandatory: Always create a container below the Root to avoid locking yourself out of tenant-wide settings.
  3. Audit Before Deny: When applying cascading policies, use Audit mode first to measure impact on existing subscriptions.
  4. Immutability Matters: Choose your MG IDs carefully. You are stuck with them for the life of the landing zone.

Next Steps:

Sources