Cloud
Terraform vs Bicep for Enterprise Azure
Both languages are fine. The differences that will actually shape your platform are about where desired state lives, what happens when you delete a block, and how much of your estate you can describe in one place.
Every Azure platform engagement reaches this question in the first fortnight, and it is almost always asked in the least useful form: which language is better?
Both are declarative. Both are readable. Both are used at serious scale on Azure. If syntax were the deciding factor, this would be a short article and a coin toss.
The decisions that matter are architectural, and there are four of them.
1. Where the desired state lives
Terraform keeps an explicit state file. In Azure that normally means a storage account, with blob leases providing the locking. That file is an asset in its own right: it can contain secrets in plain text, it needs backup and access control, and it is a point of contention between everyone running a pipeline.
Bicep has no state file. Azure Resource Manager is the state — what exists in the subscription is the truth, and each deployment is recorded in deployment history.
This is a genuine simplification. An entire category of operational problem disappears: no stale locks, no divergence between the state file and reality, no terraform state mv surgery performed nervously on a Friday.
Terraform gets something in return, though, and it is not a small thing.
2. What happens when you delete a block
This is the difference that surprises teams in production.
Delete a resource from a Terraform configuration and run apply, and Terraform destroys it. Terraform knows what it created, because it wrote that down.
Delete a resource from a Bicep file and deploy in the default incremental mode, and nothing happens. The resource stays exactly where it is. Incremental mode adds and updates; it does not remove what you stopped describing. Complete mode does delete anything in the resource group that is absent from the template, but it is a blunt instrument and few teams run it willingly at scale.
Deployment stacks exist to close precisely this gap: a stack tracks the resources it manages, can remove them when they leave the template, and carries deny settings to block changes made outside the deployment path.
The practical consequence is worth stating plainly. Adopt Bicep without deployment stacks and your templates describe what should exist but never what should not. The estate quietly accumulates resources nobody recognises and nobody dares delete. If you choose Bicep, choose deployment stacks at the same time — retrofitting them onto an estate full of orphans is considerably less pleasant.
3. How far the language reaches
For most enterprises this is the question that settles the argument.
Bicep deploys Azure resources, and it does that very well, at every scope — tenant, management group, subscription and resource group. Support for Microsoft Graph objects such as groups and app registrations has been developing through Bicep’s extensibility work; check its current status before you plan around it.
Terraform’s reach is categorically wider. azurerm and azuread cover Azure and Entra ID, and beyond them sit providers for GitHub, Cloudflare, Datadog, Databricks, Snowflake and several hundred others. One plan can create a resource group, the Entra group that governs access to it, the GitHub repository that deploys to it and that repository’s branch protection rules.
If your platform genuinely stops at the ARM boundary, this does not matter. Very few platforms do. The moment a landing zone needs an Entra group, a DNS record at an external registrar and a repository with the right protections, a single terraform apply starts to look less like a preference and more like an architecture.
4. New Azure features on day one
Bicep is generated from the ARM API specifications, so new resource types and API versions are usable almost immediately.
Terraform’s azurerm provider is hand-written and therefore lags, sometimes by months. The escape hatch is the azapi provider, which speaks the ARM REST API directly from Terraform. Mature Terraform estates on Azure run azurerm and azapi side by side and treat the combination as normal. It is worth knowing this before concluding that Terraform cannot yet do something.
Previewing a change
terraform plan remains the benchmark others are measured against, but its accuracy depends on the state file matching reality. Where drift exists, the plan is confident and wrong.
az deployment what-if computes the difference server-side against the live resource, so it cannot be fooled by a stale state file. In exchange it is noisier: resource providers that do not return every property produce diffs that are artefacts of the API rather than real changes, and teams learn to read past them.
Neither output is a guarantee. Both are inputs to a human decision, and both are considerably better than deploying and finding out.
Refactoring
Renaming a Terraform resource without a moved block destroys and recreates it — a lesson most teams learn exactly once. The moved and import blocks have made this far more manageable than it used to be, but the hazard is intrinsic: the state file binds a name in your code to an object in the cloud.
In Bicep, the symbolic name is a local identifier. Rename it and nothing happens in Azure, because the resource name is what ARM cares about. Restructuring a Bicep codebase is meaningfully cheaper than restructuring a Terraform one.
What matters less than people think
Syntax. HCL and Bicep are both perfectly good. No cloud programme has ever failed because of a for-loop.
“Native versus third-party.” Bicep being Microsoft’s own matters in a support conversation. It does not confer a capability advantage.
Module availability. Azure Verified Modules publishes for both languages. This used to be a real Terraform advantage and is much less so now.
The one procurement question worth raising early
HashiCorp moved Terraform to the Business Source License in 2023, which prompted the OpenTofu fork. For the overwhelming majority of users the BSL changes nothing — it restricts offering competing hosted products, not internal infrastructure work. But it is a question your legal team may eventually ask, OpenTofu exists as an open-source alternative that is drop-in for most configurations, and Bicep raises no licensing question at all.
Answer it deliberately at the start, rather than in a procurement review three months into delivery.
How to actually decide
Four questions, in order. Most organisations stop at the first.
- Does your infrastructure code need to describe anything outside ARM — Entra ID objects, GitHub, an external DNS provider, SaaS platforms, another cloud? If yes, Terraform.
- Is the estate Azure-only, with a team already living in the Microsoft toolchain? Bicep, with deployment stacks from day one.
- Do you already run Terraform elsewhere, with state, pipelines and module conventions established? Then do not introduce a second language for the Azure portion. Consistency is worth more than a marginal fit.
- Is this a self-contained Azure deployment owned by a product team — an AKS cluster, one application’s resources? Bicep is lighter: no backend, no state, no lock, nothing to protect.
Running both
The common enterprise pattern is Bicep for the platform and landing zone, Terraform for application estates that reach into Entra ID, GitHub and SaaS.
It works. Price it honestly first: two module libraries, two review conventions, two sets of pipeline tasks, two skill sets to hire and retain, and a boundary question every time a new component appears. Choose it deliberately for a stated reason. Do not drift into it because two teams made different choices in the same quarter and nobody reconciled them.
What actually determines the outcome
Not the language.
The Azure estates that stay healthy are the ones where somebody decided, early and explicitly:
- who owns the module library, and how a change to it gets reviewed
- what the deployment identity is permitted to do, and what it is not
- how drift is detected, and what happens when it is found
- which scope each pipeline deploys at, and who can approve at that scope
Settle those four and either tool will serve you well. Leave them unsettled and neither will save you — which is the more useful conclusion, even if it is not the one the question was fishing for.