Terraform Loves Microsoft

Overview

Over the past 12 months I’ve been extremely fortunate to have a number of sessions with the Program Management Team who are responsible for working on the AzureRM Provider the team also look after other tooling surrounding the Azure Portal Experience such as AzCLI. The focus of these discussions has always been how can we make the Azure Terraform experience better.

I’ve found that there are a number conversations that constantly repeat themselves at many locations such as client offices, meetups, conferences or just within corridor conversations at companies I’ve worked at. The conversations are always prefixed with the term Why would I use Terraform If...?

What are the Issues today?

Following the If of that question is always at least one of the following:

  1. I can just use the native clouds tooling?
  2. It doesn’t have zero day support for the API?
  3. I only work on one cloud provider?
  4. I have a whole heap of existing infrastructure already?

Well the good news is the Program Managers at Microsoft have been listening and they have built a few truly interesting pieces of capability that rapidly help us address these questions.

The New Provider - AzAPI

The AzAPI provider is a very thin layer on top of the Azure ARM REST APIs. Use this new provider to authenticate to and manage Azure resources and functionality using the Azure Resource Manager APIs directly.

This provider complements the AzureRM provider by enabling the management of Azure resources that are not yet or may never be supported in the AzureRM provider such as private/public preview services and features.

The key part of the above sentence is complements, this new provider should be used in addition to the standard AzureRM provider not instead of.

Why do we need it?

As many of you would know Terraform Providers are executable binaries that are written in GO, acting as a translation layer over an RPC interface that enables Terraform Core to manage external APIs, such as Azure, AWS, GCP, vSphere etc.

Traditionally with Terraform Providers, each of the objects described by the external APIs are translated through the provider into Resources and Data Sources that can be easily consumed via HCL, it’s one of the main benefits of Terraform, it’s ease of use and extensibility.

However, that has previously meant that before Terraform can harness objects within an external API it needs the translation layer of the provider to have defined and accepted that input. This often translates into a lag between what the external APIs have available for consumption vs. what the Terraform provider has available.

This new provider terraform-provider-azapi solves that problem for Azure.

Microsoft have announced a new suite of tools that support Terraform on Azure and have announced this on their blog this morning.

How?

The new provider enables two resources, that’s correct, only two resources; azapi_resource and azapi_update_resource.

AzApi takes a different approach, one that’s somewhat similar to Bicep acting as a JSON-like translation for the Azure Resource Manager API. AzApi does a similar thing, where it leverages a defined resource to enable a standardised payload response to the Azure Resource Manager API.

By doing this it instantly brings all objects configurable within the Azure Resource Manager API into zero day support of the provider, essentially removing one of the questions that I’m forever asked about!

Q: Why would I use Terraform if it doesn’t have 0 day support for Azure?

A: It does now!

The Resources

azapi_resource

This resource enables the management of any Azure Resource Manager Resource, that support the use of CRUD (Create, Read, Update and Delete). As an example if we were wanting to manage an Azure Automation Account through the AzApi provider, because there was a new preview feature in the base configuration of this resource not available to us within the AzureRM provider, It would look something like the below:

resource "azapi_resource" "automationAccount" {
  type      = "Microsoft.Automation/automationAccounts@2021-06-22"
  name      = "myAccount"
  parent_id = azurerm_resource_group.test.id

  location = azurerm_resource_group.test.location
  body = jsonencode({
    properties = {
      disableLocalAuth    = true
      publicNetworkAccess = false
      sku = {
        name = "Basic"
      }
    }
  })
}

As you can see from the example above, there is a large amount of familiarity with our standard Terraform configurations, however you will see elements such as type, parent_id and body which are points of different to the AzureRM look and feel.

type - Will be nothing new to those who have lived in the ARM Template world. The value for this field is the resource-type and the api-version, following the convention: <resource-type>@<api-version> as an example Microsoft.Storage/storageAccounts@2021-08-01

parent_id - Is the Id of the logical group the resource or item is deployed within, such as a resource group’s id. As an example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test

body - Is the JSON objected that contains the request body used to either create or update the Azure resource in question.

azapi_update_resource

The azapi_update_resource is specifically used to manage a subset of existing Azure Resource Manager resource’s properties, working only with the Read and Update operations. Essentially the purpose of this resource is to enable your to add or modify the properties of an existing resource.

It is important to note that this resource doesn’t support the Delete operation, so if you simply delete the resource the properties will remain unchanged. So to revert properties you must set them back to what you previously had before deleting the configuration. This should be front of mind when looking to use this resource, as those who are unaware of this may get caught out.

In the below example we have a good use case for this resource, we want to update the idleTimeoutInMinutes value of an Azure Load Balancer, however, that value isn’t exposed via the AzureRM provider currently. So we leverage the AzureRM provider to create the resource and set all the support properties and then configure just what is needed with the azapi_update_resource resource.

resource "azurerm_lb" "example" {
  name                = "example-lb"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name

  frontend_ip_configuration {
    name                 = "PublicIPAddress"
    public_ip_address_id = azurerm_public_ip.example.id
  }
}

resource "azurerm_lb_nat_rule" "example" {
  resource_group_name            = azurerm_resource_group.example.name
  loadbalancer_id                = azurerm_lb.example.id
  name                           = "RDPAccess"
  protocol                       = "Tcp"
  frontend_port                  = 3389
  backend_port                   = 3389
  frontend_ip_configuration_name = "PublicIPAddress"
}

resource "azapi_update_resource" "example" {
  type        = "Microsoft.Network/loadBalancers@2021-03-01"
  resource_id = azurerm_lb.example.id

  body = jsonencode({
    properties = {
      inboundNatRules = [
        {
          properties = {
            idleTimeoutInMinutes = 15
          }
        }
      ]
    }
  })

  depends_on = [
    azurerm_lb_nat_rule.example,
  ]
}

When should I use it?

As you may have detected through this post, whilst I am excited about the use of the AzApi provider, it is important that it is used as a complimentary addition to the AzureRM provider, it is not a direct replacement.

This is so that you can maximise the use of Terraform’s benefits. The declarative nature of Terraform means the value of the documentation and example modules are some of the strongest assets available to this, and because of the nature of the AzApi provider, this is lost.

Final Thoughts

In short it is a great additional capability to have when building infrastructure on Azure, but it doesn’t change the way in which we should do so with Terraform. Now that we have zero day support for all aspects of Azure there isn’t anything we can’t Terraform!