Azure

Exploring the basics - Choosing the right IaC tool

Exploring the basics - Choosing the right IaC tool

TL;DR Choosing the right IaC tool can be a difficult task. This post will try to explain some of the basic differences of the most common tools.

Link to full size comparison table

Table of Contents

Background

Recently I saw an IaC comparison table made by Fabrice Kurz on LinkedIn. The table listed Terraform, Azure PowerShell, Azure CLI, and ARM Templates as compared options. In a comment below, I mentioned that Azure Bicep should be in this table for a fair comparison with Azure Native IaC. Someone commented that I should post my own table, and this post is the result.

Ansible, Puppet, Chef, and other similar languages are not included here because they are more Configuration as Code (CaC) than Infrastructure as Code. They have some functionality for deploying resources, but are mostly used for managing desired state inside a virtual machine operating system.

Which tools?

I will compare a few different tools:

  • Terraform
  • Azure Bicep
  • Azure CLI and Azure PowerShell
  • Pulumi
  • ARM Templates

The comparison will be done with a few different metrics:

  • Characteristics
  • Stability/Actuality
  • Preview/Planning changes
  • Learning curve
  • Deployment orchestration

Infrastructure as Code

Benefits

There are many advantages to using Infrastructure as Code, hereby referred to only as IaC.

  • Versioning your infrastructure
  • Repeatability from dev to test to production
  • Deployment consistency between workloads
  • Change review for your infrastructure
  • Improved, updated, and automated documentation
  • Shorter implementation times for projects

I will not go into the specifics of IaC benefits, as this would maybe warrant a post of its own. Maybe some other time.

Key concepts

Idempotency - The ability to run the same command several times with the same outcome. Three runs of create storage account with the same parameters should only create one storage account, not three. None of these runs should fail if the storage account already exists, and it should be updated if configuration differs from the code being run.

Declarative means describing the desired state for your infrastructure, but not all steps needed to get there or in what order.

Imperative means you need to take orchestration and dependencies into account when writing your infrastructure. Getting the ordering or dependencies wrong might cause an error in deployment.

Terraform

Back to top

Information

HashiCorp Terraform is an infrastructure as code tool that lets you define both cloud and on-prem resources in human-readable configuration files that you can version, reuse, and share. You can then use a consistent workflow to provision and manage all of your infrastructure throughout its lifecycle. Terraform can manage low-level components like compute, storage, and networking resources, as well as high-level components like DNS entries and SaaS features.

Characteristics

Terraform is idempotent and declarative. It always keeps track of your changes and stores them in a state file. The state file can be kept locally, or you can use remote backends for storing it.

Stability / Actuality

Not long ago Terraform was notoriously known for being a few steps behind ARM Templates or Bicep. All this changed with the AzApi provider which now provides Terraform users with the same access to preview features like ARM or Bicep users. With AzApi I would say that Terraform gets access to new features and functionality at approximately the same time as ARM or Bicep.

Preview / Change planning

As Terraform stores your known state in a state file, it has excellent control of changes made between last deployment and the code you currently have. It will also tell you this in the terraform plan action. Any changes done will be refreshed in to the state file on a few events (terraform plan, terraform apply, and terraform refresh). When you are ready to make a change, use the plan action to get a nice, verbose layout listing all changes necessary. You can also ignore certain properties, which I haven’t seen in the other frameworks.

Learning curve

I would say Terraform is easy to learn, and will have a similar learning experience to Azure Bicep.

Deployment orchestration

Deployment of Terraform resources is very straightforward. You have some Terraform files, and use terraform init, then terraform apply. This will create your infrastructure, given there is no errors in your code. Much deployment logic and orchestration is builtin to the Terraform executable, and it will sort out any dependencies for you implicitly. There are times when implicit dependencies is not enough, then you can use depends_on to explicitly depend on other resources in the current deployment.

Azure Resource Manager Templates (ARM Templates)

Back to top

Information

ARM Templates are JavaScript Object Notation (JSON) files that define the infrastructure and configuration for your project. The template uses declarative syntax, which lets you state what you intend to deploy without having to write the sequence of programming commands to create it. In the template, you specify the resources to deploy and the properties for those resources.

Since ARM Templates are written in JSON, this means they are not very human friendly. JSON was created for machine consumption, and is very verbose. Authoring can be somewhat challenging because of the many curly brackets, colons, and other specifics.

Characteristics

ARM Templates are declarative and idempotent, and you describe the infrastructure using JSON notation in template files. Templates can be nested or linked for modularity and re-use, though this increases the complexity of authoring and template use.

Stability / Actuality

ARM Templates are native to Azure and have immediate support for all preview functionality. Features will be added here for the foreseeable future, as many have invested heavily in using this for their IaC deployments.

Preview / Change planning

ARM Templates have ARM What-If deployment functionality for predicting any changes to your infrastructure. ARM Templates does not use a state file, and as such need to fetch the actual Azure status for each run. This can cause slow planning, and means that there is really no record of how your infrastructure was during the previous deployment. It gives a fair overview of changes needed for infrastructure to match code.

The output of ARM Template What-If deployment looks like this:

Learning curve

Getting to know JSON syntax and writing it might not be everyone’s cup of tea. You should in stead focus on Azure Bicep, which I will write about in the next paragraph. It takes a long time to get used to writing JSON, and you will be struggling with for example with function usage when using functions in functions with concatenation in a string. Syntax becomes hard to read and write, and sometimes you can’t even split the content over more than a single line.

Deployment orchestration

Orchestration in ARM Templates is simple. Put all the resources in an ARM Template and ship it off to the ARM backend to hash out dependencies and order of creation. You don’t need to create complex orchestration yourself, and Microsoft does not recommend it. You need to write some logic for actually deploying the templates, as there is no arm apply or comparative actions like Terraform or Pulumi. Deployment is done on Management Group, Subscription, or Resource Group scope.

Azure Bicep

Back to top

Information

Bicep is a domain-specific language (DSL) that uses declarative syntax to deploy Azure resources. In a Bicep file, you define the infrastructure you want to deploy to Azure, and then use that file throughout the development lifecycle to repeatedly deploy your infrastructure. Your resources are deployed in a consistent manner.

Bicep provides concise syntax, reliable type safety, and support for code reuse. Bicep offers a first-class authoring experience for your infrastructure-as-code solutions in Azure.

Bicep isn’t intended as a general programming language to write applications. A Bicep file declares Azure resources and resource properties, without writing a sequence of programming commands to create resources.

You can use Bicep instead of JSON to develop your Azure Resource Manager templates (ARM templates). The JSON syntax to create an ARM template can be verbose and require complicated expressions. Bicep syntax reduces that complexity and improves the development experience. Bicep is a transparent abstraction over ARM template JSON and doesn’t lose any of the JSON template capabilities. During deployment, the Bicep CLI converts a Bicep file into ARM template JSON.

Characteristics

Idempotent and declarative. Basically this is a simplification of ARM Templates, and has 1:1 feature parity. ARM Templates can be built from Bicep files, and Bicep files can be decompiled to ARM Templates.

Stability / Actuality

Since this is an Azure Native IaC, Bicep will always receive native functionality immediately when available in the ARM API. You can also extend Bicep with Kubernetes manifest files, to deploy K8s workloads and configurations. There will be more extensibility here, and Azure AD has been mentioned more than once.

Preview / Change planning

This experience is identical to ARM What-If deployments, as it is currently the same backend processing it.

Learning curve

Bicep is generally speaking easy to learn and author. The language is less verbose and with a more human friendly syntax. It looks more like YAML than JSON. When compared to the equivalent JSON template, Bicep files are more concise and easier to read. Bicep requires no previous knowledge of programming languages. Bicep syntax is declarative and specifies which resources and resource properties you want to deploy.

Deployment orchestration

Similar to deploying ARM Templates, in that the deployment methods for ARM (Azure CLI, Azure PowerShell) also supports Bicep files without explicit conversion. You need to write some logic for actually deploying the templates, as there is no bicep apply or comparative actions like Terraform or Pulumi. Deployment is done on Management Group, Subscription, or Resource Group scope.

Azure PowerShell

Back to top

Information

Azure PowerShell is a set of cmdlets for managing Azure resources directly from PowerShell. Azure PowerShell is designed to make it easy to learn and get started with, but provides powerful features for automation. The Az PowerShell module is based on the .NET Standard, and works with PowerShell 7.0.6 LTS and PowerShell 7.1.3 or higher on all platforms including Windows, macOS, and Linux. It’s also compatible with Windows PowerShell 5.1. It is recommended to use PowerShell Core, also known as PowerShell or pwsh.

Characteristics

Not idempotent and not declarative. All logic for deployment must be manually scripted by the author. Any error checking must be added in scripts. Any attempts to retry a creation of e.g. a storage account will result in an error stating that the storage account already exists / name not available.

Stability / Actuality

Receives new features almost immediately, but might be prioritized below Azure CLI and Bicep.

Preview / Change planning

No state and no planning by default. All planning must in any case be manually scripted by the author.

Learning curve

If you already know PowerShell you are on your way to do Azure Automation with Azure PowerShell. The syntax is friendly if you already know your way around a PowerShell script. All cmdlets are based on verb-noun naming, and will be familiar for anyone using PowerShell. Help is easily available by running Get-Help Verb-Noun like Get-Help Login-AzAccount or Get-Help New-AzVM.

Deployment orchestration

No builtin orchestration so you will need to script any logic you need when deploying resources with Azure PowerShell.

Azure CLI

Back to top

Information

The Azure Command-line Interface is a cross-platform command-line tool to connect to Azure and execute administrative commands on Azure resources. It allows the execution of commands through a terminal using interactive command-line prompts or a script.

For interactive use, you first launch a shell such as cmd.exe on Windows, or Bash on Linux or macOS, and then issue a command at the shell prompt. To automate repetitive tasks, you assemble the CLI commands into a shell script using the script syntax of your chosen shell, and then you execute the script.

You can install the Azure CLI locally on Linux, Mac, or Windows computers. It can also be used from a browser through the Azure Cloud Shell or run from inside a Docker container.

Characteristics

Idempotent but not really declarative, as in you need to create the resources in the correct order to avoid errors. Attempting to create e.g. a storage account before the resource group exists will result in an error stating resource group cannot be found.

Stability / Actuality

Receives new features almost immediately, but might be prioritized below Bicep and ARM Templates.

Preview / Change planning

No state and no planning by default. All planning must in any case be manually scripted by the author.

Learning curve

Azure CLI is user friendly and can be used in Bash or PowerShell scripts. The output provided from running commands are JSON and can easily be manipulated in PowerShell. The syntax is easy to learn, and is based on az subgroup command like for example az vm create or az group create. Help is easily available by running az –help.

Deployment orchestration

No builtin orchestration so you will need to script any logic you need when deploying resources with Azure CLI.

Pulumi

Back to top

Information

Pulumi is a modern infrastructure as code platform. It leverages existing programming languages - TypeScript, JavaScript, Python, Go, .NET, Java, and markup languages like YAML - and their native ecosystem to interact with cloud resources through the Pulumi SDK. A downloadable CLI, runtime, libraries, and a hosted service work together to deliver a robust way of provisioning, updating, and managing cloud infrastructure.

Pulumi programs, written in general-purpose programming languages, describe how your cloud infrastructure should be composed. To declare new infrastructure in your program, you allocate resource objects whose properties correspond to the desired state of your infrastructure. These properties are also used between resources to handle any necessary dependencies and can be exported outside of the stack, if needed.

Programs reside in a project, which is a directory that contains source code for the program and metadata on how to run the program. After writing your program, you run the Pulumi CLI command pulumi up from within your project directory. This command creates an isolated and configurable instance of your program, known as a stack. Stacks are similar to different deployment environments that you use when testing and rolling out application updates. For instance, you can have distinct development, staging, and production stacks that you create and test against.

Characteristics

Pulumi is declarative and idempotent and you describe the infrastructure using any language supported. The language decides how resources are created, and how you would logically deploy them.

Stability / Actuality

Pulumi does depend on an Azure Native provider, and as such will have access to preview functionality when they are added to the provider. I do not know enough about Pulumi to say anything about how long you need to wait for new features.

Preview / Change planning

Pulumi has the functionality called pulumi preview which lets you describe any necessary changes to make the infrastructure match your code. This plan compares your code with the refreshed stored state. The Pulumi state is stored in a central service as a recommended best practice. You can also store this in any other supported location. Pulumi state service is free, and seems to be an easy choice for beginners.

The output of pulumi preview looks like this:

Learning curve

Since Pulumi is not a language of its own, you would need to learn a new programming language before becoming proficient in this tool. If you already are developing applications in Python, Go, C#, Java, or any other supported language, go ahead and use it. The learning curve might not be so steep in this case. For me personally it would mean learning a new programming language, which puts it far behind the competition.

Deployment orchestration

As far as I can tell, you need to orchestrate much of Pulumi deployment yourself. This example leads me to believe the steps in the python script is processed from the top down. This would be a big drawback if you don’t get any built in dependency mapping. Someone proficient with Pulumi can comment on this if I am mistaken.

Deployment is performed with a simple command pulumi up.

In summary

Now I have listed a few of the tools out there, and it is up to you to choose the right one for you or your organization. Keep in mind that skillset is a crucial part of choosing an IaC tool, and will have an impact on how fast you can adopt the cloud. Remember that the important part is driving adoption and enabling the developers/product teams to do their job efficiently, secure, optimized, and cost controlled.