Microsoft publishes an enterprise-scale landing zone with dozens of management groups, policy assignments and a hub network. It is a good architecture and it is more than most organisations need on day one, which is why the common outcome is a half-deployed version that nobody understands and a subscription created by hand next to it.
Here is the subset we deploy, in the order it earns its keep.
Management groups: policy inheritance, not org chart
Management groups exist to make policy inherit. They are not a place to mirror your reporting structure. Five levels is a supported maximum and two or three is what you want.
Tenant Root
└── org
├── platform (identity, management, connectivity)
├── landing-zones
│ ├── corp (internal, no public ingress)
│ └── online (internet-facing)
├── sandbox (loose policy, hard budget cap)
└── decommissioned (deny everything, subscriptions on the way out)
The corp and online split is the one people skip and later wish they had. It lets you assign "no public IP addresses" to an entire branch instead of arguing about it per subscription.
Assign policy at management groups, never at subscriptions. A policy assigned to thirty subscriptions individually is thirty things to maintain and drift.
Subscriptions: the real boundary
A subscription is Azure's blast radius. Quotas are per subscription per region, RBAC scopes cleanly to it, and billing rolls up from it. So: one subscription per workload per environment, not per team and not per department.
The platform subscriptions are separate and small: one for identity, one for management (Log Analytics, automation), one for connectivity (the hub VNet, firewall, ExpressRoute or VPN gateways). Keeping connectivity in its own subscription means the network team owns a boundary rather than a resource group.
Resource groups sit inside subscriptions and should follow lifecycle: things that get deleted together live together. A resource group holding a production database and a developer's test VM is a resource group nobody can safely delete.
Azure Policy: the part that does the work
This is where Azure is genuinely strong, and it is the reason the landing zone holds. Start with these, assigned at org or at the landing-zones branch:
- Deny public IP addresses on network interfaces in
corp. - Deny storage accounts with public blob access, and require
minimumTlsVersion1.2 and HTTPS-only traffic. - Deny SQL and PostgreSQL servers with public network access enabled.
- DeployIfNotExists diagnostic settings to the central Log Analytics workspace for every resource type you care about. This is the single most valuable policy in the set: it means new resources are logged without anyone remembering.
- DeployIfNotExists Microsoft Defender for Cloud plans on every new subscription.
- Deny resource creation outside your permitted regions.
- Require tags for
environment,ownerandcost-centre, withModifyeffect to inherit them from the resource group where possible.
Deploy every policy in Audit mode first, look at compliance for two weeks, then flip to Deny. A Deny policy assigned without that step is an outage waiting for the next deployment.
Identity: Entra ID groups, PIM for anything privileged
RBAC assignments go to groups, never to users, and the groups come from your identity provider. Standing Owner or Contributor on a production subscription should not exist — those roles are eligible assignments in Privileged Identity Management, activated with justification and an expiry, with an alert on activation.
Managed identities for every workload, so no application ever holds a secret to authenticate to Azure. Where a secret is genuinely needed, Key Vault with RBAC (not access policies) and purge protection on.
Terraform, and the Azure-specific traps
One state per subscription per layer, in a storage account in the management subscription, with versioning and a resource lock on the container. Authenticate CI with OIDC federated credentials on a user-assigned managed identity — no client secrets.
Two provider behaviours to know: azurerm requires features {} in the provider block and its defaults are opinionated (soft delete and purge behaviour for Key Vault, in particular), and resource locks applied by policy will make your terraform destroy fail in staging in a way that is confusing the first time.
The same structural principles as the AWS landing zone apply; the mechanisms differ but the blast-radius logic does not. We deploy this at the start of most cloud engagements.
What to do this week
Open the management group blade and look at where your policies are assigned. If the answer is "at subscriptions" or "nowhere", the first move is one audit-mode assignment at the root that requires diagnostic settings. In two weeks it will have told you which resources are invisible to your logging.