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
| Option | Recommendation | Comment |
|---|---|---|
| Bicep | Strong choice for Azure-first teams | Native ARM integration and clear scope-based deployments |
| Terraform | Strong choice for existing Terraform organizations | Use where Terraform is already the standard platform tool |
| Azure Verified Modules | Recommended for reusable, module-based implementation | Tested module patterns for Azure resources |
| ALZ IaC accelerator | Recommended for new platform landing zone builds | Current preferred accelerator direction |
| Legacy ALZ-Bicep / caf-enterprise-scale | Use for existing or migration scenarios | Not 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.
Recommended Configuration
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:
az deployment mg create \ --management-group-id contoso \ --name platform-baseline \ --location switzerlandnorth \ --template-file ./infra/management-groups/main.bicepA 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:
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.bicepFor Terraform:
terraform fmt -checkterraform validateterraform planThe 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.