The AzAPI Terraform Provider for deploying and managing Microsoft Azure resources is a lightweight, “thin layer” provider built on top of the Azure ARM (Azure Resource Manager) REST APIs. This enables the management of any Microsoft Azure resource using any Azure ARM API version. The primary benefit of the AzAPI Terraform Provider is that it can be used to deploy and manage new Azure resources and resource properties that aren’t yet supported by the azurerm Terraform Provider.



Use AzAPI Terraform Provider to managed Azure Preview Resources and Properties

The AzAPI Terraform Provider supports only couple different resource in Terraform. This is not an expansive list of different Microsoft Azure resources, since the intention of this provider is to enable management of resources not yet supported by the azurerm Terraform Provider, including Preview Properties of the Azure Resources that are supported by the azurerm provider.

For this simplistic, yet powerful, reason for the AzAPI Provider, this provider only supported these two Terraform resources:

  • azapi_resource – Used for managing Azure Resources that are not yet supported by the azurerm Terraform Provider.
  • azapi_update_resource – Used for managing Preview Properties of Azure Resources that are not yet supported by the azurerm Terraform Provider.

Essentially, you’ll want to generally use the azurerm Terraform Provider when managing Azure Resources using Terraform Infrastructure as Code projects. However, there are times when that provider does not yet support a specific Azure Resource or property needs to be managed from Terraform. In these special cases, the AzAPI Terraform Provider provides the capabilities to manage these resources and properties from Terraform.

The way the AzAPI Terraform Provider works is it is built as a thin layer on top of the Azure ARM (Azure Resource Management) REST APIs. This enables AzAPI to be able to manage any Azure Resource as long as the Azure Resource Type symbolic name and API Version are specified.

When setting the value of the type attribute on either of the azapi_resource or azapi_update_resource resources, the format for the expected value is as follows:

<azure-resource-type>@<api-version>

The placeholders in the previous Azure Resource symbolic type and API version definition will be replace by the following values to specify a particular Azure Resource:

  • azure-resource-type – This is the symbolic name of the Azure ARM Resource Type to be managed.
  • api-version – This is the Azure ARM REST API Version for the specific Azure Resource Type specified. This will need to be a supported API Version for the specific Azure Resource Type that’s being targeted with the AzAPI provider.

The following is a full Azure Resource symbolic type with API version to help add clarity on specifying the necessary values on managing resources, like the Custom IP Prefixes resource in Azure:

Microsoft.Network/customIpPrefixes@2021-03-01

On a related note, the symbolic name of the Azure ARM Resource Type and API Version is similar what Azure Bicep uses to support managing any Azure Resource using that set of IaC tooling for Microsoft Azure.

azapi_resource Resource Definition

The azapi_resource resources is used for managing Azure Resources that are not yet supported by the azurerm Terraform Provider. Without a specific Terraform resource defined in the AzAPI provider, the azapi_resource resource is used with the type attribute set to the Azure ARM Resource Type name and API Version to target within the Azure REST API.

The following is an example targeting the Azure Custom IP Prefixes resource type (Microsoft.Network/customIpPrefixes) using the 2021-03-01 API Version:

resource "azapi_resource" "build5nines_publicip" {
  type      = "Microsoft.Network/customIpPrefixes@2021-03-01"
  name      = "build5nines_publicip"
  parent_id = azurerm_resource_group.example.id
  location  = "northcentralus"

  body = jsonencode({
    properties = {
      cidr          = "10.0.0.0/24"
      signedMessage = "Sample Message for WAN"
    }
  })
}

In the previous example, there are a few additional attributes configured on the azapi_resource resource to configure the necessary properties on the Azure Resource being managed. The definition of these additional attributes are as follows:

  • name – The name of the Azure Resource
  • parent_id – The Azure Resource ID for the Azure Resource that will be the Parent of this Azure Resource. The previous example shows setting this to the Resource ID of an Azure Resource Group to place the resource in.
  • location – This is the Azure Region / Location to provision the resource within.
  • body – This is set to a JSON encoding of all the required properties to configure on the Azure Resource being managed.

The body attribute is set using a Terraform object that contains all the necessary properties and values needed for the resource. Then, the Terraform jsonencode() function is used to encode that Terraform object into a JSON string that is then passed to the asapi_resource resources body property. This JSON string is what gets passed as the body of the Azure ARM REST API request made to manage the Azure Resource.

The following is an additional example of defining the azapi_resource Terraform resource from the AzAPI provider. This time to manage a Microsoft.MachineLearningServices/workspaces/compute Azure Resource.

resource "azapi_resource" "build5nines_machinelearning_compute" {
  name = "build5nines_ml_compute"
  parent_id = data.azurerm_machine_learning_workspace.existing.id
  type = "Microsoft.MachineLearningServices/workspaces/computes@2021-07-01"

  location = "eastus"
  body = jsonencode({
    properties = {
      computeType      = "ComputeInstance"
      disableLocalAuth = true
      properties = {
        vmSize = "STANDARD_NC6"
      }
    }
  })
}

azapi_update_resource Resource Definition

The azapi_update_resource resource is used for managing Preview Properties of Azure Resources that are not yet supported by the azurerm Terraform Provider. Without a specific Terraform resource defined in the AzAPI provider, the azapi_update_resource resource is used with the type attribute set to the Azure ARM Resource Type name and API Version to target within the Azure REST API.

The following is an example targeting the the Azure Container Registry resource type (Microsoft.ContainerRegistry/registries) using the 2020-11-01-preview API Version to set the anonymousPullEnabled Preview Feature:

resource "azapi_update_resource" "test" {
  type        = "Microsoft.ContainerRegistry/registries@2020-11-01-preview"
  resource_id = azurerm_container_registry.acr.id

  body = jsonencode({
    properties = {
      anonymousPullEnabled = var.bool_anonymous_pull
    }
  })
}

NOTE: The anonymousPullEnabled Preview Property of the Azure Container Registry resource is likely no longer in Preview, but this example still represents the method for how the azapi_update_resource resource is used to manage Preview Properties within Microsoft Azure.

In the previous example, there are a few additional attributes configured on the azapi_update_resource resource to configure the necessary properties on the targeted Azure Resource. The definition of these additional attributes are as follows:

  • resource_id – This is set to the Azure Resource ID for the Azure Resource targeted to update the properties of.
  • body – This is set to a JSON encoding of all the required properties to configure on the Azure Resource being managed.

The body attribute is set in the same fashion on the azapi_update_resource resource to the azapi_resource resource. This enables the necessary Azure Resource Preview Properties to be defined and set to the necessary values to manage them using the AzAPI Terraform Provider.


AzAPI Provider Definition

The AzAPI Terraform Provider is defined in a Terraform Project using the provider block just like any other Terraform Provider. This is done similar to defining the azurerm Terraform Provider

# Using required_providers is recommended best practice
# when defining Terraform Providers on a project.
terraform {
  required_providers {
    azapi = {
      source = "azure/azapi"
    }
  }
}

provider "azapi" {
  # subscription_id = "..."
  # client_id       = "..."
  # client_secret   = "..."
  # tenant_id       = "..."
}

There are several attributes supported for use on the provider block when using the AzAPI Terraform Provider to define the Azure Service Principal or Managed Identity to use when authenticating with Microsoft Azure. These are very similar to the attributes supported by the azurerm Provider too.

  • client_id – (Optional) Client ID to use.
  • subscription_id – (Optional) Subscription ID for the Azure Subscription to use.
  • tenant_id – (Optional) The Tenant ID of the Azure AD Tenant to authenticate against.
  • client_secret – (Optional) The Client Secret to use when authenticating with Service Principal.

Also, the AzAPI and azurerm Terraform Providers can be used together in the same Terraform Project to enable all the required Azure resources to be deployed and managed.


Authenticate AzAPI Provider with Azure

Authenticating the AzAPI Terraform Provider against an Azure Subscription is performed the same way as using the azurerm Terraform Provider. The easiest way to authenticate the AzAPI provider is to use the Azure CLI to login to an Azure Subscription, then the AzAPI Terraform Provider will automatically use the account logged in with to authenticate itself against the Azure Subscription when managing resources.

Overall there are three methods that can be used to authenticate the AzAPI provider against Microsoft Azure. The following list shows the different methods supported:

  • Login using Azure CLI (az login) and set the Azure Subscription (az account set) to the necessary Azure Subscription.
  • Create a Service Principal within Azure AD with permissions on the Azure Subscription necessary for the provider to manage resources.
    • Pass Service Principal to Terraform using Environment Variables
    • Pass Service Principal to Terraform using provider block

Login using Azure CLI

The Azure CLI can be used to authenticate with Microsoft Azure, then the AzAPI Terraform Provider will be able to use those credentials to manage Azure Resources. Logging in to Azure is done using the az login command, and then the az account set command is used to explicitly tell the Azure CLI which Azure Subscription to target subsequent resources commands towards. This is important to specify when the logged in account has access to multiple Azure Subscriptions.e

az login
az account set --subscription "<azure-subscription-id|azure-subscription-name>"

When using the az account set command, the --subscription argument is used to specify the Azure Subscription to target. This argument accepts either the GUID that is the Azure Subscription ID, or the text name of the Azure Subscription. Since Azure Subscriptions can be renamed but their GUID will never change, it’s best practice to use the Azure Subscription ID in scripts and workflow pipelines so they don’t break if the subscription name is changed at some point in the future.

Pass Service Principal Credentials using Environment Variables

The credential information for the Service Principal to authenticate against Microsoft Azure can be specified using Environment Variables. This works the same in different environments. The AzAPI provider will automatically pull in the values from Environment Variables if they are named as follows:

  • ARM_SUBSCRIPTION_ID – This needs to be set to the Azure Subscription ID (guid).
  • ARM_TENANT_ID – This will be the Azure Subscription Tenant ID (guid), or otherwise known as the Azure Active Directory ID.
  • ARM_CLIENT_ID – This needs to be the “Client ID” or “App ID” for the Service Principal that was created in Azure AD.
  • ARM_CLIENT_SECRET – This needs to be the “Client Secret” (aka Password) for the Service Principal that was created in Azure AD.

The following are examples of setting the environment variables using both bash and PowerShell:

# Use Bash to set Environment Variables
export ARM_SUBSCRIPTION_ID="<azure-subscription-id>"
export ARM_TENANT_ID="<azure-ad-tenant-id>"
export ARM_CLIENT_ID="<service-principal-app-id>"
export ARM_CLIENT_SECRET="<service-principal-secret>"

# Use PowerShell to set Environment Variables
$env:ARM_SUBSCRIPTION_ID="<<azure-subscription-id>"
$env:ARM_TENANT_ID="<azure-ad-tenant-id>"
$env:ARM_CLIENT_ID="<service-principal-app-id>"
$env:ARM_CLIENT_SECRET="<service-principal-secret>"

Once the Environment Variables are set with the necessary values for the Azure Subscription and Service Principal to target, then the AzAPI provider will automatically pull those values in without needing to add additional configurations to the provider block within the Terraform Project.

Pass Service Principal Credentials using provider block

The AzAPI provider also supports the ability to configure the provider block with attributes to set the necessary values to authenticate with the Azure Subscription and Service Principal. This can be done using the Terraform provider block as follows:

# We strongly recommend using the required_providers block to set the
# Azure Provider source and version being used
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=3.0.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}

  subscription_id = "00000000-0000-0000-0000-000000000000"
  tenant_id       = "00000000-0000-0000-0000-000000000000"
  client_id       = "00000000-0000-0000-0000-000000000000"
  client_secret   = "xxxxxxxxxxxxxxxxxxxx"
}

While it’s not recommended to hard code the “Client Secret” for the Service Principal, you could hard code the other values if desired. If the organization has a different Azure Subscriptions for each environment being deployed within, then it may not make sense to hard code these values at all. In this case, provider block is able to be configured to pull these values from input variables or event custom environment variables instead.

Input variables can be defined on the Terraform Project, and then those variables can be used to set the provider attributes to set values like the Azure Subscription ID or Client ID and Secret to use for authenticating with Microsoft Azure. This is defined in the same syntax as using any other input variables in Terraform.

The following is an example showing the Terraform code for adding a input variable to the project and setting one of the attributes on the AzAPI provider:

variable "azure_subscription_id" {
  type = string
}

provider "azurerm" {
  features {}

  subscription_id = var.azure_subscription_id
  # other attributes set similarly
}

Custom Environment Variables can be used in a similar fashion to using input variables to set the AzAPI provider attributes to authenticate with Azure. The Environment Variables must be set before running Terraform commands. In the Terraform code, on the provider block for the AzAPI provider, the custom Environment Variables can be accessed using the env. prefix, in a similar fashion to the var. prefix for input variables, using the name of the Environment Variable.

The following is are examples of setting a custom Environment Variable using both bash and PowerShell:

# Use Bash to set Environment Variables
export CUSTOM_AZURE_SUBSCRIPTION_ID="<azure-subscription-id>"

# Use PowerShell to set Environment Variables
$env:CUSTOM_AZURE_SUBSCRIPTION_ID="<<azure-subscription-id>"

After the custom Environment Variables are created, the Terraform provider block for the AzAPI provider can reference them as follows:

provider "azurerm" {
  features {}

  subscription_id = env.CUSTOM_AZURE_SUBSCRIPTION_ID
  # other attributes set similarly
}
Microsoft MVP

Chris Pietschmann is a Microsoft MVP (Azure & IoT) and HashiCorp Ambassador (2021) with 20+ years of experience designing and building Cloud & Enterprise systems. He has worked with companies of all sizes from startups to Fortune 100. He is also a Microsoft Certified Azure Solutions Architect and developer, a Microsoft Certified Trainer (MCT), and Cloud Advocate. He has a passion for technology and sharing what he learns with others to help enable them to learn faster and be more productive.