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:
- I can just use the native clouds tooling?
- It doesn’t have zero day support for the API?
- I only work on one cloud provider?
- 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!