Infrastructure as code aims to make resource deployment more predictable. Instead of writing complicated scripts, you write out what the resource should look like and then have a tool figure out how to deploy it. For Azure, the infrastructure-as-code solution has been the Azure Resource Manager or ARM templates written in the JSON syntax. Microsoft recently released a new configuration called Azure Bicep, which makes the configuration writing process more manageable. In this article, you'll learn about the Bicep syntax, authoring a Bicep template, and deploying Azure resources using Azure PowerShell and Azure CLI.
Latest posts by Jeff Brown (see all)


To follow along with this tutorial, you will need:

  • An Azure tenant and access to a subscription with Owner or Contributor rights.
  • PowerShell with the Azure PowerShell (Az) module installed. This tutorial uses PowerShell v7.1.4 and Az module v6.6.0.
  • Azure CLI. This tutorial uses v2.29.0.
  • VS Code or other IDE. However, VS Code has extensions available to make the Bicep authoring experience easier. More on this later in the article.

What is Azure Bicep?

Azure Bicep is Microsoft's new declarative language for creating and managing Azure resources. Bicep is a domain-specific language, or DSL, meaning you currently use it only for managing Azure. Microsoft's goal with Azure Bicep is to better the template authoring experience with simpler syntax and better support for reusing code.

You can think of Bicep as a revision of the existing ARM template language. While the syntax has changed, Bicep retains the same functionality and deployment methods. In fact, the Bicep binary takes your Bicep code and converts it to an ARM template during deployment. While Bicep is relatively new, Microsoft has announced that it is ready for production use, starting with version 0.3.

Understanding Azure Bicep benefits

Bicep provides many benefits over traditional ARM JSON templates and other third-party tools.

  • Simpler syntax: Many IT professionals face a steep learning curve in the complex ARM JSON syntax. Azure Bicep syntax is more concise and easier to read in comparison. If you are familiar with the Hashicorp Terraform language, you'll find the Bicep syntax very similar.
  • Faster resource support: Bicep automatically supports all Azure services, whether in preview or generally available. This support includes new API versions of resources. Other third-party providers must update their tools and modules to support new features.
  • Module support: Break your Bicep code into a set of related resources called modules. Modules enable code reusability and simplified deployments.
  • No state management: Tools like Terraform require managing a state file that tracks currently deployed resources. With Bicep, the state is stored in Azure, so you can confidently deploy updates as expected.

How to install Azure Bicep

To get started with Bicep, you need to install a few tools on your local system. Luckily, Microsoft provides multiple ways to use Bicep across Windows, Linux, and macOS.

Bicep CLI

Microsoft has a Bicep-specific command line interface (CLI) for working with Bicep files. You use this CLI to compile Bicep files (.bicep) into ARM JSON templates. You can also decompile ARM JSON templates into Bicep files to convert your existing templates.

The Bicep CLI supports installation on Windows, Linux, and macOS. If you install the Bicep CLI tool manually, you need to add it to your PATH environment variable to use the commands from the command line.

Microsoft provides several scripts and code samples for downloading, installing, and configuring PATH across multiple platforms. Use the links below for more details about your platform.

Azure CLI

If you're a fan of the Azure CLI, you're in luck when it comes to using Bicep. Starting in version 2.20.0 and later, the Azure CLI automatically includes an instance of the Bicep CLI. This Bicep CLI instance is self-contained and does not conflict with any versions you install manually. Azure CLI does not add the Bicep CLI binary to your PATH variable.

Azure CLI automatically installs the Bicep CLI when you issue a command that needs Bicep. If required, you can install, update, or validate the Bicep CLI manually using the following commands:

# Manually install the Bicep CLI in Azure CLI
az bicep install

# Upgrade to the latest Bicep CLI version
az bicep upgrade

# Verify Bicep CLI version
az bicep version

Azure PowerShell

To use Bicep within Azure PowerShell, you need version 5.6.0 or later installed. Unlike the Azure CLI, Azure PowerShell does not automatically install the Bicep CLI. You will need to install the Bicep CLI manually using the links provided above. Be sure to add the Bicep CLI binary to your PATH variable.

The self-contained Bicep CLI instance found in the Azure CLI is not available for PowerShell commands. However, be on the lookout for a future version of the Azure PowerShell module that includes Bicep CLI commands natively.

Writing Azure Bicep files

You can write Bicep templates using the tool or IDE of your choice. Bicep files have a .bicep extension. For the best template authoring experience, you should look at using Visual Studio Code. Microsoft provides a Bicep extension that provides language support and autocompletion to assist in writing Bicep templates.

To install the Bicep extension:

  1. In Visual Studio Code, select the Extension icon on the left side.
    Accessing the Extensions tab in Visual Studio Code
  2. Search the marketplace for "bicep."
  3. Find the Bicep extension from Microsoft, and click Install.

    Installing the Bicep extension in Visual Studio Code

    Installing the Bicep extension in Visual Studio Code

With the Bicep extension installed, Visual Studio Code provides autocomplete entries for resource properties when authoring Bicep templates. For example, here is a definition of a storage account, with Visual Studio Code providing the storage account kind property values.

The Bicep extension provides autocomplete values for resource definition

The Bicep extension provides autocomplete values for resource definition

Understanding Bicep template components

Like other infrastructure-as-code languages, Bicep templates have various components and syntax for defining resources. The following sections introduce just a few of these components and capabilities. To follow along, create an empty file named vnet.bicep, which you'll use to write a template for deploying a virtual network.

Declaring resources

Bicep's primary purpose is to define resources. Resource definitions comprise several key pieces of information. Here is a basic structure for a resource definition, starting with the keywork resource:

resource <symbolic name> <resource type>@<api version> = {
  //Required properties
  name: <resource name>
  location: <Azure region>

  // Remaining resource-specific properties

Let's review each component and the role it plays in the declaration:

  • Symbolic name: The symbolic name represents the resource within the Bicep template. This name will not be the name of the resource. The actual resource name is defined later in the resource block. Symbolic names are case-sensitive.
  • Resource type: The resource type defines what kind of resource you are deploying. If you have worked with ARM templates, these resource types should be familiar. The format includes the resource provider, followed by the resource name. Examples include Microsoft.Network/virtualNetworks or Microsoft.Storage/storageAccounts. For a list of all resource types, check out the Azure template documentation.
  • API version: The API version determines the properties and features that are available when defining a resource. This property is the same as defining an API version in an ARM template.
  • Resource properties: All resources need a name and a deployment location. The combination of resource type and API version determines which properties are required and which are optional.

To get started, open the empty vnet.bicep file created earlier and create a virtual network resource with a symbolic name (myVnet), deployed resource name (vnet-app-prod-westus2), location (westsu2), and addressPrefixes definition ( Also, note the resource type and API version in the first line.

resource myVnet 'Microsoft.Network/virtualNetworks@2021-03-01' = {
  name: 'vnet-app-prod-westus2'
  location: 'westus2'
  properties: {


Like ARM templates, you use parameters to pass data to the configuration during deployment. Parameters make templates more dynamic, as you can use the same template multiple times and customize it to the environment.

You define parameters using the keyword param, followed by the parameter name and type. Parameter types include string, integer, boolean, object, and array. Parameters can also have default values; you add these using an equals (=) sign.

Add two parameters to your Bicep template for environment and location. The environment variable has a decorator for allowed values ('prod', 'qa', and 'dev'), meaning these are the only values you can specify for the parameter. You can read more about the available decorators here.

The location parameter also has a default value using the resourceGroup() function. This default value means that the parameter defaults to the resource group's location if you do not specify the location at deployment time. Read more about available Bicep functions here.

param environment string

param location string = resourceGroup().location


Variables are great when you reuse the same information multiple times in a template. If a value has a complicated expression, just define the expression once, and then use the variable name instead throughout the template. To define a variable, use the var keyword followed by the variable name. Use the equals sign to assign a value to the variable name.

Let's say this virtual network template is for deploying the network components of an application and other resources. You want to establish a base name for Azure resources using the app name, environment, and location.

Create a variable named appName, and use string interpolation to create the base name. String interpolation means that parts of a string have placeholders that are later replaced with an actual value. For the appName definition, the string contains the values passed through the environment and location parameters.

var appName = 'app-${environment}-${location}'

With the parameters and variables defined, you can update the virtual network name and location using these values. Note that the resource name property also uses string interpolation to append "vnet-" to the beginning of the name.

param environment string

param location string = resourceGroup().location

var appName = 'app-${environment}-${location}'

resource myVnet1 'Microsoft.Network/virtualNetworks@2021-03-01' = {
  name: 'vnet-${appName}'
  location: location
  properties: {

Deploying Bicep templates

You deploy Bicep templates the same way you deploy ARM templates. This tutorial uses both Azure PowerShell and Azure CLI as deployment methods. Both examples use an existing resource group named rg-bicepdemo, located in the WestUS2 Azure region.

Azure PowerShell

To deploy the Bicep template using Azure PowerShell:

  1. Connect to your Azure tenant using the Connect-AzAccount command.
  2. If necessary, select the subscription with the rg-bicepdemo resource group using the Set-AzContext command.
  3. Use the New-AzResourceGroupDeployment command specifying the -ResourceGroupName and -TemplateFile path. Use inline parameters to specify the values of -environment and -location.
New-AzResourceGroupDeployment `
     -ResourceGroupName "rg-bicepdemo" `
     -TemplateFile .\vnet.bicep `
     -environment "prod" `
     -location "westus2"

Azure CLI

To deploy the Bicep template using Azure CLI:

  1. Connect to your Azure tenant using the az login command.
  2. If necessary, select the subscription with the rg-bicepdemo resource group using the az account set command.
  3. Use the az deployment group create command specifying the --resource-group and --template-file path. Unlike PowerShell, use --parameters to specify the template parameter values. If there are multiple parameters, separate them using a space.
az deployment group create `
--resource-group rg-bicepdemo `
--template-file vnet.bicep `
--parameters environment=dev location=eastus2

Converting ARM and Bicep templates

If you have a library of existing ARM templates, you can convert these to Bicep-formatted files using the Bicep CLI. Use the bicep decompile command and specify the path to the ARM JSON template.

bicep decompile azure-deploy.json

The decompile process is best-effort, and Microsoft does not guarantee direct mapping between the two template languages. Be sure to review the decompiled Bicep template and fix any warnings or errors. If you encounter an error, feel free to open an issue on the Bicep GitHub Issues page.

You can also convert a Bicep template to an ARM JSON template using the bicep build command, like this:

bicep build vnet.bicep


Microsoft is putting a lot of effort into building the Bicep language and tools. It may take a while, but Bicep will become Microsoft's preferred infrastructure-as-code language for Azure. ARM templates will always have a place, but hopefully seeing Bicep's simpler syntax has convinced you of its value.

Subscribe to 4sysops newsletter!

If you want to learn more about Azure Bicep, check out all the Microsoft Learn modules covering Bicep. If you want to compare Bicep and ARM template syntax, check out the Bicep Playground, where you can compare the two languages.

  1. Joe Abbott 2 years ago

    Thanks a lot. It really helped me.

  2. WorkTime 2 years ago

    Thank you for such a detailed article. It is what I needed now.

Leave a reply

Your email address will not be published. Required fields are marked *


© 4sysops 2006 - 2023


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account