Azure Landing Zones
Guide to Azure Landing Zones covering CAF architecture, management groups, subscription organization, policy-driven governance, hub-spoke networking, and IaC deployment.
Prerequisites
- Understanding of Azure subscriptions and resource groups
- Familiarity with Azure Policy concepts
- Experience with Infrastructure as Code (Bicep or Terraform)
- Basic understanding of Azure networking (VNets, peering)
What Are Azure Landing Zones?
An Azure Landing Zone is a pre-configured, well-architected environment that provides the foundational infrastructure for hosting workloads in Azure. It implements security, governance, networking, and identity controls that are consistent, repeatable, and aligned with Microsoft's Cloud Adoption Framework (CAF). Think of it as the "platform" layer that your application teams deploy into, with guardrails already in place.
Without a landing zone, each team creates their own subscriptions, networking, and security controls independently. This leads to inconsistent configurations, security gaps, networking conflicts, and governance chaos. A landing zone standardizes these foundations so application teams can focus on building applications instead of reinventing infrastructure.
This guide covers the Azure Landing Zone architecture recommended by Microsoft's CAF, including management group hierarchy, subscription organization, policy-driven governance, hub-spoke networking, identity integration, and operational management. You will learn both the conceptual design and practical implementation using Bicep, Azure CLI, and Azure Policy.
Two Deployment Approaches
Microsoft offers two approaches to deploy landing zones: Azure Landing Zone Accelerator (formerly Enterprise-Scale) deploys the full architecture through the Azure Portal or Infrastructure as Code, including management groups, policies, and networking. Start Small and Expand (formerly CAF Terraform modules) lets you build incrementally. The Accelerator is recommended for new environments as it provides a complete, opinionated foundation.
Management Group Hierarchy
Management groups are the organizational structure above subscriptions in Azure. They enable you to apply Azure Policy, RBAC, and budgets at scale. The Azure Landing Zone architecture defines a specific management group hierarchy that separates platform responsibilities from workload responsibilities.
The hierarchy is designed so that policies applied at higher levels are inherited by all lower levels. A policy applied to the "Landing Zones" management group automatically applies to all subscriptions in all child management groups. This inheritance model is the key to governance at scale.
Recommended Management Group Structure
| Management Group | Purpose | Contains |
|---|---|---|
| Tenant Root Group | Top-level default group | All management groups |
| Intermediate Root | Organization top-level | Platform + Landing Zones + Decommissioned + Sandbox |
| Platform | Shared platform services | Management, Connectivity, Identity |
| Platform / Management | Monitoring and logging | Log Analytics, Automation, Microsoft Sentinel |
| Platform / Connectivity | Networking | Hub VNet, VPN/ExpressRoute, DNS, Firewall |
| Platform / Identity | Identity services | Domain controllers (if hybrid), Entra Connect |
| Landing Zones | Application workloads | Corp + Online child groups |
| Landing Zones / Corp | Internal apps (connected to corp network) | Production, Non-Production subscriptions |
| Landing Zones / Online | Internet-facing apps | Public-facing application subscriptions |
| Sandbox | Developer experimentation | Sandbox subscriptions with limited guardrails |
| Decommissioned | Retired subscriptions | Subscriptions being cleaned up |
# Create the management group hierarchy
# Start with the intermediate root group
az account management-group create \
--name "contoso" \
--display-name "Contoso"
# Platform management groups
az account management-group create \
--name "contoso-platform" \
--display-name "Platform" \
--parent "contoso"
az account management-group create \
--name "contoso-management" \
--display-name "Management" \
--parent "contoso-platform"
az account management-group create \
--name "contoso-connectivity" \
--display-name "Connectivity" \
--parent "contoso-platform"
az account management-group create \
--name "contoso-identity" \
--display-name "Identity" \
--parent "contoso-platform"
# Landing zone management groups
az account management-group create \
--name "contoso-landingzones" \
--display-name "Landing Zones" \
--parent "contoso"
az account management-group create \
--name "contoso-lz-corp" \
--display-name "Corp" \
--parent "contoso-landingzones"
az account management-group create \
--name "contoso-lz-online" \
--display-name "Online" \
--parent "contoso-landingzones"
# Sandbox and decommissioned
az account management-group create \
--name "contoso-sandbox" \
--display-name "Sandbox" \
--parent "contoso"
az account management-group create \
--name "contoso-decommissioned" \
--display-name "Decommissioned" \
--parent "contoso"
# Move a subscription to its management group
az account management-group subscription add \
--name "contoso-connectivity" \
--subscription "connectivity-sub-id"
# Verify the hierarchy
az account management-group show \
--name "contoso" \
--expand \
--recursePolicy-Driven Governance
Azure Policy is the enforcement mechanism for landing zone governance. Policies audit, deny, or automatically remediate resources that do not comply with your standards. The landing zone architecture uses policy assignments at the management group level to ensure consistent governance across all subscriptions.
Microsoft provides over 1,000 built-in policy definitions. The ALZ reference implementation assigns a curated set of these at each level of the management group hierarchy. You can customize these and add your own policies.
# Assign a policy at the management group level
# Example: Require tags on all resources
# First, get the built-in policy definition ID
POLICY_DEF=$(az policy definition list \
--query "[?displayName=='Require a tag on resources'].id" -o tsv)
# Assign it to the Landing Zones management group
az policy assignment create \
--name "require-environment-tag" \
--display-name "Require Environment tag on all resources" \
--scope "/providers/Microsoft.Management/managementGroups/contoso-landingzones" \
--policy "$POLICY_DEF" \
--params '{"tagName": {"value": "Environment"}}' \
--enforcement-mode Default
# Assign the "Allowed locations" policy to restrict deployments
az policy assignment create \
--name "allowed-regions" \
--display-name "Restrict to approved regions" \
--scope "/providers/Microsoft.Management/managementGroups/contoso-landingzones" \
--policy "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c" \
--params '{
"listOfAllowedLocations": {
"value": ["eastus", "eastus2", "westus2", "westeurope", "northeurope"]
}
}'
# Create a policy initiative (group of policies) for baseline security
az policy set-definition create \
--name "landing-zone-baseline" \
--display-name "Landing Zone Security Baseline" \
--definitions '[
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/0015ea4d-51ff-4ce3-8d8c-f3f8f0179a56",
"parameters": {}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/404c3081-a854-4457-ae30-26a93ef643f9",
"parameters": {}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e71308d3-144b-4262-b144-efdc3cc90517",
"parameters": {}
}
]' \
--management-group "contoso-landingzones"Essential Policy Assignments by Level
| Management Group | Policy | Effect |
|---|---|---|
| Intermediate Root | Allowed locations | Deny |
| Intermediate Root | Audit diagnostic settings | Audit |
| Platform | Deploy diagnostic settings to Log Analytics | DeployIfNotExists |
| Landing Zones | Require tags | Deny |
| Landing Zones | Deny public IP creation | Deny |
| Landing Zones / Corp | Deny inbound from internet | Deny |
| Sandbox | Cost budget limits | Audit |
| Decommissioned | Deny all resource creation | Deny |
Start with Audit Mode
When deploying new policies, always start with Audit effect (orenforcementMode: DoNotEnforce) before switching to Deny. This lets you assess the impact on existing resources and identify false positives before policies start blocking deployments. A Deny policy in production can break CI/CD pipelines and prevent incident response if not carefully tested.
Hub-Spoke Networking
The landing zone uses a hub-spoke network topology. The hub virtual network (VNet) in the Connectivity subscription provides shared networking services: Azure Firewall or third-party NVA for traffic inspection, VPN Gateway or ExpressRoute for on-premises connectivity, Azure Bastion for secure VM access, and DNS resolution. Spoke VNets in landing zone subscriptions peer with the hub to access these shared services.
# Create the hub VNet in the Connectivity subscription
az network vnet create \
--name "vnet-hub-eastus" \
--resource-group "rg-connectivity" \
--location "eastus" \
--address-prefixes "10.0.0.0/16" \
--subnet-name "AzureFirewallSubnet" \
--subnet-prefix "10.0.1.0/24"
# Add additional hub subnets
az network vnet subnet create \
--name "GatewaySubnet" \
--vnet-name "vnet-hub-eastus" \
--resource-group "rg-connectivity" \
--address-prefix "10.0.2.0/24"
az network vnet subnet create \
--name "AzureBastionSubnet" \
--vnet-name "vnet-hub-eastus" \
--resource-group "rg-connectivity" \
--address-prefix "10.0.3.0/24"
az network vnet subnet create \
--name "snet-dns-inbound" \
--vnet-name "vnet-hub-eastus" \
--resource-group "rg-connectivity" \
--address-prefix "10.0.4.0/24"
# Deploy Azure Firewall
az network firewall create \
--name "afw-hub-eastus" \
--resource-group "rg-connectivity" \
--location "eastus" \
--sku AZFW_VNet \
--tier Premium \
--vnet-name "vnet-hub-eastus" \
--conf-name "fw-config" \
--public-ip "pip-afw-hub-eastus"
# Get the firewall private IP for route tables
FW_PRIVATE_IP=$(az network firewall show \
--name "afw-hub-eastus" \
--resource-group "rg-connectivity" \
--query "ipConfigurations[0].privateIPAddress" -o tsv)
# Create a spoke VNet (in a landing zone subscription)
az network vnet create \
--name "vnet-app-a-eastus" \
--resource-group "rg-app-a-networking" \
--location "eastus" \
--address-prefixes "10.1.0.0/16" \
--subnet-name "snet-app" \
--subnet-prefix "10.1.1.0/24"
# Peer spoke to hub (bidirectional)
az network vnet peering create \
--name "spoke-to-hub" \
--resource-group "rg-app-a-networking" \
--vnet-name "vnet-app-a-eastus" \
--remote-vnet "/subscriptions/conn-sub-id/resourceGroups/rg-connectivity/providers/Microsoft.Network/virtualNetworks/vnet-hub-eastus" \
--allow-forwarded-traffic \
--allow-gateway-transit false \
--use-remote-gateways true
az network vnet peering create \
--name "hub-to-spoke-app-a" \
--resource-group "rg-connectivity" \
--vnet-name "vnet-hub-eastus" \
--remote-vnet "/subscriptions/app-a-sub-id/resourceGroups/rg-app-a-networking/providers/Microsoft.Network/virtualNetworks/vnet-app-a-eastus" \
--allow-forwarded-traffic \
--allow-gateway-transit true
# Route all traffic through the firewall
az network route-table create \
--name "rt-spoke-default" \
--resource-group "rg-app-a-networking" \
--location "eastus"
az network route-table route create \
--name "default-to-firewall" \
--route-table-name "rt-spoke-default" \
--resource-group "rg-app-a-networking" \
--address-prefix "0.0.0.0/0" \
--next-hop-type VirtualAppliance \
--next-hop-ip-address $FW_PRIVATE_IPAzure Virtual WAN as an Alternative
For large-scale deployments (50+ spokes, multiple regions, complex SD-WAN integration), consider Azure Virtual WAN instead of a custom hub VNet. Virtual WAN provides a Microsoft-managed hub with automated spoke connectivity, integrated firewall (Firewall Manager), built-in VPN and ExpressRoute gateways, and inter-hub routing. It simplifies operations but costs more than a custom hub for small environments.
Subscription Organization
Subscriptions are the primary unit of management, billing, and scale in Azure. The landing zone architecture recommends a subscription-per-workload model where each application or team gets its own subscription. This provides clear cost attribution, independent RBAC, and resource limit isolation.
# Create a new subscription (requires enrollment account access)
# Azure Enterprise Agreement or MCA required
az account create \
--offer-type "MS-AZR-0017P" \
--display-name "App-A-Production" \
--enrollment-account-name "enrollment-account-id" \
--owner-object-id "team-lead-object-id"
# Move subscription to the correct management group
az account management-group subscription add \
--name "contoso-lz-corp" \
--subscription "new-sub-id"
# Set up budgets for the subscription
az consumption budget create \
--budget-name "app-a-monthly" \
--amount 5000 \
--category Cost \
--time-grain Monthly \
--start-date "2026-04-01" \
--end-date "2027-04-01" \
--subscription "app-a-sub-id" \
--notifications '{
"Actual_GreaterThan_80": {
"enabled": true,
"operator": "GreaterThan",
"threshold": 80,
"contactEmails": ["team-a@contoso.com"],
"contactRoles": ["Owner"]
}
}'Identity and Access Management
Landing zone RBAC follows the principle of least privilege with clear separation between platform teams and application teams. Platform engineers manage shared infrastructure (networking, identity, management), while application teams have autonomy within their landing zone subscriptions.
# Assign platform team roles at the Platform management group
az role assignment create \
--assignee-object-id "platform-team-group-id" \
--role "Contributor" \
--scope "/providers/Microsoft.Management/managementGroups/contoso-platform"
# Assign application team roles at their subscription
az role assignment create \
--assignee-object-id "app-a-team-group-id" \
--role "Contributor" \
--scope "/subscriptions/app-a-sub-id"
# Create a custom role for network operations (read network, no changes)
az role definition create --role-definition '{
"Name": "Network Viewer",
"Description": "Can view network resources but not modify them",
"Actions": [
"Microsoft.Network/*/read",
"Microsoft.Network/networkWatchers/*/read"
],
"NotActions": [],
"AssignableScopes": [
"/providers/Microsoft.Management/managementGroups/contoso-landingzones"
]
}'
# Deny Owner role at Landing Zones (prevent teams from changing RBAC)
# Use a custom deny assignment or policy to restrict role assignmentsOperational Management
The Management subscription hosts shared operational services that provide visibility across the entire landing zone. A centralized Log Analytics workspace collects diagnostic logs and metrics from all subscriptions, Microsoft Defender for Cloud provides security posture management, and Azure Monitor delivers alerting and dashboards.
# Create the central Log Analytics workspace
az monitor log-analytics workspace create \
--workspace-name "law-central-eastus" \
--resource-group "rg-management" \
--location "eastus" \
--retention-time 90 \
--sku "PerGB2018"
# Enable Microsoft Defender for Cloud at management group level
# (Configured via Azure Policy with DeployIfNotExists)
az security auto-provisioning-setting update \
--name "default" \
--auto-provision "On"
# Enable Defender plans
az security pricing create \
--name "VirtualMachines" \
--tier "Standard"
az security pricing create \
--name "StorageAccounts" \
--tier "Standard"
az security pricing create \
--name "KeyVaults" \
--tier "Standard"
# Deploy diagnostic settings via policy (auto-configure for new resources)
# This policy deploys diagnostic settings to the central workspace
# for all supported resource types across all landing zone subscriptions
# Create an alert rule for security events
az monitor metrics alert create \
--name "high-cpu-alert" \
--resource-group "rg-management" \
--scopes "/subscriptions/app-a-sub-id" \
--condition "avg Percentage CPU > 90" \
--window-size "5m" \
--evaluation-frequency "1m" \
--action-group "/subscriptions/mgmt-sub-id/resourceGroups/rg-management/providers/Microsoft.Insights/actionGroups/platform-team"Deploying with Infrastructure as Code
The entire landing zone should be deployed through Infrastructure as Code (IaC) for reproducibility, version control, and automated testing. Microsoft provides reference implementations in Bicep, Terraform, and ARM templates.
# Deploy the ALZ Bicep reference implementation
# Clone the repository
git clone https://github.com/Azure/ALZ-Bicep.git
cd ALZ-Bicep
# Deploy management groups
az deployment tenant create \
--template-file infra-as-code/bicep/modules/managementGroups/managementGroups.bicep \
--parameters parTopLevelManagementGroupPrefix=contoso \
--parameters parTopLevelManagementGroupDisplayName="Contoso" \
--location eastus
# Deploy custom policy definitions
az deployment mg create \
--management-group-id contoso \
--template-file infra-as-code/bicep/modules/policy/definitions/customPolicyDefinitions.bicep \
--parameters parTargetManagementGroupId=contoso \
--location eastus
# Deploy policy assignments
az deployment mg create \
--management-group-id contoso-landingzones \
--template-file infra-as-code/bicep/modules/policy/assignments/policyAssignments.bicep \
--location eastus
# Deploy hub networking
az deployment sub create \
--subscription "connectivity-sub-id" \
--template-file infra-as-code/bicep/modules/hubNetworking/hubNetworking.bicep \
--parameters parHubNetworkAddressPrefix="10.0.0.0/16" \
--parameters parAzFirewallEnabled=true \
--location eastusALZ Terraform Module
If your organization uses Terraform, the officialAzure/caf-enterprise-scale Terraform module deploys the same architecture. It is a single module call that creates the management group hierarchy, deploys policies, and configures core resources. Use it as a starting point and customize the archetype definitions to match your organization's requirements.
Common Pitfalls
Skipping the platform design. Jumping straight to deploying workloads without establishing the landing zone foundation leads to technical debt that is expensive to fix later. Invest time upfront in management groups, policies, and networking.
Too many management groups. Do not create management groups for every team or application. Stick to the recommended hierarchy and use subscriptions for workload isolation. Management groups should represent governance boundaries, not organizational charts.
Ignoring DNS early. Private DNS zones, DNS forwarding, and hybrid DNS resolution are complex and hard to retrofit. Design DNS architecture as part of the initial landing zone deployment, especially if you use Private Endpoints.
Not planning IP address space. Overlapping address spaces between hub and spoke VNets, or between Azure and on-premises, cause connectivity failures. Document all IP allocations centrally and use Azure IPAM or a spreadsheet as the source of truth.
Deploying manually. Every landing zone component should be deployed through IaC with CI/CD pipelines. Manual deployments create configuration drift and make it impossible to reproduce the environment reliably.
Microsoft Entra ID (Azure AD) GuideAzure OpenAI Service GuideAWS Organizations & SCPs (for comparison)Key Takeaways
- 1Landing Zones provide a standardized, well-governed foundation for Azure workloads.
- 2Management group hierarchy separates platform services from application workloads.
- 3Azure Policy at management group level enforces governance across all subscriptions.
- 4Hub-spoke networking centralizes shared services (firewall, VPN, DNS) in a connectivity subscription.
- 5Subscription-per-workload model provides clear cost attribution and resource isolation.
- 6The entire landing zone should be deployed through Infrastructure as Code with CI/CD.
Frequently Asked Questions
Should I use Azure Landing Zone Accelerator or build custom?
How many management groups should I create?
What is the difference between hub-spoke and Virtual WAN?
How do I migrate existing subscriptions to a landing zone?
Written by CloudToolStack Team
Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.
Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.