AzureFixes Logo
AZUREFIXES
DEBUG FASTER. DEPLOY SMARTER.
Terraform vs Bicep: Choosing IaC for Azure in 2026
Published on
9 min read

Terraform vs Bicep: Choosing IaC for Azure in 2026

Both tools work. Both are production-ready. Both are used by serious Azure shops in 2026. The question isn't which one is better — it's which one is better for your specific situation. Here's a structured way to think through that.


Terraform vs Bicep at-a-glance comparison: cloud scope, state management, Azure feature lag, language, tooling, testing, and best-for summary

What Each Tool Actually Is

Terraform is a declarative IaC tool from HashiCorp (now also available as the OSS fork OpenTofu since the BSL license change). You write HCL, run terraform plan to preview changes, and terraform apply to execute them. Terraform tracks what it deployed in a state file — by default a local terraform.tfstate, but in any serious deployment you push it to an Azure Blob Storage backend.

The provider model is Terraform's superpower: a single workflow covers Azure, AWS, GCP, Kubernetes, GitHub, Datadog, and 800+ others. Your team learns one mental model and one CI/CD pattern.

Bicep is a domain-specific language from Microsoft that compiles to ARM JSON. You write readable .bicep files, run az bicep build (or let the CLI do it automatically), and deploy with az deployment group create. ARM itself tracks what exists; Bicep is stateless from your perspective.

Because Bicep sits directly on top of ARM, it supports every Azure API version the day Microsoft publishes it — including private previews. Terraform's azurerm provider lags by weeks or months while maintainers code, test, and release support for new features.


The Four Dimensions That Actually Matter

1. Cloud Scope

If you touch anything outside Azure — AWS for a CDN edge, GCP for BigQuery, Cloudflare for DNS — Terraform wins decisively. One language, one pipeline, one state.

If you're fully Azure-committed and have no realistic plans to expand outside, cloud scope is a wash. Both tools can manage your entire estate.

2. State Management

Terraform requires you to manage state. That means:

  • Provisioning a storage account before you can bootstrap Terraform itself
  • Protecting the state file (it contains secrets in plaintext until Terraform 1.10+ encrypts them)
  • Handling state lock failures when CI jobs fail mid-apply
  • Running terraform state mv when you refactor modules

Bicep has no state file. ARM knows what's deployed. You run az deployment what-if to preview, and ARM handles idempotency. This is a significant operational simplicity win for smaller teams.

The catch: ARM's complete mode deletes resources not in your template. Incremental mode (the default) is additive — it won't remove resources you orphan. This means Bicep can accumulate drift over time without explicit cleanup.

3. Azure Feature Lag

This is Bicep's biggest concrete advantage. When Microsoft announces a new capability — say, a new App Service environment tier, a new Cosmos DB feature, or a new SKU — Bicep supports it immediately because it maps directly to an ARM API version. You set apiVersion: '2025-04-01' and you're using the new API today.

With Terraform, you wait for the azurerm maintainers (or OpenTofu community) to ship a provider update. For stable GA features this is typically a few weeks. For preview features it can be months, or never.

In practice this matters most for teams that need to use preview features in production — common in fast-moving Azure AI workloads, confidential computing, or anything using features that are GA in one region and preview everywhere else.

4. Team Background and Ecosystem

If your team is primarily infrastructure engineers with existing Terraform expertise across AWS and GCP — don't switch to Bicep just because you added Azure. The productivity cost of a tool change outweighs the purity of a native DSL.

If your team is primarily Azure developers or .NET engineers who know az cli but not Terraform — Bicep's syntax is closer to what they're used to (resource declarations, ARM parameter files, VS Code extension with IntelliSense).


Code Side-by-Side

The same App Service Plan + Web App in both tools:

Terraform (HCL):

resource "azurerm_service_plan" "api" {
  name                = "asp-portal-prod"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  os_type             = "Linux"
  sku_name            = "P1v3"
}

resource "azurerm_linux_web_app" "api" {
  name                = "app-portal-api-prod"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  service_plan_id     = azurerm_service_plan.api.id
  https_only          = true

  site_config {
    minimum_tls_version = "1.2"
    ftps_state          = "Disabled"
    application_stack {
      dotnet_version = "8.0"
    }
  }

  identity {
    type = "SystemAssigned"
  }
}

Bicep:

param location string = resourceGroup().location
param appName string = 'app-portal-api-prod'

resource appServicePlan 'Microsoft.Web/serverfarms@2024-04-01' = {
  name: 'asp-portal-prod'
  location: location
  kind: 'linux'
  sku: {
    name: 'P1v3'
    tier: 'PremiumV3'
  }
  properties: {
    reserved: true
  }
}

resource webApp 'Microsoft.Web/sites@2024-04-01' = {
  name: appName
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
    siteConfig: {
      minTlsVersion: '1.2'
      ftpsState: 'Disabled'
      linuxFxVersion: 'DOTNETCORE|8.0'
    }
  }
}

The Bicep version is slightly more verbose in the properties block but uses the actual ARM API schema directly — which means the VS Code extension autocompletes every valid property with the right type.


The Hybrid Pattern Most Teams Actually Adopt

After a few years of running both, most Azure shops end up with something like this:

infra/
├── terraform/          # Platform layer — Terraform
│   ├── networking/     # VNet, subnets, NSGs, Private DNS
│   ├── security/       # Key Vault, Entra ID app registrations
│   ├── monitoring/     # Log Analytics Workspace, Action Groups
│   └── policy/         # Azure Policy assignments, initiatives
└── bicep/              # Application layer — Bicep
    ├── app-services/   # App Service Plans, Web Apps, slots
    ├── databases/      # Azure SQL, Cosmos DB, PostgreSQL Flex
    ├── storage/        # Storage Accounts, Blob containers
    └── ai/             # Azure OpenAI, AI Search, AI Foundry

Terraform handles the platform layer — things that change rarely, that span the whole organization, and that you need to replicate across cloud providers (DNS, routing tables, policy). The state-based model is exactly right here: you want tight drift detection on your VNet configuration.

Bicep handles application resources — things that need day-zero Azure API support, that are owned by individual product teams, and where the stateless model is a feature (ARM already knows what's deployed; the team doesn't need a separate state backend per microservice).

The two layers communicate via ARM resource IDs and Azure outputs. Terraform can read Bicep-deployed resources using data "azurerm_" data sources. Bicep can reference Terraform-provisioned resources via existing keyword:

// Reference a subnet provisioned by Terraform
resource vnet 'Microsoft.Network/virtualNetworks@2024-05-01' existing = {
  name: 'vnet-platform-prod'
  scope: resourceGroup('rg-platform-prod')
}

resource subnet 'Microsoft.Network/virtualNetworks/subnets@2024-05-01' existing = {
  parent: vnet
  name: 'app-subnet'
}

When Terraform Wins Outright

  • Multi-cloud or hybrid-cloud organizations — single workflow for everything
  • Large platform engineering teams with existing Terraform expertise and module libraries
  • Workloads that require complex dependency graphs across providers (Azure + GitHub + Cloudflare + Datadog)
  • Organizations using Sentinel policy-as-code or Spacelift/Atlantis for governance
  • Teams that want drift detection with explicit terraform plan before every change

When Bicep Wins Outright

  • Fully Azure environments where you never plan to manage non-Azure resources
  • Workloads that need new Azure preview APIs the week they ship
  • Smaller teams that don't want to manage a Terraform state backend
  • Azure DevOps or GitHub Actions pipelines already using az cli heavily
  • Teams where developers (not infra engineers) own deployments — Bicep's VS Code extension has excellent autocomplete

When It Genuinely Doesn't Matter

For greenfield Azure-only deployments with a team that has no existing IaC expertise in either tool: flip a coin. Both have good documentation, active communities, and Azure DevOps pipeline templates. The best tool is the one your team will actually maintain.


State of Play in 2026

A few things that shifted the calculus recently:

OpenTofu — the Linux Foundation fork of Terraform post-BSL-license-change — is now at 1.9 and is production-stable. If you were hesitant about Terraform because of HashiCorp's license changes, OpenTofu is a drop-in replacement. The azurerm provider works unchanged.

Bicep Deployment Stacks — introduced in 2024, Deployment Stacks let you manage the full lifecycle of a group of Bicep-deployed resources including deletion. This closes one of Bicep's biggest gaps vs Terraform: the inability to cleanly delete resources when you remove them from your template. With Deployment Stacks, ARM tracks the stack and can deny changes or delete unmanaged resources on update. For many teams this removes the last reason to choose Terraform over Bicep for app-layer resources.

Terraform Provider v4azurerm ~> 4.0 dropped in late 2024 with breaking changes to the resource model for several services. If you're migrating from v3, budget time for it. The changes are documented but non-trivial for large estates.


Decision Framework

Start here:

  1. Do you manage non-Azure cloud resources? Yes → Terraform. No → continue.
  2. Do you need Azure preview features immediately when they ship? Yes → Bicep (or hybrid). No → continue.
  3. Does your team already have strong HCL expertise? Yes → Terraform. No → continue.
  4. Is your team small (<5 people running IaC) and fully Azure-focused? Yes → Bicep. No → continue.
  5. Do you have a large existing Terraform module library? Yes → Terraform. No → consider the hybrid pattern.

Most organizations that seriously evaluate this end up at the hybrid pattern. That's not a cop-out — it's a genuine recognition that Terraform and Bicep have different strengths at different layers of the stack, and using each where it excels is better engineering than religious adherence to one tool.


Checklist Before You Decide

RequirementTerraformBicep
Multi-cloud resources
Azure preview API day-zero
Stateless deployment model
Drift detection with explicit planpartial
VS Code IntelliSense on all propertiespartial
Large community module registrygrowing
Clean resource deletion (stacks)✓ (Deployment Stacks)
Sentinel / OPA policy enforcementPSRule
No external state backend needed
Works with existing azurerm modules

Both columns have more checkmarks than they used to. The gap between the tools narrowed significantly in 2024-2026. That's why the hybrid pattern is increasingly the right call — not because teams can't decide, but because the tools genuinely optimize for different problems.