Brewing the Latest Azure Landing Zone – Automating Your Platform

Using IaC, Azure DevOps, and GitHub Actions to deploy modern Landing Zones.

Think of your Azure Landing Zone (ALZ) like a perfectly brewed flat white. You can handcraft each shot—carefully spinning up management groups, policies, and subscriptions—or you can automate the process with precision, ensuring each brew (deployment) tastes just as intended.

Automation is where mature cloud environments lift from repeatable to reliable. In this post, we’re taking that next step: automating your platform layer using Infrastructure as Code (IaC) and modern CI/CD practices with Azure DevOps and GitHub Actions.

What is Landing Zone Automation?

Landing zone automation turns the conceptual Azure architecture from the Cloud Adoption Framework (CAF) into deployable, consistent reality.

It’s about codifying the platform foundation—management groups, policies, networks, role assignments, and more—so your entire environment can be replicated, validated, and governed repeatably.

Whether you’re deploying Microsoft’s standard ALZ composition or the Sovereign Landing Zone (SLZ) for government and regulated industries, automation ensures:

  • Consistent structure across tenants and subscriptions
  • Version-controlled platform definitions
  • Governance and compliance baked into the deployment pipeline
  • Smooth integration with organisational CI/CD tooling

How It Works

At the heart of automated landing zones sits a hierarchy of IaC definitions and orchestrations:

1. Core IaC – Bicep Evolution

Bicep has now matured beyond a deployment language—it’s the clear path forward from ARM templates.
With ALZ modules defined in Bicep, engineers can:

  • Parameterise management group hierarchies
  • Configure resource consistency across environments
  • Simplify updates and version pinning through Azure Container Registry (ACR)-hosted modules

The latest ALZ accelerator leverages reusable Bicep modules organised by design area—Identity, Network, Governance, Management, and Security.

2. Deployment Orchestration

Infrastructure deployment happens through:

  • Bootstrap scripts (main.bicep orchestrators or PowerShell wrappers)
  • Azure DevOps pipelines or GitHub Actions for environment rollout
  • Separate parameter files (e.g., dev, test, prod, sovereign) for scalable multi‑environment management

3. Control Plane Integration

Each run authenticates via a managed identity or service principal with least‑privilege RBAC, deploying to the control plane (management groups, policy assignments, subscriptions).
The pipeline enforces drift detection through outputs from what-if operations before applying changes.

flowchart LR A[Git Repository] --> B[Pipeline Trigger] B --> C{Environment?} C -->|Prod| D[Deploy Bicep modules to MG hierarchy] C -->|Test| E[Deploy to sandbox subscriptions] D --> F[Policy & Blueprint Assignment] E --> F F --> G["Post-deployment validation"] G --> H["Success Notification / Azure Monitor Logs"]

Sovereign Landing Zone Automation

For regulated workloads, the Sovereign Landing Zone (SLZ) introduces additional validation layers and compliance automation.

SLZ deployments use the same core ALZ Bicep modules but add governance and security overlays specific to sovereign or isolated cloud environments.

Key components to automate include:

  • Deployment to sovereign‑specific tenants with restricted Azure AD endpoints
  • Policy and Initiative Definitions sourced from the SLZ GitHub repository
  • Compliance blueprint outputs registered as artefacts for audit traceability
  • Approval gates and manual interventions where government regulations require validation
  • Separation of parameter sets per compliance classification (e.g. Standard, Protected, Secret System)

This layered model ensures sovereign deployments remain CAF‑aligned while adhering to national compliance and residency requirements.

Real‑World Impact

Let’s take a common enterprise example:
A national healthcare organisation managing both standard and sovereign tenants across regions.

With automated landing zones, they can:

  • Provision new regulated workloads automatically through the Sovereign Landing Zone blueprint
  • Apply the same guardrails, RBAC assignments, and network controls to every deployment
  • Satisfy compliance audits via Git version history of policy assignments and configurations
  • Maintain consistent operations pipelines across both public and sovereign Azure clouds

This removes human error from foundational design and shifts governance to “code as policy”.

Implementation Examples

Deploying via the Azure Portal or CLI

For teams just beginning to automate, you can start with Azure CLI or PowerShell to deploy the ALZ accelerator:

1
2
3
4
5
az deployment sub create \
  --name platformBootstrap \
  --location australiaeast \
  --template-file main.bicep \
  --parameters @parameters.prod.json

Using Bicep – Example Snippet

Below is a simplified snippet showing an automated management group structure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
param rootMgId string = 'contoso-root'
param landingZoneParent string = 'platform'

module mgNetwork 'modules/mg.bicep' = {
  name: 'mgNetwork'
  params: {
    displayName: 'Network'
    parentId: rootMgId
  }
}

module mgSecurity 'modules/mg.bicep' = {
  name: 'mgSecurity'
  params: {
    displayName: 'Security'
    parentId: rootMgId
  }
}

output mgIds object = {
  network: mgNetwork.outputs.id
  security: mgSecurity.outputs.id
}

Supplementary Module Files

Each module encapsulates a discrete design area—aligning with the ALZ approach of modular, reusable components.

📂 modules/mg.bicep

Creates or updates a management group and outputs its ID.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
param displayName string
param parentId string

resource mg 'Microsoft.Management/managementGroups@2021-04-01' = {
  name: uniqueString(displayName)
  properties: {
    displayName: displayName
    parent: {
      id: tenantResourceId('Microsoft.Management/managementGroups', parentId)
    }
  }
}

output id string = mg.id

📂 modules/policyAssignment.bicep

Assigns a built‑in or custom policy or initiative to the management group level.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
param policyDefinitionId string
param displayName string
param scope string
param parameters object = {}

resource policyAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = {
  name: uniqueString(displayName)
  properties: {
    displayName: displayName
    policyDefinitionId: policyDefinitionId
    scope: scope
    parameters: parameters
    enforcementMode: 'Default'
  }
}

output assignmentId string = policyAssignment.id

📂 modules/roleAssignment.bicep

Grants a role definition to a service principal, group, or managed identity.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
param principalId string
param roleDefinitionId string
param scope string

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(principalId, roleDefinitionId, scope)
  properties: {
    principalId: principalId
    roleDefinitionId: roleDefinitionId
    scope: scope
  }
}

📂 modules/networking/vnet.bicep

Defines a basic virtual network with multiple subnets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
param vnetName string
param location string
param addressSpace string = '10.0.0.0/16'
param subnets array = [
  {
    name: 'default'
    prefix: '10.0.0.0/24'
  }
]

resource vnet 'Microsoft.Network/virtualNetworks@2023-04-01' = {
  name: vnetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [ addressSpace ]
    }
    subnets: [for subnet in subnets: {
      name: subnet.name
      properties: {
        addressPrefix: subnet.prefix
      }
    }]
  }
}

output vnetId string = vnet.id

Bicep File Structure Example

A typical repository layout for an automated landing zone looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/alz-platform/
├── main.bicep                     # Root orchestrator for entire platform
├── modules/                       # Reusable modules by design area
│   ├── mg.bicep                   # Management group creation
│   ├── policyAssignment.bicep     # Azure Policy or Initiative deployment
│   ├── roleAssignment.bicep       # RBAC bindings
│   ├── networking/
│   │   ├── vnet.bicep
│   │   └── firewall.bicep
│   └── identity/
│       └── managedIdentity.bicep
├── parameters/
│   ├── prod.parameters.json
│   ├── test.parameters.json
│   └── sovereign.parameters.json
└── pipelines/
    ├── azure-pipelines.yml        # Azure DevOps pipeline definition
    └── deploy-alz.yml             # GitHub Actions workflow

Each folder represents a deployable component, enabling modular rollouts and granular version control.
This layout scales naturally for multi‑environment and multi‑tenant SLZ scenarios where the sovereign parameters and policy baselines differ.

Gotchas & Edge Cases

A few real‑world kinks to watch out for:

  • Management group drift: Ensure pipelines perform what-if prior to deployments to catch unexpected manual changes.
  • Policy replication lag: Azure Policy assignments can take minutes to propagate; build delay logic or retry policies into orchestrations.
  • Sovereign Cloud API differences: Sovereign regions (e.g., Azure Government) may have limited support for preview resource providers—always validate pipeline dependencies in each environment.
  • Permissions & scope: The service connection must have Management Group Contributor and Policy Contributor roles at the root management group for full control-plane deployments.
  • Parameter reuse drift: Reusing *.parameters.json files across dissimilar tenants may cause mismatched IDs—keep parameter sets per environment.

Best Practices

  • Use Bicep modules with version pinning to avoid breaking changes from upstream ALZ module updates.
  • Treat the pipeline as the source of truth, never the portal—manual changes create drift.
  • Apply policy assignments and role definitions within IaC, not post-provisioning.
  • Structure repositories by design area or environment, depending on operating model.
  • Incorporate Azure Policy compliance scans or what-if previews into every pull request.
  • Separate parameter files for sovereign, regulatory, and standard environments.
  • Integrate approval gates in DevOps or GitHub to replicate SLZ-compliant change control.
🍺
Brewed Insight: Automation isn’t just about saving clicks; it’s about embedding your governance model into code and culture. Once your landing zone can deploy itself—public or sovereign cloud alike—your platform becomes the recipe, not the experiment.

Learn More