In a nutshell
Picture a property developer building a planned neighbourhood. Before a single house goes up, they pour the foundations, run the water and power, lay the roads, install the street lighting, and agree the building codes. Only then do individual builders arrive and put houses on their plots — fast, because the hard shared work is already done and nobody has to re-negotiate where the sewage pipe goes.
An Azure landing zone is exactly that, for cloud. Before any application team deploys a workload, the platform team lays the shared foundations — management groups (how the estate is zoned), Azure Policy (the building codes), networking (the roads and utilities), and identity (who is allowed onto which plot). Application teams then “land” their workloads in a pre-governed, pre-connected environment and move fast without re-litigating security, networking, or compliance on every project. Microsoft’s Cloud Adoption Framework (CAF) is the playbook for the whole neighbourhood; the Azure landing zone is the reference blueprint you build from it.
The pay-off is inheritance. Set a rule once at the top — “no public IP addresses in the internal zone”, “every resource must carry a cost-centre tag” — and every current and future subscription beneath it obeys automatically. That is the difference between governance you enforce and governance you merely hope for.
Level: Advanced · Time: ~23 min
Prerequisites — you’ll get the most from this if you already understand Azure subscriptions and resource groups, virtual networks and subnets, and Azure RBAC role assignments. If VNet peering or NSGs are still new, read the virtual network basics lesson first.
After this lesson you will be able to:
- Explain the eight CAF design areas and why a landing zone exists at all.
- Design a management-group hierarchy and reason about policy inheritance and blast radius.
- Stand up hub-and-spoke (or Virtual WAN) networking with forced tunnelling through a firewall.
- Enforce guardrails with Azure Policy —
Deny,Audit, andDeployIfNotExists— and know why a carelessDenycan deadlock your own platform. - Choose between hand-rolled IaC, the
caf-enterprise-scalemodule, and the newer ALZ accelerator + Azure Verified Modules. - Automate new-subscription creation with subscription vending.
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.
Think of the eight areas as three layers stacked on each other. The root (billing/tenant, identity) is what everything else inherits — get the tenant and identity model wrong and every layer above inherits the mistake. The structure (resource organization, network, security) is what most of this lesson builds. The operating model (management, governance, platform automation) is what keeps the whole thing healthy on day 2 and beyond.
| Design area | The decision you’re really making | Implemented with |
|---|---|---|
| Billing & tenant | One tenant or many? EA/MCA enrollment shape | Entra tenant, billing account roles |
| Identity & access | Cloud-only, hybrid, or synced? Who is privileged? | Entra ID, RBAC, PIM, Conditional Access |
| Resource organization | How do policy and RBAC inherit? | Management groups, subscriptions |
| Network topology | Regional hub-spoke or global Virtual WAN? | VNets, firewall, gateways, DNS |
| Security | How do you detect and prevent? | Defender for Cloud, encryption, Key Vault |
| Management | How do you monitor, back up, patch? | Log Analytics, Azure Monitor, Backup |
| Governance | How do you stay compliant? | Azure Policy, budgets, tags |
| Platform automation | How is all of the above delivered? | IaC, pipelines, workload identity |
A subtlety beginners miss: these areas are not sequential steps you finish and forget. They are continuous concerns. You revisit network topology when a new region opens, identity when an acquisition arrives, and governance every time a new compliance regime lands. A landing zone is a living platform, not a one-off project.
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.
A few rules that save real pain:
- Depth is capped at six levels of management groups below the Tenant Root Group. The hierarchy above uses four — leave headroom; a tree that mirrors your org chart tends to sprawl past the limit and is painful to refactor later.
- Never place workloads in the Tenant Root Group, and assign policy there only for the truly universal rules — everything inherits it, including the platform subscriptions you may later need to exempt.
- Group by policy needs, not by team. Corp vs Online is a governance boundary (public ingress allowed or not) — exactly what a management group is for. Team ownership belongs on subscriptions and RBAC, not on the MG shape.
The same hierarchy in the Azure CLI, one node at a time:
# Create the top-level MG under the Tenant Root Group, then children
az account management-group create --name contoso --display-name "Contoso"
az account management-group create --name platform --display-name "Platform" --parent contoso
az account management-group create --name landingzones --display-name "Landing Zones" --parent contoso
Moving a subscription into a management group re-parents its inheritance instantly — it picks up every policy and role assignment on the new branch:
az account management-group subscription add --name landingzones --subscription <SUBSCRIPTION_ID>
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 |
Why a subscription and not just another resource group? A subscription is Azure’s real unit of scale, isolation, and blast radius:
- Quotas and limits are largely per-subscription (regional vCPU quotas, public IPs, role-assignment counts). A noisy workload can exhaust a shared subscription’s quota and starve its neighbours; separate subscriptions keep those ceilings independent.
- Blast radius — a leaked credential, a runaway script, or a mistaken
Contributorgrant is contained to one subscription. Resource groups do not give you that: RBAC and policy can scope to an RG, but the billing, quota, and identity blast-radius boundary is the subscription. - Billing clarity — one subscription per workload/environment makes cost showback trivial: the invoice is the cost breakdown.
The trade-off is sprawl — dozens or hundreds of subscriptions is normal at enterprise scale, which is exactly why you never hand-create them. You vend them from a template (see Going deeper). A common convention is one subscription per workload × environment — sub-payments-prod, sub-payments-nonprod — each landing in the management group whose guardrails it needs.
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
}
The single most important networking fact for a landing zone: VNet peering is not transitive. If spoke A peers with the hub and spoke B peers with the hub, spoke A still cannot reach spoke B on its own. That is a feature — it forces east-west traffic through the hub firewall where you can inspect and log it. To actually allow (and inspect) spoke-to-spoke traffic you point both spokes’ 0.0.0.0/0 route at the firewall via UDR, and the firewall makes the allow/deny decision.
The peering above is only the spoke side. For spokes to use the hub’s VPN/ExpressRoute gateway, the hub side must offer gateway transit:
resource "azurerm_virtual_network_peering" "hub_to_spoke" {
name = "hub-to-prod"
resource_group_name = azurerm_resource_group.connectivity.name
virtual_network_name = azurerm_virtual_network.hub.name
remote_virtual_network_id = azurerm_virtual_network.prod.id
allow_forwarded_traffic = true
allow_gateway_transit = true # hub offers its gateway…
}
# …and the spoke sets use_remote_gateways = true (shown above) to consume it.
Peering must be created on both sides — it is never implied. And keep the layers straight: NSGs are stateful, per-subnet/per-NIC allow-deny lists (cheap, distributed), while Azure Firewall is the centralised, FQDN-aware, logged choke point. You use both — NSGs for micro-segmentation inside a spoke, the firewall for everything crossing the hub.
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'
}
}
Policy has more effects than Deny, and a landing zone uses most of them:
| Effect | What it does | Typical landing-zone use |
|---|---|---|
Deny |
Blocks the create/update request outright | No public IP on NICs in Corp |
Audit |
Allows it, flags non-compliance | Report resources missing tags |
Append / Modify |
Adds or changes properties on write | Force a tag value, add TLS settings |
DeployIfNotExists (DINE) |
Deploys a remediation resource after the fact | Onboard new VMs to Log Analytics / Defender |
AuditIfNotExists |
Flags when a related resource is missing | VM without a backup |
Disabled |
Turns the policy off for staged testing | Phased rollout |
Two operational must-knows. First, DeployIfNotExists and Modify run under a managed identity that Azure creates for the assignment — and that identity needs an explicit role at the remediation scope, or the remediation silently fails with an authorization error. Second, always assign a new initiative with enforcementMode: 'DoNotEnforce' (compliance-only) first, read the results, and only then flip enforcement on. When one resource legitimately must break a rule, use a policy exemption scoped to that resource or resource group rather than weakening the policy for everyone.
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.)
Three additions separate a real landing zone from a demo:
- Break-glass accounts. Keep two cloud-only emergency-access accounts excluded from Conditional Access and PIM, with long random passwords in a sealed vault. The day your identity provider or MFA service has an outage, these are how you still get Owner on the tenant. Monitor them — any sign-in should page someone.
- Conditional Access is the other half of RBAC. RBAC decides what a principal may do; Conditional Access decides under what conditions (device compliance, location, MFA, sign-in risk). A landing zone pairs least-privilege roles with Conditional Access that demands MFA and a compliant device for anything privileged.
- Workloads use managed identities, not secrets. Applications and pipelines authenticate with managed identities or workload identity federation, so there is no client secret to leak or rotate. Grant those identities roles the same way — to a group, at the narrowest scope that works.
Scope discipline is the whole game: a role assignment at a management group is inherited by every subscription beneath it. Owner at the top-level MG is Owner on the entire estate. Grant the narrowest role at the narrowest scope that still gets the job done.
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.
The split above is also a state and blast-radius boundary. Give the platform stack its own remote state (its own storage account + container) and the application spokes theirs, so an app team’s terraform apply can never touch — or corrupt the state of — the hub, the policies, or the MG hierarchy. Application changes arrive by pull request against their own spoke module; the platform team reviews and merges.
A representative deployment pipeline gates every change behind a plan and a manual approval:
# azure-pipelines.yml — platform landing-zone deployment (representative)
trigger:
branches:
include: [ main ]
stages:
- stage: validate
jobs:
- job: plan
steps:
- script: terraform plan -out=tfplan
displayName: Terraform plan
- stage: apply
dependsOn: validate
condition: succeeded()
jobs:
- deployment: apply
environment: platform-prod # protected: requires manual approval
strategy:
runOnce:
deploy:
steps:
- script: terraform apply tfplan
displayName: Terraform apply
The environment: platform-prod is the control point — Azure DevOps environments (and GitHub environments) let you attach a required-reviewer check, so no landing-zone change reaches production without a human approving the exact plan.
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.
Going deeper
Platform vs application landing zones
The phrase “landing zone” gets used for two different things, and mixing them up causes real confusion. Platform landing zones are the shared-service subscriptions the platform team owns — connectivity (the hub), management (Log Analytics, automation), and identity. Application landing zones are the subscriptions handed to workload teams to land in — a spoke, pre-wired to the hub and pre-governed by inherited policy. The platform landing zone is built once, carefully. Application landing zones are mass-produced. Everything under “subscription vending” below is about mass-producing application landing zones safely.
Hub-spoke vs Virtual WAN
Classic hub-and-spoke means you build and own the hub VNet, the firewall, the gateways, and every peering — maximum control, maximum operational surface. Azure Virtual WAN (vWAN) is the managed alternative: Microsoft runs the hub as a service, you attach spokes and branches, and it handles transit routing — including spoke-to-spoke, which classic peering cannot do without your UDRs.
| Hub-spoke (self-managed) | Virtual WAN | |
|---|---|---|
| Hub ownership | You build and operate it | Managed by Azure |
| Transit routing | Manual (UDRs, NVAs) | Built in |
| Global reach | You interconnect regions yourself | Native any-to-any across regions |
| Best when | One or a few regions, deep control needed | Many regions/branches, SD-WAN, scale |
Rule of thumb: a handful of regions and a strong network team → hub-spoke gives you control. Many regions, lots of on-prem branches, or a global mesh → vWAN removes a mountain of routing toil. The network landing zone lesson works this decision in depth.
The ALZ accelerator, caf-enterprise-scale, and Azure Verified Modules
You have three honest options for building all of this as code, in rising order of “batteries included”:
- Hand-rolled IaC (what the snippets in this lesson show). Total control, total responsibility. Great for learning and for genuinely bespoke estates; a lot to maintain.
- The
Azure/caf-enterprise-scale/azurermmodule (a.k.a. Enterprise-Scale). One large, opinionated Terraform module that deploys the whole CAF reference — MG hierarchy, a big library of built-in policy assignments, and the platform resources — driven by configuration. Fast and battle-tested, but you inherit its opinions. - The ALZ accelerator + Azure Verified Modules (AVM). The current Microsoft direction: Azure Verified Modules are the official, supported, single-source-of-truth building blocks (resource modules and higher-level pattern modules), and the ALZ accelerator is a bootstrapping tool that scaffolds a starter repository, pipelines, and the ALZ pattern modules for you. Both Bicep and Terraform flavours exist. New builds should start here.
Whichever accelerator you pick, treat its default policy set as a proposal: assign it in DoNotEnforce, review what each initiative would deny or deploy, and enforce in waves. The Azure Verified Modules platform lesson goes deep on composing AVM modules.
Subscription vending
Subscription vending is the automated production line for application landing zones: a workload team files a request (name, environment, cost centre, network size); a pipeline creates the subscription, moves it under the correct management group, wires its spoke VNet and hub peering, applies budgets and RBAC, and hands back a ready-to-use subscription — in minutes, with zero click-ops. The Azure/lz-vending/azurerm module is purpose-built for it:
module "landing_zone" {
source = "Azure/lz-vending/azurerm"
version = "~> 5.0"
location = "eastus"
# Create and place the subscription
subscription_alias_enabled = true
subscription_display_name = "sub-payments-prod"
subscription_billing_scope = var.billing_scope # placeholder — your EA/MCA scope
subscription_workload = "Production"
subscription_management_group_association_enabled = true
subscription_management_group_id = "landingzones"
# Vend and peer the spoke network
virtual_network_enabled = true
virtual_networks = {
spoke = {
address_space = ["10.20.0.0/24"]
hub_peering_enabled = true
hub_network_resource_id = var.hub_vnet_id # placeholder
}
}
}
The equivalent first step in the CLI is az account alias create, which is what actually mints a subscription under a billing scope:
az account alias create \
--name sub-payments-prod \
--billing-scope "<BILLING_SCOPE_ID>" \
--display-name "sub-payments-prod" \
--workload Production
SRE guardrails: drift, blast radius, and day 2
A landing zone is not “done” at deployment — it is operated. Three concerns dominate day 2:
- Drift. Someone clicks something in the portal and reality diverges from code. Two defences work together: preventive guardrails (
Deny/Modifypolicies stop the change happening) and detective ones (a scheduledterraform planor a deployment-stack what-if that alerts on divergence). Preventive beats detective — aDenypolicy is a guardrail a portal click cannot cross. - Blast radius. Design so a mistake is contained: policy and RBAC scoped narrowly, per-subscription isolation, separate state files, and no standing
Owner(PIM makes it eligible-not-active). Ask of every change, “if this is wrong, how far does the damage reach?” - The reconcile loop. Because everything is code applied through pipelines, recovery is re-apply, not hand-repair. That is the SRE payoff of a landing zone: desired state is written down and continuously reconciled, so humans manage the policy, not the individual resources.
Common beginner mistakes
- “A landing zone is just the network.” The network is one of eight design areas. A landing zone is the whole governed foundation — management groups, policy, and identity as much as VNets. Build only the network and you’ve built a hub, not a landing zone.
- Separating teams with resource groups instead of subscriptions. Resource groups share the subscription’s quotas, billing, and blast radius. Reach for a subscription per workload/environment; use resource groups for lifecycle grouping inside a subscription.
- Assigning policy at the subscription instead of the management group. Assign at the MG and every current and future subscription inherits it automatically. Assign per-subscription and you’ll forget the next one — governance you have to remember is governance that fails.
- Assuming VNet peering is transitive. Spoke A peered to the hub and spoke B peered to the hub does not let A reach B. You route through the hub firewall with UDRs (or use Virtual WAN). This trips up almost everyone once.
- Turning on a broad
Denyin production first. An over-scopedDenycan block your ownDeployIfNotExistsremediation (the deadlock in the scenario above) or lock out a legitimate workload. Always assign new policy inDoNotEnforce, read compliance, then enforce. - Giving the deployment pipeline
Ownerat the Tenant Root Group. That isOwnerover the entire estate in one credential. Scope the pipeline’s identity to the management group it manages, prefer workload identity federation over a stored secret, and gate applies behind PR review. - Treating the landing zone as a project that “finishes.” It is a platform you operate. Budget for day 2: drift detection, policy tuning, new-region expansion, and subscription vending as demand grows.
Practice challenges
Work these top to bottom — they escalate from a single command to diagnosing a real policy deadlock. Try each before opening the solution.
- (Beginner) Create a child management group. Add a
Sandboxmanagement group directly under the top-levelcontosoMG using the Azure CLI.
<details> <summary>Solution</summary>
az account management-group create --name sandbox --display-name "Sandbox" --parent contoso
Why: --parent sets inheritance — sandbox now inherits every policy and role assignment on contoso.
</details>
- (Beginner) Require an
ownertag on new resources. Assign the built-in Require a tag on resources policy at thelandingzonesMG so resources lacking the tag are refused.
<details> <summary>Solution</summary>
az policy assignment create \
--name require-owner-tag \
--scope "/providers/Microsoft.Management/managementGroups/landingzones" \
--policy "871b6d14-10aa-478d-b590-94f262ecfa99" \
--params '{ "tagName": { "value": "owner" } }'
Why: the built-in Require a tag on resources definition has a Deny effect; scoping it at the MG makes every subscription below inherit the requirement.
</details>
- (Intermediate) Complete the peering both ways with gateway transit. Given the spoke-to-hub peering in the lesson, write the hub-to-spoke side so spokes can use the hub’s gateway.
<details> <summary>Solution</summary>
resource "azurerm_virtual_network_peering" "hub_to_spoke" {
name = "hub-to-prod"
resource_group_name = azurerm_resource_group.connectivity.name
virtual_network_name = azurerm_virtual_network.hub.name
remote_virtual_network_id = azurerm_virtual_network.prod.id
allow_forwarded_traffic = true
allow_gateway_transit = true
}
Why: peering is not implied on the far side — the hub must set allow_gateway_transit = true to match the spoke’s use_remote_gateways = true.
</details>
- (Intermediate) Roll out a new deny initiative safely. You want to add “deny public IP on NICs” to the
CorpMG without risking an outage. What is the safe sequence?
<details> <summary>Solution</summary>
Assign it in compliance-only mode, inspect, then enforce:
az policy assignment create --name deny-public-ip --enforcement-mode DoNotEnforce \
--scope "/providers/Microsoft.Management/managementGroups/corp" \
--policy "83a86a26-fd1f-447c-b59d-e51f44264114"
# review results, then update the assignment with --enforcement-mode Default
az policy state summarize --management-group corp
Why: DoNotEnforce reports what would be denied without blocking anything, so you catch legitimate breakers before they become an outage.
</details>
- (Advanced) Vend an application landing zone. Sketch the module call that creates
sub-web-prod, places it underlandingzones, and peers a10.30.0.0/24spoke to the hub.
<details> <summary>Solution</summary>
module "web_prod" {
source = "Azure/lz-vending/azurerm"
version = "~> 5.0"
location = "eastus"
subscription_alias_enabled = true
subscription_display_name = "sub-web-prod"
subscription_billing_scope = var.billing_scope
subscription_workload = "Production"
subscription_management_group_association_enabled = true
subscription_management_group_id = "landingzones"
virtual_network_enabled = true
virtual_networks = {
spoke = {
address_space = ["10.30.0.0/24"]
hub_peering_enabled = true
hub_network_resource_id = var.hub_vnet_id
}
}
}
Why: vending does creation + placement + networking in one templated, reviewable unit — no click-ops, and the new subscription inherits landingzones guardrails immediately.
</details>
- (Advanced) Break the DINE-vs-Deny deadlock. A
Deny-PublicEndpointpolicy atCorpis blocking the Private Endpoints that aDeployIfNotExistspolicy is trying to create for Key Vault. Without weakening the deny for everyone, how do you unblock the platform?
<details> <summary>Solution</summary>
Create a policy exemption for the platform resource group (or narrow the deny with a notIn on that scope), then re-run remediation:
az policy exemption create \
--name exempt-platform-pe \
--policy-assignment "<DENY_ASSIGNMENT_ID>" \
--exemption-category Waiver \
--scope "/subscriptions/<SUB_ID>/resourceGroups/rg-platform-connectivity"
az policy remediation create --name remediate-pe --policy-assignment "<DINE_ASSIGNMENT_ID>"
Why: the fix is scoping, not weakening — a targeted exemption lets the platform bootstrap its own private connectivity while the deny still protects every workload subscription. </details>
Landing-zone readiness checklist
Glossary
- Landing zone — a pre-provisioned, governed Azure environment (identity, network, policy, management) that workloads “land” in, so teams build fast on shared, compliant foundations.
- Cloud Adoption Framework (CAF) — Microsoft’s end-to-end guidance for adopting Azure; the Azure landing zone is its reference architecture for the ready/govern phases.
- Management group (MG) — a container above subscriptions for applying Azure Policy and RBAC once and inheriting downward. Up to six levels deep below the Tenant Root Group.
- Subscription — Azure’s unit of scale, billing, quota, and blast radius. Democratization means one per workload/environment rather than a few shared giants.
- Platform landing zone — the shared-service subscriptions the platform team owns (connectivity, management, identity).
- Application landing zone — a subscription/spoke handed to a workload team, pre-wired and pre-governed; mass-produced by vending.
- Hub-and-spoke — a topology where a central hub VNet holds shared services (firewall, gateways, DNS) and workload spokes peer to it.
- Virtual WAN (vWAN) — a managed hub-as-a-service alternative to self-built hub-spoke, with built-in any-to-any transit routing across regions.
- Azure Policy — the engine that audits or enforces rules on resources. Key effects:
Deny,Audit,Append/Modify,DeployIfNotExists,AuditIfNotExists. - Initiative (policy set) — a bundle of policy definitions assigned and reported as one unit.
- DeployIfNotExists (DINE) — a policy effect that deploys a remediation resource when something is missing; runs under a managed identity that needs its own role assignment.
- Policy exemption — a scoped, time-boxable waiver that excludes a specific resource or scope from a policy without weakening it globally.
- enforcementMode /
DoNotEnforce— assign a policy in compliance-only mode to see what would happen before it actually blocks anything. - UDR (User-Defined Route) — a custom route (often
0.0.0.0/0→ firewall) that overrides Azure’s default routing to force traffic through an appliance. - Gateway transit — a peering option letting spokes use the hub’s VPN/ExpressRoute gateway (
allow_gateway_transiton the hub,use_remote_gatewayson the spoke). - PIM (Privileged Identity Management) — makes privileged roles eligible rather than always-active; engineers activate just-in-time with approval and MFA.
- Conditional Access — Entra policies that gate sign-in on conditions (MFA, device compliance, location, risk), complementing RBAC.
- Break-glass account — an emergency cloud-only admin account excluded from Conditional Access/PIM, used only when normal access paths fail.
- Subscription vending — automated production of application landing zones: create subscription → place under MG → wire network → apply budgets/RBAC, all as code.
- Azure Verified Modules (AVM) — Microsoft’s official, supported library of IaC building blocks (Bicep and Terraform): resource modules and higher-level pattern modules.
- ALZ accelerator — a bootstrapping tool that scaffolds a starter repo, pipelines, and ALZ pattern modules for a new landing zone.
- Blast radius — how far the damage from a mistake or compromise can spread; minimized by narrow scopes, subscription isolation, and no standing privilege.
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.