I've written ARM templates, and I know the pain. Hundreds of lines of nested JSON, string interpolation with concat(), and resource IDs that make you question your life choices. Bicep is Microsoft's answer, giving you the same deployment power as ARM templates but with a more readable syntax. By March 2021, it had reached general availability, making infrastructure-as-code on Azure readable again.
ARM templates in JSON are verbose, repetitive, and syntactically brittle. A simple Azure App Service + SQL Database deployment requires hundreds of lines of JSON with deeply nested resource properties, manual resource ID construction using concat(), and no support for comments. Bicep replaces the JSON syntax with a concise declarative language, making the same deployment a fraction of the size and readable to developers not steeped in ARM quirks.
I still remember a night in 2019 when a production rollout stalled because an ARM template built a storage account ID with concat() and missed a leading slash. The deployment error was a vague 'Invalid resource ID' and the logs gave no clue which line caused it. When we rewrote that piece in Bicep, the compiler flagged the missing slash as a type mismatch before we even ran the deployment. In my experience, that early feedback saves at least an hour of firefighting per release, and in larger orgs the savings compound quickly.
Bicep compiles to ARM JSON, with no separate runtime or API. A Bicep file is transpiled to ARM JSON and deployed via the standard ARM deployment API. The compilation is deterministic and reversible, meaning ARM templates can be decompiled to Bicep, with some limitations. This ensures Bicep deployments have identical semantics to ARM JSON deployments, with no new failure modes or deployment services to trust.
In our CI pipelines we run 'az bicep build' to generate the JSON and then feed it to the standard 'az deployment group create' command. The build step is fast – a 300‑line Bicep file compiles in under 150 ms on a modest Azure Pipelines agent – and because the output is pure ARM JSON we can still use existing policy checks and what‑if previews. We also cache the compiled JSON as an artifact, which cuts down on network churn when the same template is promoted across environments.
Bicep modules allow resource definitions to be composed from reusable components. A module is a .bicep file that takes parameters and deploys a set of resources. Modules can be referenced from a local path or from a Bicep registry, enabling platform teams to publish validated, standards-compliant module libraries for application teams to consume. This pattern is similar to Terraform modules but in the Azure-native toolchain.
Versioning modules has been a surprise hurdle. The Bicep registry we set up in Azure Artifacts lets us pin a module to a semantic version, but we quickly learned that a minor bump can introduce a breaking change if a parameter default is altered. To mitigate that we enforce a CI gate that runs 'bicep build' and 'bicep lint' on every PR, and we keep a separate test suite that does a what‑if deployment against a sandbox subscription. The extra step adds a few minutes to the PR cycle but catches mismatches that would otherwise explode in production.
When comparing Bicep to Terraform for Azure, Terraform is language-agnostic and works across providers. Bicep, on the other hand, is Azure-only. For organisations with multi-cloud infrastructure, Terraform or CDK for Terraform may be a better choice. However, for organisations that are Azure-only or Azure-primary, Bicep provides a better developer experience than Terraform for purely Azure resources, with no state file management, no provider plugins, a simpler authentication model, and first-class Azure Portal integration for deployment visualisation.