Eduard Keilholz

Hi, my name is Eduard Keilholz. I'm a Microsoft developer working at 4DotNet in The Netherlands. I like to speak at conferences about all and nothing, mostly Azure (or other cloud) related topics.
LinkedIn | Twitter | Mastodon | Bsky

I received the Microsoft MVP Award for Azure

Eduard Keilholz
HexMaster's Blog
Some thoughts about software development, cloud, azure, ASP.NET Core and maybe a little bit more...

Comparing ARM templates with Bicep

Syntax comparison

Comparing the ARM template syntax with the Bicep syntax is like comparing apples and pears. ARM templates are written using JSON, while Bicep is just a brand new syntax, But it’s worth mentioning that Bicep is created, to overcome the struggles developers experienced writing ARM templates. This results in a powerful, easy to write and maintain language that allows you to write your infrastructure as code.

Deploying Bicep

Deploying Bicep files is as easy as ARM templates as it works very similarly. In previous versions of the ARM, Bicep was just a transpiler. Once you finished your Bicep files, you were required to transpile those to an ARM template and then present that to the ARM. Today, you can skip that transpile step and present your Bicep file immediately to the ARM. So to create a new resource group, for example, can be as easy as az deployment group create -f ./your-bicep-file.bicep -g your-resource-group.

This also means you can now present your bicep files to the ARM from within your automated CI/CD processes (GitHub Actions / Azure DevOps Pipelines).


Very much like ARM templates, I like VS Code best for editing templates. Not only is the tool quick and straightforward, but there are also great extensions available that make like much easier to write these templates. An extension called Bicep, yes… that easy, allows you to write perfect Bicep templates in one go.

Let’s go and create a bicep file that creates an App Service Plan and an Azure Function Web App in Azure. Obviously, this template will not be complete, because to run an Azure Functions project you would also need a storage account, but for the purpose of this demo, let’s stick with the Service Plan and Web App.

Digging in to bicep

For the extension to work properly, your file must have the .bicep extension, so let’s go ahead and create a new file called main.bicep. Now we’re going to define a resource. But this is very different from ARM because all the plumbing for parameters, variables, functions, resources and outputs are left out, you just start declaring your resources. Start typing resource appFarm and when you hit the space bar after appFarm you’ll see that the extension starts helping you with selecting the type of resource you want to create. Once you selected Microsoft.Web/serverfarms, you get to select the API version you want to communicate with. This is the API version of the Resource Provider in the Azure Resource Manager you’re going to use.

resource appFarm 'Microsoft.Web/serverfarms@2020-12-01' = {

In the end, your first line of Bicep will look something similar to the above. Also notice, that the appFarm has scribbles underneath. This is because the resource still contains some errors, but first things first. What we did here, was declaring a resource that will show up as a variable in our template. The variable name is appFarm. Now, why does it contain errors? Because the resource misses some mandatory properties (location and name). But because we’re going to create an Azure Functions Web App, we are going to configure this server farm to use the Consumption Plan and configure it for an Azure Functions web app. In the end, your resource will look like this:

resource appFarm 'Microsoft.Web/serverfarms@2020-12-01' = {
  name: 'your-bicep-serviceplan'
  location: resourceGroup().location
  kind: 'functionapp'
  sku: {
    name: 'F1'
    capacity: 0

The name of the App Service Plan will be ‘your-bicep-serviceplan’ and its location will be set to the same as the resource group the service plan is created in. The kind property allows us to configure the service plan for Azure Functions and the SKU F1 with capacity 0 makes the service plan a Consumption Plan with 0 instances by default.

The web app

Dropping in the web app will be a very similar exercise, except for the fact that we need the web app to use the app service plan we just created and thus creating a dependency.

The Web App declaration in Bicep looks like this:

resource functionApp 'Microsoft.Web/sites@2020-12-01' = {
  dependsOn: [
  name: 'your-bicep-function-app'
  location: resourceGroup().location
  kind: 'functionapp'
  properties: {

The first line creates a resource of type Microsoft.Web/sites and uses Resource Provider version 2020-12-01 to provision that. Now the second line is interesting because it makes the Web App depend on the App Service Plan and you can see that you use the variable name of the App Service Plan to do so. You also need to set the serverFarmId to define which App Service Plan to use and you see that you can just get that ID from the properties of the appFarm variable.

Let’s build this example in a fully working CI/CD pipeline for the next blog.