Posted in:

There is a huge variety of options in Azure for creating and configuring resources (e.g. creating a Function App or a Virtual Machine).

  1. Create resources manually in the Azure Portal (great for experimentation and learning)
  2. Create an ARM template (for declarative Infrastructure as Code, great for predictable deployment of production apps)
  3. Script creation of resources using the Azure CLI or Azure PowerShell (great for spinning up/tearing down dev/test resources)
  4. Use the Azure Resource Manager SDKs to create resources in your language of preference such as C# (great when your application itself needs to dynamically create Azure resources such as an Azure Container Instance)
  5. Use third party offerings such as Terraform or Pulumi (great when you need multi-cloud support or want to combine the best of Infrastructure as Code with custom logic)

So when Azure recently announced Bicep, you might be forgiven for wondering why we need yet another way to deploy resources in Azure.

What is Bicep?

Bicep is a "Domain Specific Language for deploying Azure resources declaratively". Probably the easiest way to think of it is that it's simply ARM templates with a much nicer syntax. Bicep code is transpiled to ARM templates. In other words if (like me) you avoid ARM templates in many situations because of their verbose and complex syntax, then Bicep offers a way to give you the strengths of ARM templates without their pain.

I'll do a followup post soon showing an example Bicep template for deploying an Azure Function app, but in this post I want to summarize what I like about Bicep, and also give some of my first impressions of how it can be improved going forwards.


First of all, it certainly achieves the (not difficult) goal of being less verbose and easier for a human to read and write than an ARM template. If you've not seen a Bicep template before, here's the definition for a storage account:

param storageAccountName string
param location string = resourceGroup().location

resource storageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: storageAccountName
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
    tier: 'Standard'

Even better than just a nicer syntax, there is an excellent Visual Studio Code extension for Bicep that gives you syntax highlighting and auto-completion. This makes it very easy to discover the names of the required properties and assign the correct values.

I also really like the fact that Bicep is supported out of the box with the latest Azure CLI (and Azure PowerShell). This means you can directly deploy a Bicep template without the need to transpile it into an ARM template. Not only can it deploy, but it can also decompile an existing ARM template into a Bicep file (with az bicep decompile). So this gives you a great way to migrate existing templates into the newer syntax.

Another feature I like is that Bicep embraces modularity. You can create a Bicep file that defines your preferred way of configuring a storage account, or virtual machine. And then another Bicep template can include that definition, only needing to override parameters it wants to change. This means that over time, as you use Bicep more, you should find you can reuse pre-existing templates rather than needing to build everything up from scratch every time.

It's also nice that in the Bicep GitHub repo, you can find a generous collection of examples which should serve as a decent starting point for most of your needs.


I was able to get an Azure Function App deployed with Bicep fairly easily (despite not knowing about the sample templates which would have saved me some time). However, the getting started experience did highlight some ways in which I think Bicep can still be improved.

First, I'd love to see the Azure Portal able to generate Bicep files instead of ARM templates. And for these to be as terse as possible, rather than the verbose overspecified monstrosities of ARM templates that the Portal currently generates. Even better, I'd love to see a mode for the Azure CLI where instead of actually creating a resource with (say az functionapp create), it emits a simple Bicep template to perform that operation, with the settings you specified parameterized. Since the Azure CLI calls the ARM APIs under the hood, I don't see why that would be too difficult.

Second, most Azure resource types have hundreds of configurable parameters, and if you look at auto-generated ARM templates you'll see values supplied for everything. This can be frustrating when you are working out which properties you do and don't need to include when defining a resource. My general preference is to only specify the properties only where I am deviating from the default or if I want to explicitly call out that a certain setting is important. I don't want my Bicep files cluttered with dozens of settings that I don't really understand but are in there because some other example template had them in. Really what is needed is a single place I can go to for excellent documentation of every property of an Azure resource type, including what its default value is if you don't specify it, and what the permitted values are. That doesn't seem to exist yet (or only in a very limited form).

Third, and this is a bugbear of mine with ARM templates in general, but the names things are called in the templates are often different to the names used in the official documentation. For example, the "consumption" "App Service Plan" is a "dynamic/Y1" "Server Farm" in ARM template-speak. This is unnecessarily confusing for people who don't know the historical names of Azure resources. I don't see why Bicep (or ARM) couldn't be extended to support aliases so that things can be referred to by their most current names.

Fourth, and this is also actually a limitation of ARM templates, but brings some unnecessary complexity into Bicep, and that is that some common operations seem to lack convenience functions. For example to get the connection string for a storage account, rather than there being a method or property I can directly reference, the whole thing has to be constructed with a bunch of string concatenation calling lower-level helpers. I don't see why Bicep (or ARM) couldn't make tasks like this much simpler.

value: 'DefaultEndpointsProtocol=https;AccountName=${};EndpointSuffix=${environment()};AccountKey=${listKeys(, storageAccount.apiVersion).keys[0].value}'


Bicep is an excellent replacement for ARM templates. Anywhere you are using ARM templates, you should consider switching to Bicep. And although it is a huge improvement over ARM templates, I think there is still plenty of scope for Bicep to be made even easier to work with, so I look forward to seeing how the product evolves over the coming years.

Want to learn more about the Azure CLI? Be sure to check out my Pluralsight course Azure CLI: Getting Started.


Comment by Mikhail Shilkov

Thank you for this post!
> Since the Azure CLI uses ARM templates under the hood
I don't think this is true... or I am misunderstanding the claim. AFAIK, the Azure CLI calls Azure REST API directly. There's no deployment resource created for each command.
> a single place I can go to for excellent documentation of every property of an Azure resource
How about the Open API specs? Maybe not excellent, but they are supposed to be the source of truth

Mikhail Shilkov
Comment by Alexander Batishchev

+1. I don't think it uses ARM templates either, rather ARM API.
The difference is that ARM templates call the Deployment API which is built on top of regular API.

Alexander Batishchev
Comment by N.E.R.D.

Holy yuck. Stop vendor lock-in

Comment by Jezza

This was useful, ARM templates are verbose this sounds like Azure's version of a Cloud Formation Template, I hope it comes in YAML as well as JSON.

Comment by Mark Heath

thanks for the clarification, I'll get the post updated. The similarity is indeed that it calls the REST API, but also is idempotent - asking to create something twice doesn't error the second time around which is nice (and not what I expected)
As for the open API specs, maybe they contain enough information for the intellisense help in VS code to be much richer, or for nice human readable doc pages to be auto-generated.

Mark Heath