An Introduction to Azure Bicep: A Hands-On Tutorial

VAIBHAV HARIRAMANI
10 min readJun 8, 2024

--

Introduction

In today’s cloud-driven world, managing infrastructure as code (IaC) has become essential for scalable and maintainable cloud solutions. Microsoft Azure offers several IaC tools, with Azure Bicep emerging as a powerful, simplified language for deploying resources in Azure. This tutorial will guide you through the basics of Azure Bicep, from defining resources to using modules for better organization.

What is Bicep?

Azure Bicep is a domain-specific language (DSL) for deploying Azure resources declaratively. It aims to simplify the authoring experience compared to JSON-based Azure Resource Manager (ARM) templates. Bicep files (.bicep) are transpiled into ARM JSON templates, which can then be deployed to Azure. The key benefits of Bicep include improved readability, modularity, and ease of maintenance.

How is Bicep related to ARM templates?

You might already be familiar with Azure Resource Manager templates (ARM templates), which are files that represent Azure resources. Until Bicep was available, ARM templates had to be written in a special JSON format. One common problem with JSON templates is that they’re difficult to work with because they have a complex syntax. It can be hard to get started with writing ARM templates in JSON.

Bicep solves these problems by using a much simpler language designed specifically to help you deploy resources to Azure.

Behind the scenes, Resource Manager still operates based on the same JSON templates. When you submit a Bicep template to Resource Manager, the Bicep tooling converts your template to a JSON format in a process called transpilation. This process isn’t something you typically have to think about, but you can view the JSON template file that’s created from the Bicep file.

Define Resources

In Bicep, resources are defined using a straightforward and concise syntax. Each resource has a type, name, and properties that specify its configuration. Let’s start by defining a simple Azure Storage Account.

Install

The install command adds the Bicep CLI to your local environment. For more information, see Install Bicep tools. This command is only available through Azure CLI.

To install the latest version, use:

Azure CLI

az bicep install

To install a specific version:

Azure CLI

az bicep install --version v0.3.255

To validate your Bicep CLI installation, use:

Azure CLI

az bicep version

To upgrade to the latest version, use:

Azure CLI

az bicep upgrade

For more commands, see Bicep CLI.

Azure PowerShell doesn’t automatically install the Bicep CLI. Instead, you must manually install the Bicep CLI.

Important

The self-contained instance of the Bicep CLI installed by Azure CLI isn’t available to PowerShell commands. Azure PowerShell deployments fail if you haven’t manually installed the Bicep CLI.

When you manually install the Bicep CLI, run the Bicep commands with the bicep syntax, instead of the az bicep syntax for Azure CLI.

To check your Bicep CLI version, run:

Windows Command Prompt

bicep --version

Install manually

The following methods install the Bicep CLI and add it to your PATH. You must manually install for any use other than Azure CLI.

When installing manually, select a location that is different than the one managed by Azure CLI. All of the following examples use a location named bicep or .bicep. This location won’t conflict with the location managed by Azure CLI, which uses .azure.

Define Resources in a Bicep Template

To begin, you’ll need the Azure CLI and Bicep CLI installed. Create a new file named main.bicep and add the following code to define a Storage Account:

param location string = resourceGroup().location

@minLength(3)
@maxLength(24)
param name string = 'testDemoStorageAccount'

@allowed([
'Premium_LRS'
'Premium_ZRS'
'Standard_GRS'
'Standard_LRS'
'Standard_RAGRS'
'Standard_ZRS'
])
param type string = 'Standard_LRS'

resource stacc 'Microsoft.Storage/storageAccounts@2020-08-01-preview' = {
name: name
location: location
kind: 'StorageV2'
sku: {
name: type
}
}

In this example:

  • resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' defines a Storage Account resource.
  • name sets the name of the Storage Account, ensuring uniqueness by using uniqueString.
  • location specifies the location of the resource, set to the resource group's location.
  • sku and kind define the Storage Account's SKU and kind, respectively.
  • properties include additional configurations like accessTier.

Explanation

Parameter & Constraints Declaration

Parameters allow you to pass values into your Bicep file. This makes your deployment templates more flexible and reusable.

Location Parameter:

param location string = resourceGroup().location
  • This parameter takes the location of the resource group as its default value.

Name Parameter with Constraints:

@minLength(3) 
@maxLength(24)
param name string = 'testDemoStorageAccount'

This parameter declares the name of the storage account with constraints on the length (minimum 3 and maximum 24 characters).

Type Parameter with Allowed Values:

@allowed([   
'Premium_LRS'
'Premium_ZRS'
'Standard_GRS'
'Standard_LRS'
'Standard_RAGRS'
'Standard_ZRS' ])
param type string = 'Standard_LRS'

This parameter defines the type of the storage account with a set of allowed values. The default value is set to ‘Standard_LRS’.

Resource Declaration

Resources in Bicep are declared using the resource keyword, followed by the resource name, type, and API version.

  • Storage Account Resource:
resource stacc 'Microsoft.Storage/storageAccounts@2020-08-01-preview' = {   
name: name
location: location
kind: 'StorageV2'
sku: {
name: type
}
}

This resource declaration creates a storage account with the following properties:

  • name: The name of the storage account, provided by the name parameter.
  • location: The location of the storage account, provided by the location parameter.
  • kind: The kind of storage account, set to 'StorageV2'.
  • sku: The SKU of the storage account, determined by the type parameter.

Add Flexibility by Using Parameters and Variables

To make your Bicep template more reusable, you can add parameters and variables. Parameters allow users to input values at deployment time, while variables store intermediate values.

Here, param keywords define parameters for the Storage Account name, location, SKU, and access tier. These parameters provide flexibility and reusability to your template.

Modify main.bicep to include parameters and variables:

Using var for Variables

Variables in Bicep (var) are used to store intermediate values or complex expressions, which can be reused throughout the template.

Example of Variable Declaration

var containerName = 'myContainer'

In this example, a variable named containerName is declared and assigned the value 'myContainer'.

Define a Blob Service and Container

To add a container to the storage account, we first define a blob service as the parent resource. Then, we add the container using the parent property to reference the blob service.

Here’s how to do it:

resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = {
parent: stacc
name: 'default'
}

resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {
parent: blobService
name: containerName
}

Explanation

  • Blob Service Resource:
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = {   
parent: stacc
name: 'default'
}

Here, we define a blob service as a child of the storage account (stacc). The parent property simplifies the relationship between the storage account and the blob service.

  • Container Resource:
resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {   
parent: blobService
name: containerName
}

This code defines a container as a child of the blob service. The parent property ensures the container is correctly associated with the blob service.

This interpolation results in the full resource name path required to create the container inside the specified storage account.

Here’s an extended example that includes the use of a variable:

param location string = resourceGroup().location@minLength(3)
@maxLength(24)
param name string = 'testDemoStorageAccount'
@allowed([
'Premium_LRS'
'Premium_ZRS'
'Standard_GRS'
'Standard_LRS'
'Standard_RAGRS'
'Standard_ZRS'
])
param type string = 'Standard_LRS'
var containerName = 'myContainer'resource stacc 'Microsoft.Storage/storageAccounts@2020-08-01-preview' = {
name: name
location: location
kind: 'StorageV2'
sku: {
name: type
}
}
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = {
parent: stacc
name: 'default'
}
resource containers 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {
parent: blobService
name: containerName
}

Compile the Bicep File

Use the bicep build command to compile your Bicep file into an ARM template:

az bicep build --file .\main.bicep

This command generates a file named main.json in the same directory, which is the ARM template equivalent of your Bicep file.

Deploy the ARM Template

1. Create a Resource Group

To create a resource group, use the az group create command. Replace <resource-group> with your desired resource group name and <location> with the desired Azure region.

az group create --location westus --resource-group MyResourceGroup

2. Deploy the ARM Template

After compiling your Bicep file into an ARM template, deploy it using the az deployment group create command.

az deployment group create --resource-group <resource-group> --template-file main.json

OR

use command

New-AzResourceGroupDeployment -TemplateFile main.bicep

note: if you get error like this

New-AzResourceGroupDeployment : The term 'New-AzResourceGroupDeployment' is not recognized as the name of 
a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again.
At line:1 char:1
+ New-AzResourceGroupDeployment -TemplateFile main.bicep
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (New-AzResourceGroupDeployment:String) [], CommandNotFoundEx
ception
+ FullyQualifiedErrorId : CommandNotFoundException

You need to install the Azure Powershell module:

You can look for just the one for this command:

Install-Module -Name Az.Resources -AllowClobber -Scope CurrentUser

Or all of them:

Install-Module -Name Az -AllowClobber -Scope CurrentUser

Group Related Resources by Using Modules

As your infrastructure grows, organizing related resources into modules improves readability and maintainability. Modules in Bicep allow you to encapsulate and reuse resource definitions.

Refactor Your Template to Use Modules

Create a new file named storage.bicep and move the Storage Account definition into it:

param location string 
param name string

@allowed([
'Premium_LRS'
'Premium_ZRS'
'Standard_GRS'
'Standard_LRS'
'Standard_RAGRS'
'Standard_ZRS'
])
param type string = 'Standard_LRS'

param containerName string

resource stacc 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: name
location: location
kind: 'StorageV2'
sku: {
name: type
}
}

resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = {
parent: stacc
name: 'default'
}

resource containers 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {
parent: blobService
name: containerName
}

Now, update mainmodule.bicep to use this module:

param location string = resourceGroup().location@minLength(3)
@maxLength(24)
param storageAccountName string = 'testbiceccount'
param containerName string = 'imagefromimport'module storage 'storage.bicep' = {
name: 'storageModule'
params: {
location: location
name: storageAccountName
type: 'Standard_LRS'
containerName: containerName
}
}

In mainmodule.bicep, the module keyword imports the storage.bicep module and passes parameters to it. This approach helps manage complex deployments by breaking them into smaller, reusable components.

3. Compile and Deploy

Compile Bicep Files

Compile both Bicep files into ARM templates:

az bicep build --file .\storage.bicep
az bicep build --file .\mainmodule.bicep

Create Resource Group and Deploy

Run the following command to create the resource group and deploy the compiled main.bicep file:

az deployment group create --resource-group myResourceGroup --template-file main.json
new resources are created

Summary

Azure Bicep simplifies the process of defining, deploying, and managing Azure resources with its clean and concise syntax. By following this tutorial, you’ve learned how to:

  • Define resources using Bicep.
  • Add flexibility with parameters and variables.
  • Organize your template with modules for better maintainability.

As you continue to explore Azure Bicep, you’ll find it a powerful tool for managing your Azure infrastructure efficiently. For more advanced scenarios, refer to the official Azure Bicep documentation.

Github repo:

Happy coding!

But wait, there’s more to explore!

In upcoming articles, I’ll delve into exciting topics like Ansible, Helm charts, and beyond. Your feedback and questions are invaluable, so feel free to share as I continue this learning adventure. Stay curious, and let’s keep building amazing things! 🚀

Thank You for reading

Please give 👏🏻 Claps if you like the blog.

Made with ❤️by Vaibhav Hariramani

Don’t forget to tag us

if you find this blog beneficial for you don’t forget to share it with your friends and mention us as well. And Don’t forget to share us on Linkedin, instagram, facebook , twitter, Github

More Resources

To learn more about these Resources you can Refer to some of these articles written by Me:-

Do Checkout My other Blogs

Do find time check out my other articles and further readings in the reference section. Kindly remember to follow me so as to get notified of my publications.

Do Checkout My Youtube channel

Follow me

on Linkedin, instagram, facebook , twitter, Github

Happy coding ❤️ .

--

--

VAIBHAV HARIRAMANI

Hi there! I am Vaibhav Hariramani a Travel & Tech blogger who love to seek out new technologies and experience cool stuff.