IaC Journey continues 2022 (Bicep)

With previous practice using JSON template and reading up on IaC through colleagues of mine, I decided the next step is testing Bicep. I started this series of blogs with the naked truth of my coding journey from 2004 to 2022, I recommend reading it here as this is follow-my-journey from that post.

Bicep is a language on top of ARM Templates, and isn’t replacing ARM Templates within Microsoft Azure, at least not yet. Bicep is replacing the JSON file commonly used to deploy infrastructure in Azure. JSON and Bicep is a two structures to write infrastructure code, and when deployed is translated to ARM Templates. Bicep translates this before sending it to the ARMs API, while JSON sends it raw-file and makes ARM translate into ARM Template. And last ARM is the service in Azure receiving the ARM Templates and responsible for creating your Azure Resources, in the way you wrote them using either JSON or Bicep.

Bicep -> ARM Templates -> ARM -> Azure Resources

To learn either of these languages I need some examples, and back in the earlier days with JSON we could export a click-ops infrastructure to JSON. Read the JSON and learn how to write in JSON.

Using Bicep we don’t have that option, but if you already have a lot of JSON code, we can transform them to Bicep and start learning from that. Using the Bicep module we can decompile JSON code and turn it into Bicep code. So it is an easy transition from JSON to Bicep if you have already invested a lot into JSON code. Read how to in this Microsoft Learn Article about Decompiling JSON to Bicep.

But I don’t have a lot of JSON code I want to decompile. I know an infinite amount of code can be found on Github, including code examples from Microsoft. These Github pages also has a readme file presented on the frontpage of the Github repository. For Bicep we have this repo from Microsoft and through the readme file shown on the frontpage we can learn a lot of Bicep. Where it comes from, what it is in proper terms, when it doesn’t work, whats in the roadmap, how do you use bicep, and we also find example Bicep code for infrastructure in Azure (azure-quickstart-templates/main.bicep at master · Azure/azure-quickstart-templates · GitHub).

I already use Visual Studio Code (VS Code) for script/code and it is the recommended tool to write Bicep code to, so download and install Visual Studio Code for free. And install the Bicep Extension to make VS Code understand Bicep code and help you write the code correct.

Bicep extension for VS Code

Now we are ready to write some Bicep code, and try deploying it to Azure. But in order to do that I need to understand what I need to write in my code and I also found out about parameters and variables.

Parameters is unique settings for each deployment just like JSON. They are bought using parameters from a separate parameter file when provided, but using parameters VS Code will ask for your input during deployment. Just like PowerShell. VS Code will also suggest creating a parameter-file if you didn’t use one. Although it is nice to have the parameters documented, the more important is the actual code so we can reuse the bicep template and only provide unique parameters for the next deployment. We can also apply rules to parameters, only allowing a sub-set of what we accept in our infrastructure. We can also do this with Azure Policies, but then you know wouldn’t know until deployment failed. The error will clearly say what isn’t supported, but not what is actually supported.

An example using parameters to only allow two options and set Virtual Machine type for later use in the code:


- To deploy a Windows VM use the value "Windows"
- To deploy a Linux VM use the value "Linux"

param WindowsOrLinux string

Variables, after choosing your parameters of Virtual Machine type, we use variables to further narrow down the unique configuration. It will depend on the previous parameter, whether the deployment chooses the variables for Windows or Linux.

var image = (
windows: {
publisher: 'MicrosoftWindowsServer'
offer: 'WindowsServer'
sku: '2022-Datacenter'
version: 'latest'
linux: {
publisher: 'Canonical'
offer: 'UbuntuServer'
sku: '22.04-LTS'
version: 'latest'

So if you chose Windows you shall receive a Windows Server 2022 DataCenter in the latest version available in the Azure Gallery. Now we need to tie the parameter and variable together in a matching pair.

resource vm
'Microsoft.Compute/virtualMachine@2021-03-01' = {

  storageProfile: {
    imageReference: {
      publisher: image[WindowsOrlinux].publisher
      offer: image[WindowsOrlinux].offer
      sku: image[WindowsOrlinux].sku
      version: image[WindowsOrlinux].version

Github Repo with above code.

Here we are telling the ARM Templates to select the appropriate publisher, offer, sku and version based on the parameter we provided when prompted for Windows or Linux ($WindowsOrlinux).

I read these three as the main building blocks for a Bicep template to code your infrastructure in Azure using Bicep code.

That said a complete Bicep template for deploying a VM in Azure requires more code in order to provide ARM necessary information to create your VM. Such as storage, network VMname, VMsize, etc.

You can find a good starter bicep code which will deploya VM, in this fellows github-repo.

My updated version can be found in my Github repo, using the latest version of service providers in Azure as I am writing this blogpost.

Building modules

I did have to manually create my own resource group prior to running previous deployment. I read somewhere Bicep couldn’t do that in the same deployment, so we need to create a main deployment for the Resource Group. And inside this deployment we nest in the virtual machine deployment used above.

This is called building modules, not unlike coding with JSON:

module demoDeployment './SecondBicepDeployment.bicep' = {
  name: 'demoDeployment'
  scope: rgDemoDeployment
  params: {
    vmUserName: 'adminuser'
    vmPass: 'Password'
    windowsOrlinux: 'windows'
    location: location

How to deploy the code?

In order to deploy the code, you need to access an Azure environment and there are multiple ways we can talk to Azure. I am still using PowerShell, so that would be the easiest way to deploy my bicep code to Azure. But I am not looking for the easiest way, so here is a list of ways I tested:

  • PowerShell
  • Azure CLI
  • Azure Cloud Shell
  • VS Code

To me I benefit from previous experience with PowerShell, but Bicep isn’t built into PowerShell. We need install Bicep separately in order for PowerShell to support Bicep deployment. Because this local bicep installation translates our bicep code into ARM Template for the ARM Service.

For developers, it might be quicker to use Azure CLI for Bicep deployment. Azure CLI includes Bicep and will work out of the box.

The Azure Cloud Shell is also PowerShell and CLI, but rather then starting the tools locally on your device, you start either them using Azure Cloud Shell in your Azure Portal. Quite convenient and in the cloud shell you have your own profile, so you can install modules and they will be there next time you open Azure Cloud Shell.

I also stumbled upon deployment using VS Code, which was nice and if you are familiar with VS Code, it was a nice interactive way to use VS Code for Bicep deployment. But I am logged onto Windows with a different user/tenant, and I wasn’t able to change which user/tenant VS Code was deploying yet.

A nice benefit from Bicep deployment with VS Code was the automated generation of a parameter-file based on your interactive input during deployment.

Generates Parameter-file based on interactive input during deployment


Because it was such a benefit from VS Code Bicep deployment to create a parameter-file based on your interactive input during deployment, I figured one more chapter to cover parameter-files.

Because we want to reuse the code and deploy it over and over again, we shouldn’t need to change anything to the code for each time we deploy the code. The benefit of IaC is re-usable code making for a quicker/consistent/documented deployment, compared to click-ops.

But all servers can’t be the same, so we provide interactive input during deployment where each deployment are different. For documentation, it is nice of VS Code to write this input down and generate a parameter-file for each deployment.

But we can also provide a parameter-file during deployment:

  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "",
  "parameters": {
    "location": {
      "value": "westeurope"
    "vmUserName": {
      "value": "adminuser"
    "windowsOrlinux": {
      "value": "windows"

Thank you for reading and hope it helps you to.

If not, feel free to reach out to me and we can look at it together.

Leave a Reply


I am Roy Apalnes, a Microsoft Cloud Evangelist working av Sopra Steria. Main focus in Microsoft Security and Endpoint Management, with a bigger picture in mind.

Featured Posts