JumalaSika ltd - Fotolia

Tip

Compare ARM templates vs. Terraform for infrastructure as code

ARM templates and Terraform are popular infrastructure-as-code options. Check out an Ubuntu Server VM example in each approach and learn the pros and cons of these tools.

Infrastructure as code is the practice by which IT admins document the infrastructure configuration for an application or service in a template. Although the concept is far from new, tools have improved dramatically in quality, especially for use with cloud providers like Microsoft Azure.

Two of the most popular infrastructure-as-code tools to use with Azure are the native Azure Resource Manager (ARM) templates and HashiCorp Terraform. Azure users should compare ARM templates vs. the Terraform approach to decide which product is the better fit for their environment. Follow along with these examples of ARM and Terraform, then explore pros and cons that come with these infrastructure-as-code (sometimes called IaC) tools.

Prerequisites

To get started evaluating ARM templates and Terraform, install the following on your machine:

  • Microsoft Visual Studio Code and the Azure Resource Manager Template Extension for Visual Studio Code;
  • Terraform and Azure PowerShell -- the Azure Cloud Shell interactive shell has both pre-installed; and
  • A Microsoft Azure account.

Azure Resource Manager templates

ARM templates are JSON documents that define each aspect of the IT infrastructure. For example, a compute resource for a new VM includes its size, credentials, version and attached storage all defined in the same JSON file.

Below is an example ARM template for an Ubuntu Server VM:

# JSON

...
{
  "name": "ubuntuVM1",
  "type": "Microsoft.Compute/virtualMachines",
  "apiVersion": "2019-07-01",
  "location": "[resourceGroup().location]",
  "properties": {
    "hardwareProfile": {
      "vmSize": "Standard_A2_v2"
    },
    "osProfile": {
      "computerName": "ubuntuVM1",
      "adminUsername": "adminUsername",
      "adminPassword": "adminPassword123"
    },
    "storageProfile": {
      "imageReference": {
        "publisher": "Canonical",
        "offer": "UbuntuServer",
        "sku": "16.04-LTS",
        "version": "latest"
      },
      "osDisk": {
        "name": "ubuntuVM1-OSDisk",
        "caching": "ReadWrite",
        "createOption": "FromImage"
      }
    },
...

IT admins who are familiar with JSON formatting will find this template input familiar: Microsoft has a well-defined schema that enables easy ARM template creation in Visual Studio Code. Open a new JSON document and type arm and you can generate a new blank template to which you can add resources.

Create an ARM template JSON document in Visual Studio Code.
Figure 1. Add 'arm' to a JSON document in the Visual Studio Code terminal.

These infrastructure-as-code documents can get incredibly long and verbose -- but this is necessary to configure every detail of the infrastructure. The code sample above for the single Ubuntu VM and its supporting components comes from a JSON file that is 167 lines long.

IT admins deploy these configurations from an authenticated Azure CLI or Azure PowerShell session. This article's examples use Azure PowerShell. Run the New-AzResourceGroupDeployment cmdlet in the template file once it, and a resource group, have been deployed.

# Azure PowerShell

New-AzResourceGroupDeployment -Name demodeployment -ResourceGroupName ata-rg -TemplateFile ./az_templates/template.json -Verbose

Provided the template is valid, any defined resources deploy into that resource group when the cmdlet runs. If one of the defined resources gets destroyed, redeploy the template file -- without making changes -- to restore it.

Run the cmdlet to deploy resources into a defined resource group.
Figure 2. Run the cmdlet to deploy resources into a defined resource group.

HashiCorp Terraform

Terraform is a popular tool with DevOps practitioners because it can enforce configurations on various cloud platforms, such as Azure, AWS and Google Cloud Platform, but there are also community and experimental providers for PostgreSQL, VMware and even Active Directory.

Terraform is a multi-cloud product. Admins who know how to create infrastructure in a given provider can write Terraform resources for it. The process is relatively similar across cloud platforms.

Typically, Terraform configurations are written in HashiCorp's proprietary, declarative language called HashiCorp Configuration Language (HCL). Consequently, Terraform's learning curve is higher than that for ARM templates, which use the more common data formatting approach of JSON.

An example illustrates the difference in infrastructure code between ARM templates' JSON files and Terraform's HCL structure. The Terraform code below creates the same VM deployment that we made in the ARM template:

# HCL

resource "azurerm_linux_virtual_machine" "example" {
  name                            = "ubuntuVM1"
  resource_group_name             = azurerm_resource_group.example.name
  location                        = azurerm_resource_group.example.location
  size                            = "Standard_F2"
  admin_username                  = "adminUsername"
  admin_password                  = "adminPassword123"
  disable_password_authentication = false

  network_interface_ids = [
    azurerm_network_interface.example.id,
  ]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "16.04-LTS"
    version   = "latest"
  }
}

ARM templates are all created in one file. By contrast, Terraform evaluates every resource in the directory with a .tf extension -- and will do so for every command that runs the resource, which enables IT admins to break up resources into separate files to ease parsing. For example, an IT admin can write infrastructure as code into three files -- all of which must have a.tf file extension:

  • subnets.tf
  • security_groups.tf
  • virtual_machines.tf

IT admins can authenticate the Azure Terraform provider with the CLI or a Service Principal, which is an authentication application within Azure Active Directory. Once you set up the authentication, execute Terraform code with the init command, followed by terraform apply. Terraform should return the following output:

➜  ~ terraform apply
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_linux_virtual_machine.example will be created
  + resource "azurerm_linux_virtual_machine" "example" {
      + admin_password                  = (sensitive value)
      + admin_username                  = "adminUsername"
      + allow_extension_operations      = true
      + computer_name                   = (known after apply)
      + disable_password_authentication = false
      + id                              = (known after apply)
      + location                        = "eastus"
      + max_bid_price                   = -1
      + name                            = "ubuntuVM1"
      + network_interface_ids           = (known after apply)
      + priority                        = "Regular"
      + private_ip_address              = (known after apply)
      + private_ip_addresses            = (known after apply)
      + provision_vm_agent              = true
      + public_ip_address               = (known after apply)
      + public_ip_addresses             = (known after apply)
      + resource_group_name             = "ata-rg"
      + size                            = "Standard_A2_v2"
      + virtual_machine_id              = (known after apply)
      + zone                            = (known after apply)

      + os_disk {
          + caching                   = "ReadWrite"
          + disk_size_gb              = (known after apply)
          + name                      = (known after apply)
          + storage_account_type      = "Standard_LRS"
          + write_accelerator_enabled = false
        }

      + source_image_reference {
          + offer     = "UbuntuServer"
          + publisher = "Canonical"
          + sku       = "16.04-LTS"
          + version   = "latest"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:

Enter yes to deploy the resources from the desired configuration into the Azure subscription to which you've authenticated. Similar to ARM templates, Terraform restores an altered configuration to its desired state if you rerun the terraform apply command. This capability is useful in times when someone deletes a resource file manually or puts the system out of configuration. You can see the output from the terraform apply command in Figure 3.

Deploy resources to the authenticated Azure subscription.
Figure 3. Deploy the configuration-defined resources to the authenticated Azure subscription.

To tear down all the resources, run terraform destroy and enter yes -- just like the terraform apply command -- to remove any resources that Terraform created.

Why infrastructure as code?

It's a lot of work to write IT deployment requirements and steps as a formatted code document, and to learn how to use an IaC tool. But the benefits are numerous.

IT admins can store infrastructure code alongside app source code in version control. The IT admin can track each change to the infrastructure, reduce the size of every deployment and recreate each environment from scratch consistently.

Other teams in an organization, such as information security, benefit from infrastructure as code too. They can find new vulnerabilities and identify affected servers without scanning every server and container in the organization's IT ecosystem, for example.

ARM templates vs Terraform: pros and cons

There are some significant benefits to defining infrastructure as code. There are also downsides, but they can be inconsequential depending on the use case and requirements.

As these examples of ARM and Terraform show, the same infrastructure deployment looks different depending on the tool in use. To choose a tool, consider the product roadmaps and features. For example, ARM templates work with the latest features from Azure as soon as Microsoft releases them, because ARM is a native product for the cloud platform. Terraform, on the other hand, is open source. A Terraform community member must add any new Azure features to the Azure Terraform provider.

ARM templates' primary downside is that they are restricted to Azure cloud deployments. However, while it takes a significant amount of work to write the same infrastructure in a different provider and achieve identical results, it is possible to do it in the same tool. By contrast, Terraform users can deploy the same setup to Azure, AWS, on-premises servers and other locations by design.

ARM templates vs. Terraform
ARM templates vs. Terraform

Editor's note: Azure Arc, in preview at the time of publication, extends Azure Resource Manager capabilities to other infrastructure on premises and on other clouds.

ARM templates and Terraform provide different methods for variables, conditions and internal logic. IT admins should invest time in training to understand how the tool works and maximize its usefulness. Whichever tool your organization adopts, IaC provides return on investment almost immediately.

Conversion to infrastructure as code, rather than manual provisioning and maintenance of IT systems, is a simple task for well-defined architectures. If the deployment architecture is not well defined, then the move to Terraform or ARM templates forces your organization to reevaluate segments of its applications in production -- which makes it a great opportunity to review existing architecture.

Next Steps

Compare Azure Bicep vs Terraform for Provisioning Infrastructure

How to use Pulumi Automation API, with examples

Follow this tutorial to get started with Azure Bicep

3 actions to optimize infrastructure as code initiatives

Dig Deeper on Systems automation and orchestration