I’ve never really taken to ARM templates on Azure, they feel fiddly and don’t read overly well. Even an ARM template generated from the Azure Portal is pretty horrific and in my opinion, has been a major negative for Azure for several years. For those reasons, my go-to for Azure Infrastructure as Code has always been Terraform, and although I still prefer the YAML defined CloudFormation templates on AWS, Terraform’s HCL is a pretty close second contender.
So what is Bicep? Microsoft claims:
Bicep is a Domain Specific Language (DSL) for deploying Azure resources declaratively. It aims to drastically simplify the authoring experience with a cleaner syntax, improved type safety, and better support for modularity and code re-use.
From my initial experience, and even though Bicep is still in preview, it is certainly everything they claim it to be. Pair this with the VSCode plugin and it was extremely easy to get started.
Everything you could do in ARM was available in Bicep on day one, which is rare for a tool in preview, but makes sense given Bicep is an abstraction that sits on top of ARM. You can even cross-compile your templates between the two, which is a bonus if you’ve already invested in ARM and are looking to switch.
Next, we’ll create a file called main.bicep which contains code for a simple storage account with set the kind to blob storage (though it could be any kind):
Then we’ll create a resource group to deploy into:
az group create --resource-group my-rg --location australiasoutheast
Now to deploy! I generally prefer to use Complete mode when deploying to make sure I don’t end up with orphaned resources, but depending on the use case you may prefer to use Incremental:
az deployment group create -f ./main.bicep -g my-rg --mode Complete
and like that, we’re done!
Modules are also fully supported, for more complex environments where you shouldn’t be defining everything in a single file, you can easily split your project into multiple files (eg. one for virtual machines, another for vnets etc). To not reinvent the wheel, you can see how this works here.
As mentioned, Bicep files and ARM templates can easily be cross-compiled using the CLI, lets give it a go! The command to convert a bicep file into an ARM template is straight forward.
az bicep build --files main.bicep
The ARM template is 8 lines longer due to JSON syntax (excluding the meta-data and functions keys), or if we want to sound fancy around a 73% increase in length.
Some comparisons on more complex ARM templates, like the Simple Windows Example, proved there are generally savings to be had in the amount of syntax you need to write — and that sweet readability!
OK, let's address the elephant in the room, why would you use Bicep over Terraform? While Bicep’s syntax looks very similar to Terraform’s HCL, I can see a couple of key differences:
- Day-0 support for Azure services
- State is managed by Azure instead of externally
While Microsoft does strive to provide first-class support for Terraform on Azure, there may be cases where third-party tools are inherently delayed in supporting new Azure services or API changes.
State management is something I’m more on the fence about. Those familiar with Terraform will know it uses state files (locally or remote) to give it a “snapshot” of what is deployed to the workspace, even if that snapshot isn’t true. Bicep and ARM both use the resource group to do this, which is often more truthful (especially when using Complete deployment mode) if someone has made manual adjustments to those resources.
On the other hand, in Bicep/ARM I don't like the way subscription level (e.g., resource groups) and resource group level deployments work. I much prefer AWS’ CloudFormation where all you need is access to the account, you can literally deploy everything without any prior manual intervention or the muck around in Bicep/ARM. Especially when it comes to working with more complex project structures involving modules.
Both frameworks support the ability to confirm the changes to be applied, Terraform does default to this which I prefer over Bicep/ARM’s confirm-with-what-if option and would recommend making use of this as much as possible to fully understand what changes will occur.
Given the benefits of day-0 support and the ability to cross-compile from ARM’s JSON files, there is a very low barrier to entry to get your team using it VS changing from ARM to Terraform. If you’re starting a new project where you’ll be defining infrastructure as code and it needs to be Microsoft tools only, I wouldn’t hesitate to start with Bicep over ARM.
I do find some of the limitations around deployment types, nested templates and deployment modes frustrating, and so Terraform wins here if you’re willing to deal with state management yourself.
So should you use it? Yes if your project is simpler, go for it. But for large scale projects, I feel Terraform still has more maturity than Bicep (and ARM for that matter) especially when it comes to modules support and resource cleanup.
Oh, and one last thing. Although Bicep is in preview, it is production-ready as of v0.3 and fully supported by Microsoft Support Plans.