Getty Images

Tip

Compare Azure Bicep vs. ARM templates

Azure Bicep can be easier to use than ARM templates when deploying Azure resources. Learn how the languages compare and how Bicep can help automate deployment.

Azure Resource Manager templates are the official Microsoft-supported method for deploying resources in Azure. This native option enables IT teams to deploy a resource through the portal and export it as an ARM template for reuse.

As convenient as Microsoft has made it to auto generate ARM templates, these templates can still be difficult to work with due to their verbosity and complexity. The Azure Bicep language can help.

Learn what Azure Bicep is, how it compares to ARM and factors that influence which tool to use.

What is Azure Bicep?

Azure Bicep is a domain-specific language that operates as an abstraction layer over ARM and ARM templates. Bicep is a simplified way to author ARM templates indirectly.

To deploy a Bicep file to Azure, first write a Bicep file. Then, use either the Azure CLI or Azure PowerShell modules to deploy the Bicep template directly to Azure. These tools convert the main.bicep file into an ARM template in the background before submitting them to the Azure back end. This conversion process is called transpiling.

Use the following Bicep CLI command to convert from a Bicep file to an ARM template: bicep build main.bicep.

This command produces the ARM template equivalent of the resources represented in the main.bicep file. The ARM template is noticeably longer and more complex than the Bicep template.

Bicep vs. ARM

The following scenario showcases the differences in Bicep templates that are converted to ARM, such as length and complexity.

This example uses a template from the Azure Quickstart Templates repository. As seen here, the Quickstart Template deploys a storage account, supports a couple of parameters and has some output:

@description('Storage Account type')
@allowed([
  'Premium_LRS'
  'Premium_ZRS'
  'Standard_GRS'
  'Standard_GZRS'
  'Standard_LRS'
  'Standard_RAGRS'
  'Standard_RAGZRS'
  'Standard_ZRS'
])
param storageAccountType string = 'Standard_LRS'

@description('The storage account location.')
param location string = resourceGroup().location

@description('The name of the storage account')
param storageAccountName string =
'store${uniqueString(resourceGroup().id)}'

resource sa 'Microsoft.Storage/storageAccounts@2022-09-01' = {
 
name: storageAccountName
  location: location
  sku: {
    name: storageAccountType
  }
  kind: 'StorageV2'
  properties: {}
}

output storageAccountName string = storageAccountName
output storageAccountId string = sa.id

The total line count is 31. Save it as sa.bicep, and run the following bicep build command from the same directory: bicep build sa.bicep.

The following file is called sa.json:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.16.2.56959",
      "templateHash": "1346177644236912037"
    }
  },
  "parameters": {
    "storageAccountType": {
      "type": "string",
      "defaultValue": "Standard_LRS",
      "allowedValues": [
        "Premium_LRS",
        "Premium_ZRS",
        "Standard_GRS",
        "Standard_GZRS",
        "Standard_LRS",
        "Standard_RAGRS",
        "Standard_RAGZRS",
        "Standard_ZRS"
      ],
      "metadata": {
        "description": "Storage Account type"
      }
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]",
      "metadata": {
        "description": "The storage account location."
      }
    },
    "storageAccountName": {
      "type": "string",
      "defaultValue": "[format('store{0}', uniqueString(resourceGroup().id))]",
      "metadata": {
        "description": "The name of the storage account"
      }
    }
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2022-09-01",
      "name": "[parameters('storageAccountName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "[parameters('storageAccountType')]"
      },
      "kind": "StorageV2",
      "properties": {}
    }
  ],
  "outputs": {
    "storageAccountName": {
      "type": "string",
      "value": "[parameters('storageAccountName')]"
    },
    "storageAccountId": {
      "type": "string",
      "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
    }
  }
}

Now, the total line count is 67. You can discount lines four through 10, as those are metadata from the transpilation to ARM. Regardless, this output has significantly more lines than the Bicep template, creating a compelling case for writing Bicep instead of ARM.

Use Microsoft's Bicep Playground to author a Bicep template, view the resulting ARM template and compare the two. Here is a comparison between the two for the previous example.

The differences in Bicep templates that are converted to ARM
Compare the two examples.

Migrate from ARM to Bicep

Microsoft officially recommends the use of Bicep. Bicep has feature parity with ARM and is easier to use. For further examples of syntax comparison, check out Microsoft's language comparison.

Bicep files still convert to the ARM language before they're submitted to Azure, so ARM isn't going away in the foreseeable future. Users comfortable with the ARM language should feel safe to keep writing with it.

Those that choose to learn Bicep can get started by using the Bicep CLI to decompile ARM templates into Bicep. Microsoft warns about possible errors and gives users a starting point with templates they're already familiar with.

Here's an example of decompilation. Using the previous storage account example, save the ARM template version to sa.json. Run the following Bicep CLI command: bicep decompile sa.json.

Example of decompilation in Azure Bicep
An example of decompilation

The output file is similar to Quickstart's original main.bicep file. In fact, the only difference is the resource name.

Next, review variable names within the Bicep file, and attempt to build the file back into an ARM template to view any transpilation errors. This is where things can get tricky, but error messages point users in the right direction as they learn the language.

Key takeaway

While Bicep is easier to write and understand, ARM isn't going anywhere. In the decompile command, you can still use ARM Templates and decompile them into Bicep as a way to learn Bicep. If you want to get fancy, you can even export a resource group as an ARM template and then decompile that straight into Bicep. This is the joy of using native tooling when the native tooling is incredibly effective.

Dig Deeper on Cloud provider platforms and tools