Skip to content

Azure landing-zone hierarchy and policy lab

Compiles a bounded Azure landing-zone Bicep example without deploying cloud resources.

Cloud Security 2 min read

Implementation: Partially tested

Implementation

main.bicep Bicep · 78 lines
labs/azure-landing-zone/main.bicep
1targetScope = 'tenant'
2 
3@description('Stable management group identifier for the organization landing-zone root.')
4param rootManagementGroupId string
5 
6@description('Display name for the organization landing-zone root.')
7param rootDisplayName string
8 
9@description('Azure region required for tenant-scope deployment metadata.')
10param deploymentLocation string = 'canadacentral'
11 
12@description('Locations audited by the sample baseline policy.')
13param allowedLocations array = [
14  'canadacentral'
15  'canadaeast'
16]
17 
18resource root 'Microsoft.Management/managementGroups@2023-04-01' = {
19  name: rootManagementGroupId
20  properties: {
21    displayName: rootDisplayName
22  }
23}
24 
25resource platform 'Microsoft.Management/managementGroups@2023-04-01' = {
26  name: '${rootManagementGroupId}-platform'
27  properties: {
28    displayName: 'Platform'
29    details: {
30      parent: {
31        id: root.id
32      }
33    }
34  }
35}
36 
37resource landingZones 'Microsoft.Management/managementGroups@2023-04-01' = {
38  name: '${rootManagementGroupId}-landing-zones'
39  properties: {
40    displayName: 'Landing zones'
41    details: {
42      parent: {
43        id: root.id
44      }
45    }
46  }
47}
48 
49resource sandbox 'Microsoft.Management/managementGroups@2023-04-01' = {
50  name: '${rootManagementGroupId}-sandbox'
51  properties: {
52    displayName: 'Sandbox'
53    details: {
54      parent: {
55        id: root.id
56      }
57    }
58  }
59}
60 
61module baseline 'modules/policy-baseline.bicep' = {
62  name: 'landing-zone-audit-baseline'
63  scope: managementGroup(rootManagementGroupId)
64  params: {
65    allowedLocations: allowedLocations
66    assignmentLocation: deploymentLocation
67  }
68  dependsOn: [
69    root
70  ]
71}
72 
73output managementGroupIds object = {
74  root: root.id
75  platform: platform.id
76  landingZones: landingZones.id
77  sandbox: sandbox.id
78}

Run it

  • npm run verify:bicep

This lab compiles a small tenant-scope Bicep design for a management-group hierarchy and an audit-first custom policy assignment. It demonstrates deployment shape, federated CI identity, and a reviewable what-if boundary; it does not deploy during repository validation.

Architecture decision

The sample separates platform, landing-zone, and sandbox subscriptions beneath an organization root. The policy begins with audit, because changing a broad management-group policy to deny before impact analysis can interrupt workloads. Production design still needs connectivity, identity, security, decommissioned, and workload-archetype management groups based on the organization's operating model.

Prerequisites and compile

  • Azure CLI 2.83.0 or later with Bicep support.
  • No Azure login is needed to compile.
az bicep build --file labs/azure-landing-zone/main.bicep

Expected result: Bicep returns a compiled ARM template without diagnostics. The repository code validator performs this compilation when Azure CLI is available and reports an explicit limitation otherwise.

What-if, not deployment

After replacing every angle-bracket placeholder, an authorized reviewer may inspect:

az login --tenant <tenant-id> --allow-no-subscriptions
az deployment tenant what-if
 --name landing-zone-review
 --location canadacentral
 --template-file labs/azure-landing-zone/main.bicep
 --parameters '@labs/azure-landing-zone/parameters.example.json'

The included workflow uses GitHub OIDC and a protected environment. Configure the federated credential with an exact repository/environment subject, constrain the service principal to the required tenant/management-group operations, and require a human review of the what-if artifact. Do not add a client secret.

Deployment and rollback considerations

Management-group and policy changes have a wide blast radius. Roll out new policy in audit mode, inventory noncompliance, assign named exemption owners with expiry, pilot a narrow management group, then move to deny only with tested break-glass and rollback procedures. Removing a policy assignment does not undo resource mutations performed by deployIfNotExists or modify; this sample deliberately uses audit.

Negative cases to review

  • placeholder or duplicate management-group IDs;
  • an OIDC subject broader than the protected environment;
  • policy enforcement before exemptions and regional dependencies are inventoried;
  • a hierarchy move that changes inherited role and policy scope;
  • subscription vending without ownership, budget, network, logging, and lifecycle metadata;
  • a tenant-level deployment identity with permanent credentials or excessive roles.

Cleanup and limitations

Compilation creates main.json; remove that generated file after inspection. This lab does not execute what-if in CI because it has no authorized tenant, and it does not deploy, move subscriptions, create role assignments, configure networking, or validate tenant-specific Azure Policy aliases. What-if itself is predictive output, not proof of runtime correctness.

References