Cloud can be tricky sometimes. Find out what scenarios we've ran into that are worth being mentioned and explained.
Introduction
This article explores the differences between Azure ARM templates and Azure Bicep, explaining their syntax variations and how to efficiently convert ARM templates into Bicep using Azure CLI.
What Are Azure ARM and Azure Bicep?
Both Azure ARM (Azure Resource Manager) and Bicep serve the same goal: defining and deploying infrastructure as code in Azure. However, before Azure Bicep was introduced, ARM templates—written in JSON—were the primary method for declarative infrastructure deployment. Due to JSON's rigid structure and verbosity, ARM templates can become complex, difficult to manage, and cumbersome to read.
Azure Bicep, on the other hand, is a domain-specific language (DSL) designed specifically for Azure infrastructure. It introduces a simplified syntax, improved readability, and enhanced reusability through modules. Despite its differences, Bicep is built on top of ARM, meaning every resource deployable via ARM can also be deployed with Bicep.
The diagram above illustrates the process of deploying a Bicep template through Azure Resource Manager. In this workflow, ARM templates act as an intermediate layer between Bicep and Azure Resource Manager. When a Bicep template is built, it is transpiled into an ARM JSON template, which is then processed by Azure for deployment.
az bicep build --file my-template.bicep
az bicep decompile --file my-template.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"variables": {
"appCount": 3,
"baseName": "[uniqueString(resourceGroup().id)]"
},
"resources": [
{
"name": "[concat(variables('baseName'), '-app-', copyIndex())]",
"type": "Microsoft.Web/sites",
"apiVersion": "2021-02-01",
"location": "[resourceGroup().location]",
"properties": {},
"copy": {
"name": "appLoop",
"count": "[variables('appCount')]"
}
}
]
}
Bicep Equivalent
var appCount = 3
var baseName = uniqueString(resourceGroup().id)
resource appService 'Microsoft.Web/sites@2021-02-01' = [for i in range(0, appCount): {
name: '${baseName}-app-${i}'
location: resourceGroup().location
properties: {}
}]
{
"parameters": {
"deployLogAnalytics": {
"type": "bool",
"defaultValue": true
}
},
"resources": [
{
"condition": "[parameters('deployLogAnalytics')]",
"name": "log-analytics-workspace",
"type": "Microsoft.OperationalInsights/workspaces",
"apiVersion": "2021-06-01",
"location": "[resourceGroup().location]",
"properties": {}
}
]
}
param deployLogAnalytics bool = true
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-06-01' = if (deployLogAnalytics) {
name: 'log-analytics-workspace'
location: resourceGroup().location
properties: {}
}
{
"resources": [
{
"name": "my-keyvault",
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2021-11-01",
"location": "[resourceGroup().location]",
"properties": {}
},
{
"name": "my-functionapp",
"type": "Microsoft.Web/sites",
"apiVersion": "2021-02-01",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults', 'my-keyvault')]"
],
"properties": {}
}
]
}
resource keyVault 'Microsoft.KeyVault/vaults@2021-11-01' = {
name: 'my-keyvault'
location: resourceGroup().location
properties: {}
}
resource functionApp 'Microsoft.Web/sites@2021-02-01' = {
name: 'my-functionapp'
location: resourceGroup().location
properties: {}
dependsOn: [keyVault] // Explicit dependency (not needed if keyVault is referenced)
}
az bicep decompile --file template.json
az bicep build --file converted.bicep
Conclusion