Skip to main content

Platform Automation and IaC

The platform is a product, and a product is built from source. If the management groups, policies, and platform subscriptions only exist as a series of portal clicks, then nobody can review a change, reproduce the environment, or trust that what is documented matches what is deployed.

Purpose

This page defines the Infrastructure as Code strategy for the Azure platform: tool choice, repository structure, deployment scopes, and accelerator options.

Design Reasoning

For a new platform implementation, Microsoft’s recommended direction is to use Azure Verified Modules (AVM) together with the Azure Landing Zones IaC accelerator, in either Bicep or Terraform.

The tool choice is real but secondary:

  • Bicep is a strong choice for Azure-first teams. It integrates directly with Azure Resource Manager, supports clean scope-based deployments, and the AVM-based ALZ approach uses Azure Deployment Stacks for lifecycle management.
  • Terraform is a strong choice for organizations already standardized on Terraform, needing a broader multi-cloud workflow, or with established HCL pipelines.

The decision that actually matters is not Bicep versus Terraform. It is that the platform is deployed, reviewed, and operated as code — through pull requests and pipelines, not the portal.

New build vs. migration

For a greenfield platform, start from the ALZ accelerator with AVM. The classic Azure/ALZ-Bicep and the Terraform caf-enterprise-scale module are best reserved for existing implementations and migration scenarios, not as the starting point for a new deployment.

Architecture Decision Record

OptionRecommendationComment
BicepStrong choice for Azure-first teamsNative ARM integration and clear scope-based deployments
TerraformStrong choice for existing Terraform organizationsUse where Terraform is already the standard platform tool
Azure Verified ModulesRecommended for reusable, module-based implementationTested module patterns for Azure resources
ALZ IaC acceleratorRecommended for new platform landing zone buildsCurrent preferred accelerator direction
Legacy ALZ-Bicep / caf-enterprise-scaleUse for existing or migration scenariosNot ideal as the starting point for new deployments

Tradeoff: Adopting AVM and the accelerator means learning their conventions instead of writing everything from scratch. That up-front learning buys tested modules, a maintained upgrade path, and far less custom code to own.

A repository structure that separates platform from workload and groups code by deployment scope:

platform-foundation/
docs/
infra/
management-groups/
policies/
role-assignments/
platform/
management/
connectivity/
identity/
pipelines/
environments/
prod/
nonprod/

For Bicep, separate deployments by scope:

  • Tenant scope where required
  • Management group scope for hierarchy, policies, and RBAC
  • Subscription scope for platform subscription resources
  • Resource group scope for specific services

For Terraform, separate state by scope or platform capability to reduce blast radius and support least-privilege pipeline identities.

Give each pipeline identity (a workload identity / service principal with federated credentials) the least privilege for its scope — the policies pipeline does not need workload Contributor, and the workload pipeline does not need management-group rights.

Example Implementation

A management-group-scoped Bicep deployment:

Terminal window
az deployment mg create \
--management-group-id contoso \
--name platform-baseline \
--location switzerlandnorth \
--template-file ./infra/management-groups/main.bicep

A minimal Terraform provider baseline:

terraform {
required_version = ">= 1.6.0"
}
provider "azurerm" {
features {}
}

Validation Steps

Always preview changes before applying them. For Bicep, validate and run a what-if:

Terminal window
az deployment mg validate \
--management-group-id contoso \
--location switzerlandnorth \
--template-file ./infra/management-groups/main.bicep
az deployment mg what-if \
--management-group-id contoso \
--location switzerlandnorth \
--template-file ./infra/management-groups/main.bicep

For Terraform:

Terminal window
terraform fmt -check
terraform validate
terraform plan

The what-if / plan output is a required review gate, not a formality — it is how reviewers see the blast radius before a change reaches the tenant.

Common Mistakes

  • Using the portal accelerator as the long-term operating model instead of the IaC accelerator.
  • Making manual portal changes after IaC is introduced, which causes drift between code and reality.
  • Mixing too many scopes into one deployment, which enlarges blast radius and complicates least privilege.
  • Giving pipelines overly broad permissions.
  • Not separating platform and workload code.
  • Skipping pull requests and review gates for platform changes.

References