Stage 4 · Provision
Bicep & ARM Templates for Azure
Bicep vs ARM
Transpilation, debugging, and when to use which — understanding the relationship between Bicep and ARM.
Same Engine, Different Syntax
Bicep is not a replacement for ARM — it is a transpilation layer on top of ARM. The az bicep build command converts .bicep files to ARM JSON. The ARM engine deploys both formats identically. Understanding this relationship is key to debugging and advanced usage.
When you deploy a Bicep file, the az CLI transpiles it to ARM JSON behind the scenes and submits the JSON to the ARM API. The portal, CLI, and SDKs all use the same ARM deployment pipeline regardless of the source format.
How Transpilation Works
$ az bicep build --file main.bicep --outfile main.json
$ az bicep build --file main.bicep # Outputs main.json by default
# Decompile ARM back to Bicep
$ az bicep decompile --file main.jsonThe build command produces ARM JSON that is identical to what you would write by hand. The decompile command converts ARM JSON back to Bicep — useful for migrating existing templates.
param storageName string
param location string = resourceGroup().location
resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageName
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
}
output storageId string = storage.idThis simple Bicep file declares a parameter, a resource, and an output. The transpiled ARM JSON will contain the same resource with the same properties, wrapped in the ARM template schema.
{
"\$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageName": { "type": "string" },
"location": { "type": "string", "defaultValue": "[resourceGroup().location]" }
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('storageName')]",
"location": "[parameters('location')]",
"sku": { "name": "Standard_LRS" },
"kind": "StorageV2"
}
],
"outputs": {
"storageId": { "type": "string", "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]" }
}
}The transpiled ARM JSON is functionally equivalent. Parameter references become [parameters()] expressions. Resource references become [resourceId()] calls. The output wraps the resource reference.
Debugging Bicep
When a Bicep deployment fails, the error message references the transpiled ARM JSON — not your Bicep source. To debug, transpile the Bicep file and examine the ARM JSON to understand what is being deployed.
$ az bicep build --file main.bicep --outfile main.json
# Inspect the generated ARM JSON
$ cat main.json | jq '.resources[0]'
# Fix the Bicep issue, rebuild, and redeployBuilding to ARM JSON lets you inspect exactly what will be submitted to the ARM API. Many Bicep errors are actually ARM-level issues — missing required properties, invalid API versions, or incorrect resource references.
Feature Parity
Bicep supports most ARM features but not all. Some advanced ARM features like linked templates, complete mode deployments, and template specs have limited or different support in Bicep.
| Feature | ARM | Bicep |
|---|---|---|
| Basic resources | Full | Full |
| Parameters and variables | Full | Full |
| Linked templates | Native | Deployments with existing keyword |
| Complete mode | Native | Not directly — use ARM JSON |
| Template specs | Native | Limited support |
| Modules | Linked deployments | Native module syntax |
Decision Matrix
- New Azure project: Use Bicep.
- Existing ARM templates: Migrate incrementally to Bicep.
- Need complete mode: Use ARM JSON for that deployment.
- Need template specs: Use ARM JSON or Bicep with limitations.
- Multi-cloud project: Use Terraform instead.
- Team unfamiliar with Azure: Start with Bicep for simpler syntax.
Migrating ARM to Bicep
$ az bicep decompile --file template.json
# Creates template.bicep — review and fix manually
$ az bicep build --file template.bicep --outfile template.json
# Verify the output matches the originalThe decompile command handles most of the conversion. You will need to manually review and fix edge cases — custom functions, complex expressions, and template spec references.
Do not attempt a big-bang migration. Migrate one template at a time. Start with simple templates and work toward complex ones. Keep the ARM JSON backup until you verify the Bicep output is identical.
Bicep can express everything ARM can, plus additional abstractions like modules, loops, and type safety. The transpilation adds a layer but does not remove any capability.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.