OpenTofu
OpenTofu is the open-source infrastructure-as-code engine that plans and applies the platform's modules: it reads the configuration, compares it with the recorded state and the cloud, and changes only the difference. The Azure Enterprise Baseline and the Azure Web App and Data and ETL Blueprints pin OpenTofu 1.12.6 and run it through Terragrunt.
What it does
A module is a folder of resources and variables. A provider is the plugin that talks to one API (Azure Resource Manager, Microsoft Graph, Databricks). A plan shows the changes a configuration would make; an apply makes them and records the result in state, kept in a backend with a lock so two runs cannot write at once. Version constraints pin the engine and every provider so a run tomorrow behaves like a run today.
How BuiltForProd uses it
One engine version. Every root.hcl sets terraform_version_constraint = ">= 1.12.6, < 2.0.0", the workflows install 1.12.6 through the setup-opentofu action, and the Container Apps runner image carries the same version, checksum-verified.
Providers. root.hcl generates each unit's versions.tf, the only place providers are pinned:
| Provider | Constraint | Used in |
|---|---|---|
hashicorp/azurerm | ~> 5.7 | Every infrastructure repository |
hashicorp/azuread | ~> 3.10 | Azure Enterprise Baseline: groups, Conditional Access, federated credentials |
Azure/azapi | ~> 2.13 | Azure Enterprise Baseline: settings azurerm does not cover, such as DNSSEC |
hashicorp/time | ~> 0.13 | Four Baseline modules, to wait for role assignments to propagate |
hashicorp/random | ~> 3.7 | Web App Blueprint |
hashicorp/helm | ~> 3.3 | Web App Blueprint: ArgoCD, cert-manager, External Secrets Operator |
hashicorp/kubernetes | ~> 3.2 | Web App Blueprint: namespaces, NAP objects, issuers, the ArgoCD Application |
databricks/databricks | ~> 1.135 | Data and ETL Blueprint: catalog, grants, job |
A module ships its own versions.tf only when it uses a provider alias or a provider root.hcl does not pin, and then repeats the pins exactly; scripts/check-module-versions.py enforces it in pre-commit and CI.
Modules. Every module is local to its repository, thin and limited to one resource category; none comes from a registry, and tflint is told to lint only the repository's own code.
State. The azurerm backend keeps every unit's state as one blob in the storage account stacmeeus2roottfstate, container tfstate, in acme-core-root, locked with a blob lease and keyed by the unit's path. Access is Microsoft Entra only, with the GitHub OIDC token in CI. The Baseline identity and ACME_PlatformLeads write the whole container; the web app and ETL infrastructure identities write only below apps/app-blueprint/ and apps/etl-blueprint/ through the custom role acme-tfstate-prefix-contributor with a path condition; ACME_PlatformEngineers read; the code pipelines have no state access. See Azure Storage for the account's protection.
Terms you will see
| Term | Meaning |
|---|---|
| Provider | The plugin for one API, pinned in the generated versions.tf. |
| Version constraint | The allowed engine or provider versions, such as ~> 5.7. |
| State | OpenTofu's record of what it manages, one blob per unit. |
| Blob lease lock | The backend's lock that keeps two runs from writing one state. |
| Local module | A module in the repository itself; no registry downloads. |
Where to read more
- Azure Enterprise Baseline overview, Azure Web App Blueprint overview and Azure Data and ETL Blueprint overview.
- Terragrunt for how OpenTofu is driven.
- OpenTofu and Terraform for why OpenTofu.