A landing zone is the pre-provisioned, governed environment your workloads land in — networking, identity, policy, and management already wired up so application teams move fast without re-litigating security on every project. Microsoft’s Cloud Adoption Framework (CAF) codifies this into the Azure landing zone architecture. This guide builds one the way it’s done in regulated enterprises.
The eight design areas
Every enterprise-scale landing zone is a set of decisions across eight areas. Get these right and the rest is implementation detail:
- Azure billing & Entra tenant — enrollment, tenant topology.
- Identity & access management — Entra ID, RBAC, PIM.
- Resource organization — management groups & subscriptions.
- Network topology & connectivity — hub-spoke or Virtual WAN.
- Security — Defender for Cloud, encryption, secrets.
- Management — monitoring, backup, update management.
- Governance — Azure Policy, cost controls.
- Platform automation & DevOps — IaC, pipelines, CI/CD.
Step 1 — Management group hierarchy
Subscriptions are the unit of scale; management groups are how you apply policy and RBAC once and inherit everywhere. The CAF reference hierarchy:
Tenant Root Group
└── Contoso (top-level MG)
├── Platform
│ ├── Identity (domain controllers, Entra Connect)
│ ├── Management (Log Analytics, automation)
│ └── Connectivity (hub VNet, firewall, DNS)
├── Landing Zones
│ ├── Corp (internal, no public ingress)
│ └── Online (internet-facing)
├── Decommissioned
└── Sandbox
Create it with Bicep at the tenant scope:
targetScope = 'managementGroup'
param topLevelMgId string = 'contoso'
resource platform 'Microsoft.Management/managementGroups@2023-04-01' = {
name: 'platform'
properties: {
displayName: 'Platform'
details: { parent: { id: tenantResourceId('Microsoft.Management/managementGroups', topLevelMgId) } }
}
}
Why this matters: policies assigned at Landing Zones (e.g. “deny public IPs on NICs”, “require tags”) flow to every current and future subscription beneath it. New teams inherit guardrails on day one.
Step 2 — Subscription democratization
Hand each workload (or environment) its own subscription. Subscriptions are a scale unit and a billing/blast-radius boundary — not something to hoard. A typical split:
| Subscription | Lives under | Purpose |
|---|---|---|
sub-connectivity |
Platform/Connectivity | Hub VNet, Firewall, ExpressRoute |
sub-management |
Platform/Management | Log Analytics, Automation, backup vault |
sub-identity |
Platform/Identity | Domain controllers, Entra Connect |
sub-prod-corp |
Landing Zones/Corp | Production workloads, no public ingress |
sub-prod-online |
Landing Zones/Online | Internet-facing apps |
Step 3 — Hub-and-spoke networking
The hub holds shared network services (firewall, gateways, DNS, Bastion). Spokes hold workloads and peer to the hub. All spoke-to-spoke and spoke-to-internet traffic is forced through the firewall.
Terraform for the hub VNet + Azure Firewall:
resource "azurerm_virtual_network" "hub" {
name = "vnet-hub-eus"
resource_group_name = azurerm_resource_group.connectivity.name
location = "eastus"
address_space = ["10.10.0.0/16"]
}
resource "azurerm_subnet" "firewall" {
name = "AzureFirewallSubnet" # name is mandatory & exact
resource_group_name = azurerm_resource_group.connectivity.name
virtual_network_name = azurerm_virtual_network.hub.name
address_prefixes = ["10.10.1.0/26"]
}
resource "azurerm_firewall" "hub" {
name = "afw-hub-eus"
resource_group_name = azurerm_resource_group.connectivity.name
location = "eastus"
sku_name = "AZFW_VNet"
sku_tier = "Standard"
ip_configuration {
name = "ipc"
subnet_id = azurerm_subnet.firewall.id
public_ip_address_id = azurerm_public_ip.fw.id
}
}
Peer a spoke to the hub (both directions, with gateway transit so spokes use the hub’s VPN/ER gateway):
resource "azurerm_virtual_network_peering" "spoke_to_hub" {
name = "prod-to-hub"
resource_group_name = azurerm_resource_group.prod.name
virtual_network_name = azurerm_virtual_network.prod.name
remote_virtual_network_id = azurerm_virtual_network.hub.id
allow_forwarded_traffic = true
use_remote_gateways = true
}
Then force spoke egress through the firewall with a route table (UDR) whose default route points at the firewall’s private IP:
resource "azurerm_route" "default_to_fw" {
name = "default-via-firewall"
route_table_name = azurerm_route_table.spoke.name
resource_group_name = azurerm_resource_group.prod.name
address_prefix = "0.0.0.0/0"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = azurerm_firewall.hub.ip_configuration[0].private_ip_address
}
Step 4 — Governance with Azure Policy
Policy is how a landing zone stays compliant. Assign initiatives (policy sets) at the management-group scope. Three you almost always want:
- Deny creation of public IPs in Corp landing zones.
- Audit/Deny resources without required tags (
costCenter,owner,env). - DeployIfNotExists to auto-onboard new VMs/resources to Log Analytics & Defender.
resource denyPublicIp 'Microsoft.Authorization/policyAssignments@2024-04-01' = {
name: 'deny-public-ip'
scope: managementGroup()
properties: {
policyDefinitionId: tenantResourceId(
'Microsoft.Authorization/policyDefinitions',
'83a86a26-fd1f-447c-b59d-e51f44264114') // built-in: not allow public IP on NIC
enforcementMode: 'Default'
}
}
Step 5 — Identity & least privilege
- Use Microsoft Entra ID as the control plane; sync from on-prem AD only if you must (Entra Connect / cloud sync).
- Grant roles to groups, never individuals, and scope them at the management-group or subscription level — not per-resource.
- Turn on Privileged Identity Management (PIM) so roles like Owner and User Access Administrator are eligible, not active — engineers activate JIT with approval and MFA. (See the companion Zero Trust guide.)
Step 6 — Platform automation
Everything above is IaC. Structure it so the platform team owns the hierarchy, policy, and hub, while application teams own their spokes:
platform/ # MG hierarchy, policy, hub network, Log Analytics (platform team)
landing-zones/ # per-workload spoke modules (app teams, via PR)
modules/ # shared, versioned modules (network, monitoring)
Deploy through pipelines with a service principal (or workload identity federation) that has Owner on the relevant management group — gated behind PR review and a plan approval.
Enterprise scenario
A financial-services client deployed the CAF reference hierarchy with the standard Deny-PublicIP and Deny-PublicEndpoint initiatives at the Corp management group, then ran a wave of platform-team Bicep deployments to stand up shared services. Half the deployments failed at the DeployIfNotExists remediation step. The deny policy was blocking the very Private Endpoints that other DINE policies were trying to create for Key Vault and Storage — and DINE remediation tasks run under a managed identity that the deny effect evaluates like any other principal. The result was a deadlock: the platform couldn’t bootstrap its own private connectivity under its own guardrails.
The fix was not to weaken the deny policy but to scope it correctly. Private Endpoints are not public endpoints, so the team narrowed the deny rule with a notIn exclusion on the platform resource group and assigned the remediation identity an explicit role at the right scope:
resource remediation 'Microsoft.PolicyInsights/remediations@2021-10-01' = {
name: 'remediate-pe-storage'
scope: subscription()
properties: {
policyAssignmentId: deployPrivateEndpoint.id
resourceDiscoveryMode: 'ReEvaluateCompliance'
failureThreshold: { percentage: 10 }
}
}
The broader lesson: order policy effects deliberately. Deny evaluates before DeployIfNotExists, so a deny that’s too broad silently starves your auto-remediation. Always dry-run new initiatives in enforcementMode: 'DoNotEnforce' against the platform subscriptions first, read the compliance results, then flip enforcement on.
Landing-zone readiness checklist
Where to go next
Wire this landing zone to a CI/CD pipeline, layer Microsoft Defender for Cloud regulatory compliance dashboards on top, and adopt the Well-Architected Framework to pressure-test each workload across reliability, security, cost, operational excellence, and performance. The landing zone is the foundation — WAF keeps what lands on it healthy.