A practical Azure landing zone for a small or mid-size company is a scaled-down version of Microsoft's reference architecture: an intermediate root management group under the tenant root, with Platform, Landing zones (Corp and Online), Sandbox and Decommissioned beneath it; one platform subscription that holds the hub network, VPN gateway and Log Analytics workspace; and separate subscriptions per workload and environment as spokes. Governance comes from a few Azure Policy assignments at management group scope, Azure RBAC granted to groups at subscription scope, a default management group for new subscriptions, and budgets on every subscription. Build it before the first workload, because moving resources between subscriptions later is harder than starting right.
Who this is for and what you will have
This guide is for IT teams of a few people who own Azure end to end, often alongside Microsoft 365, and who are about to move their first servers or applications into Azure or tidy up a subscription that grew without a plan. It follows the Cloud Adoption Framework (CAF) recommendations and uses the deviations Microsoft itself describes for smaller teams. At the end you will have:
- A management group hierarchy that matches the CAF reference and can grow into it.
- A subscription layout separating shared platform resources from workloads.
- A hub-and-spoke network with a clean IP plan, ready for site-to-site VPN.
- Baseline guardrails: allowed regions, required tags, protected hierarchy settings.
- Access granted through groups, with budgets and alerts on every subscription.
What to keep and what to simplify
The full Azure landing zone separates Security, Management, Connectivity and Identity into their own management groups and subscriptions, because large enterprises have different teams for each. If one small team manages everything, CAF's ISV landing zone guidance suggests considering a single Platform management group, and its minimal example keeps all platform components in one subscription.
| Component | Full reference | Small or mid-size company |
|---|---|---|
| Intermediate root management group | Yes | Yes |
| Platform child management groups (Security, Management, Connectivity, Identity) | Yes, one subscription each | One Platform management group, one platform subscription |
| Landing zones: Corp, Online, Local | Yes | Corp and Online; add Local only for Azure Local clusters |
| Sandbox and Decommissioned | Yes | Yes |
| Hub network | Per region, often Azure Firewall | One hub in your primary region; firewall when needed |
| Deployment | IaC accelerator | IaC accelerator or a scripted build like the one below |
What you should not simplify away: management groups themselves, separate platform and workload subscriptions, and a non-overlapping IP plan. Microsoft notes that starting with one subscription for both shared services and workloads is possible, but you will face problems later because not all resource types can be moved between subscriptions.
Prerequisites
- A Microsoft Entra tenant you'll use for Azure (normally the same tenant as Microsoft 365) and a billing agreement that can create several subscriptions.
- Access to the tenant root management group. Nobody has it by default; a Global Administrator elevates access once and then assigns roles, such as Hierarchy Settings Administrator for the hierarchy settings, to the platform admins.
- The Az PowerShell module, or Azure Cloud Shell.
- An agreed list of Azure regions, a tagging standard (for example
owner,costCenter,environment), and an IP address plan that doesn't overlap with your offices or other clouds. - Security groups in Microsoft Entra ID for platform admins and for each workload team.
Step 1: Build the management group hierarchy
Create an intermediate root under the tenant root group so you never assign anything directly at the tenant root. The first management group in a directory can take up to 15 minutes to create while the service initializes.
Connect-AzAccount
New-AzManagementGroup -GroupName 'contoso' -DisplayName 'Contoso'
$root = Get-AzManagementGroup -GroupName 'contoso'
foreach ($mg in 'contoso-platform','contoso-landingzones','contoso-sandbox','contoso-decommissioned') {
New-AzManagementGroup -GroupName $mg -DisplayName $mg -ParentId $root.Id
}
$lz = Get-AzManagementGroup -GroupName 'contoso-landingzones'
New-AzManagementGroup -GroupName 'contoso-corp' -DisplayName 'Corp' -ParentId $lz.Id
New-AzManagementGroup -GroupName 'contoso-online' -DisplayName 'Online' -ParentId $lz.IdThe resulting structure is three levels deep, inside CAF's recommendation of no more than three to four levels:
Tenant Root Group
└── Contoso (intermediate root)
├── Platform -> sub-platform-prod
├── Landing zones
│ ├── Corp -> sub-erp-prod, sub-erp-dev, sub-fileserver-prod
│ └── Online -> sub-website-prod
├── Sandbox -> developer and test subscriptions
└── Decommissioned -> subscriptions being cancelledUse the management group IDs carefully: the GroupName can't be changed later. Corp is for workloads that need private connectivity to your offices through the hub; Online is for workloads that face the internet directly or don't need a virtual network. Don't create management groups for production, test and development, or for regions; use separate subscriptions instead.
Step 2: Protect the hierarchy
Two hierarchy settings on the tenant root management group close common gaps:
- Default management group for new subscriptions. By default, new subscriptions land under the tenant root, outside your policies. CAF recommends a dedicated default, and suggests the sandbox management group as a good candidate, especially when staff have Visual Studio subscriptions.
- Require write permissions for creating new management groups. By default any user in the tenant can create management groups under the root.
In the Azure portal, open Management groups, select the root management group, select Settings, then Change default management group and turn on Permissions for creating new management groups. Azure PowerShell has no cmdlet for these settings, so Microsoft's sample calls the REST API:
$rootId = (Get-AzContext).Tenant.Id
$defaultId = 'contoso-sandbox'
$body = @{
properties = @{
defaultManagementGroup = "/providers/Microsoft.Management/managementGroups/$defaultId"
requireAuthorizationForGroupCreation = $true
}
} | ConvertTo-Json
$token = (Get-AzAccessToken).Token
$uri = "https://management.azure.com/providers/Microsoft.Management/managementGroups/$rootId/settings/default?api-version=2020-05-01"
Invoke-RestMethod -Method Put -Uri $uri -Authentication Bearer -Token $token `
-ContentType 'application/json' -Body $bodyThe root management group's ID is the same as the Microsoft Entra tenant ID. Current Az.Accounts versions return the token from Get-AzAccessToken as a SecureString, which is why the sample passes it to -Token (PowerShell 7) rather than building the header by hand.
Step 3: Create and place the subscriptions
Plan subscriptions per workload and per environment, plus one platform subscription:
| Subscription | Management group | Contents |
|---|---|---|
sub-platform-prod | Platform | Hub virtual network, VPN gateway, optional Azure Firewall and Bastion, Log Analytics workspace, private DNS zones |
sub-<workload>-prod | Corp or Online | Production spoke network and workload resources |
sub-<workload>-dev | Corp or Online | Non-production copy of the same workload |
sub-sandbox-<name> | Sandbox | Experiments; no connectivity to production |
Move existing subscriptions into place in the portal (Management groups > select the target > Add subscription) or with PowerShell:
New-AzManagementGroupSubscription -GroupId 'contoso-platform' -SubscriptionId '00000000-0000-0000-0000-000000000000'
New-AzManagementGroupSubscription -GroupId 'contoso-corp' -SubscriptionId '11111111-1111-1111-1111-111111111111'A subscription that moves inherits the access and policies of its new parent. Moving it needs write permission on the subscription, the current parent and the target parent, except when the root management group is the source or target. Changes can take up to 30 minutes to show because of Azure Resource Manager token and management group caching.
If you already have one subscription with everything in it, Microsoft's guidance for that scenario is to deploy the landing zone in parallel in the same tenant and phase workloads across, rather than restructuring the existing subscription in place. The existing subscription can be placed in the hierarchy and later moved to Decommissioned. For the migration planning side, see the enterprise Azure cloud migration playbook.
Step 4: Lay out the network
Use a hub-and-spoke topology, which CAF recommends and the landing zone reference architecture is built on. The hub lives in the platform subscription; each workload subscription gets its own spoke virtual network peered to the hub.
- IP plan first. Address spaces must not overlap across your offices and Azure. Reserve a block for Azure as a whole, then carve a hub range and one range per spoke.
- GatewaySubnet. If you'll connect offices, create a subnet named
GatewaySubnetin the hub. The hub-spoke reference architecture recommends /26 or larger. - AzureFirewallSubnet. If you deploy Azure Firewall, it needs a subnet named
AzureFirewallSubnetof at least /26. NSGs aren't supported on it. - Peering. For spokes to reach your offices through the hub gateway, set the hub side of each peering to allow gateway transit, set the spoke side to use the remote virtual network's gateway, and allow forwarded traffic on all peerings.
- One hub per region. Connect only spokes from the same region to a hub.
Azure Firewall in the hub gives you central egress control and inspection, but you can start without it and add it when the first internet-facing or regulated workload arrives. DDoS Protection is recommended for perimeter virtual networks; weigh it against the size of your public footprint. When you're ready to connect the office, follow the Azure site-to-site VPN setup guide.
Step 5: Assign baseline policies
Assign policies at management group scope so every current and future subscription inherits them, and keep assignments at the intermediate root few and broad. A sensible starting set:
| Policy (built-in display name) | Scope | Purpose |
|---|---|---|
| Allowed locations | Intermediate root | Keep resources in approved regions |
| Require a tag on resource groups | Landing zones | Every resource group carries an owner or cost center |
| Your choice of network and security policies | Corp | For example, deny public IPs on network interfaces in private workloads |
Assign Allowed locations with its listOfAllowedLocations parameter:
$scope = '/providers/Microsoft.Management/managementGroups/contoso'
$policy = Get-AzPolicyDefinition -BuiltIn | Where-Object { $_.DisplayName -eq 'Allowed locations' }
New-AzPolicyAssignment -Name 'allowed-locations' -DisplayName 'Allowed locations' `
-PolicyDefinition $policy -Scope $scope `
-PolicyParameterObject @{ listOfAllowedLocations = @('westeurope','northeurope') }
$lzScope = '/providers/Microsoft.Management/managementGroups/contoso-landingzones'
$tag = Get-AzPolicyDefinition -BuiltIn | Where-Object { $_.DisplayName -eq 'Require a tag on resource groups' }
New-AzPolicyAssignment -Name 'rg-require-costcenter' -DisplayName 'Require costCenter tag on resource groups' `
-PolicyDefinition $tag -Scope $lzScope `
-PolicyParameterObject @{ tagName = 'costCenter' }For a policy you're unsure about, add -EnforcementMode DoNotEnforce first, review compliance, then switch to enforcement. Sandbox subscriptions should get a lighter policy set than Corp and Online.
Step 6: Grant access through groups
Assign Azure roles to Microsoft Entra groups, not individual users, at the narrowest scope that works. CAF's recommendations for the hierarchy are specific:
- Don't give workload teams roles at management group scope. Assign them at the subscription or resource group they need.
- Platform admins can hold roles at management group scope for day-to-day work, but control those assignments with Privileged Identity Management so they're active only when needed.
$appTeam = (Get-AzADGroup -DisplayName 'AZ-ERP-Contributors').Id
New-AzRoleAssignment -ObjectId $appTeam -RoleDefinitionName 'Contributor' `
-Scope '/subscriptions/11111111-1111-1111-1111-111111111111'
$platform = (Get-AzADGroup -DisplayName 'AZ-Platform-Readers').Id
New-AzRoleAssignment -ObjectId $platform -RoleDefinitionName 'Reader' `
-Scope '/providers/Microsoft.Management/managementGroups/contoso'Protect the accounts that can change the hierarchy with phishing-resistant MFA and Conditional Access; the same identity controls that protect Microsoft 365 apply here.
Step 7: Budgets, logging and security baseline
- Budgets. On each subscription, open Budgets and select Add. Set a monthly amount, then add actual-cost and forecasted-cost alerts at thresholds such as 80% and 100%, with an email recipient or an action group. Budgets notify but never stop resources. Cost data typically arrives within 8 to 24 hours and budgets are evaluated every 24 hours, and a new subscription can take up to 48 hours before every Cost Management feature is available.
- Central logging. Create one Log Analytics workspace in the platform subscription and point diagnostic settings for the hub resources at it. Turn off log categories you don't use; Azure Firewall in particular can generate large volumes.
- Defender for Cloud. Review its recommendations for every subscription so security posture is visible from the first deployment.
Verification
Get-AzManagementGroup -GroupId 'contoso' -Expand -Recurseshows the full tree with each subscription under the intended parent.- In the portal, the root management group's Settings show the sandbox group as default and the permission toggle on. Create a test subscription and confirm it lands in Sandbox.
- Try to create a resource group without the required tag in a Corp subscription; the deployment is denied. Try a disallowed region; it's denied too.
Get-AzPolicyState -ManagementGroupName 'contoso' -Filter "policyAssignmentId eq '/providers/Microsoft.Management/managementGroups/contoso/providers/Microsoft.Authorization/policyAssignments/allowed-locations' and ComplianceState eq 'NonCompliant'"lists existing non-compliant resources to remediate. (-PolicyAssignmentNameonly works for assignments made at subscription or resource group scope, not management group scope.) Compliance results take some time to appear after assignment.- Each subscription shows a budget, and the hub peerings show Connected.
Troubleshooting
The default management group or permission toggle is greyed out. You're not on the root management group, or your account lacks Microsoft.Management/managementgroups/settings/write on it. Assign Hierarchy Settings Administrator at the root.
You can't move a subscription to Corp. You need write permission on the subscription, its current parent and the target. If your Owner role on the subscription is inherited from the current management group, you can only move it to a management group where you're also Owner.
A moved subscription doesn't appear under its new parent. Azure Resource Manager tokens and the management group cache last 30 minutes. Sign out and in, or wait.
A deployment fails with a policy denial. The error names the policy assignment. If the denial is legitimate, fix the template; if the workload genuinely needs an exception, use an exemption or a narrower -NotScope rather than loosening the policy for everyone.
A budget shows no data. New subscriptions can take up to 48 hours before Cost Management features work. Budget alerts are also missed if the subscriptions in a management group scope use different currencies.
Closing checklist
- Intermediate root with Platform, Landing zones (Corp, Online), Sandbox and Decommissioned.
- Default management group set to Sandbox; management group creation requires authorization.
- One platform subscription; separate subscriptions per workload and environment.
- Hub-and-spoke network with non-overlapping address space, GatewaySubnet and peering settings for gateway transit.
- Allowed locations and required tags assigned at management group scope.
- Roles assigned to groups at subscription scope; platform roles controlled through PIM.
- Budgets with actual and forecast alerts on every subscription; central Log Analytics workspace.
- A plan to move the build into the IaC accelerator when the team is ready.
References
- What is an Azure landing zone?
- Management groups design area
- ISV considerations for Azure landing zones
- Platform landing zone implementation options
- Protect your resource hierarchy
- Create a management group with Azure PowerShell
- Manage your Azure subscriptions at scale with management groups
- Hub-spoke network topology in Azure
- Transition a single subscription with no management groups to the Azure landing zone reference architecture
- Get-AzPolicyState
- Create a policy assignment using Azure PowerShell
- New-AzPolicyAssignment
- Assign Azure roles using Azure PowerShell
- Create and manage budgets