Terraform for Azure API Management End-to-End Configuration

Terraform brings declarative control to Azure API Management, allowing the entire APIM lifecycle from service creation through products, policies, custom domains, and diagnostics to be expressed as code. The approach replaces manual portal changes with versioned, repeatable configuration that can be promoted across dev, test, and production environments.

Introduction

API Management in Azure is a control plane for exposing backend services with governance, security, and observability. When the control plane is built with Terraform, the infrastructure is described once and reproduced anywhere. The configuration covers the supporting resources required by APIM, the service itself with hostname bindings and security settings, and the operational components that make APIs production ready.

Core Terraform Concepts for APIM

A product in APIM is just another resource definition. If you need to create a number of products, then you can use a for-each construct to generate products based on a list, with just one resource declaration. Assigning a policy to a product is also straight forward.

The interesting thing here is that Terraform can remove products from APIM if you change the definition, unlike ARM templates. This drift removal behavior makes Terraform particularly effective for long term API catalog hygiene, where obsolete products can be pruned from code and automatically removed from the live service.

Provider Setup and Prerequisites

Provider versioning is explicit in APIM projects.

provider "azurerm" { version = "=2.1.0" features {} }

The version of AzureRM used in my project is 2.1.0 which is the latest at the moment.

Current guidance for new projects uses a flexible constraint:

terraform { required_version = ">= 1.3.0" required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 4.0" } } }

Prerequisites for building and applying:

  • Azure CLI configured with appropriate permissions
  • Terraform installed version 1.0.0 or later
  • Resource group created
  • Understanding of API management concepts
  • Azure subscription with Contributor access
  • Azure CLI authenticated
  • Azure subscription ID available as a Terraform variable, for example via TFVARsubscription_id

Some patience is required because APIM provisioning takes 30-45 minutes for Developer and Premium tiers.

Provider configuration with subscription variable:

variable "subscription_id" { description = "Azure subscription ID used by the AzureRM provider." type = string } provider "azurerm" { features {} subscription_id = var.subscription_id }

Naming and Project Structure

To have consistent resource naming, we use a couple variables to construct names. Some resources in the definitions need to have an Azure-wide unique name, so the resource names are based on:

  • prefix typically a company abbreviation
  • resource function what is this resource about or for
  • environment dev, test, acc, prd
  • region region where deployed in

This leads for example to the following local variable:

locals { resourceGroupName = "${var.prefix}-${var.resourceFunction}-${var.environment}-${var.region}" }

The resource group name would become for example: didago-apim-dev-we

Consistent naming variables:

apimName = "${var.prefix}-${var.resourceFunction}-${var.environment}-${var.region}" kvName = "${var.prefix}-${var.resourceFunction}-kv-${var.environment}-${var.region}" appInsightsName = "${var.prefix}-${var.resourceFunction}-appinsights-${var.environment}-${var.region}"

Project structure recommended for APIM Terraform:

terraform-azure-apim/ ├── main.tf ├── variables.tf ├── outputs.tf ├── modules/ │ └── apim/ │ ├── main.tf │ ├── variables.tf │ └── outputs.tf └── policies/ ├── global.xml ├── product.xml └── api.xml

Supporting Resources Before APIM

The idea is to use Terraform to setup an entire APIM configuration consisting of the following resources:

  • Storage Account
  • Key vault
  • API Management + custom domain + configuration
  • Application Insights

We need a Storage Account to store the Open API and APIM policy files in. To be able to import an API into APIM via Terraform or ARM, the Open API information must be publicly accessible. Open Api documents can sometimes be exposed on an endpoint by the API itself, but for policy files we need a place to host them.

We need Key vault to store the certificate necessary to setup custom domains. Key Vault will host the certificate which we need for our custom domain and in a later stage Key Vault can also contain API key secrets in case a backend API needs one.

API management is the core resource itself and we'll also add a default API to APIM to serve as heartbeat API for a load balancer.

Application insights will be the tool to store diagnostics information in.

All necessary files can be found in my github repo.

Terraform Resource Group and Storage Account Resources

First of all we need a resource group to store all resources in

Remark on state management:

However that will be created by the Terraform engine itself based on configuration setup in the definition when you perform the init step.

Remark: To simplify execution I've disabled the configuration of the backend, so the tfstate file is created on my local disk and not in a Storage Account. When running this in a DevOps release pipeline, then this definitely must be enabled and configured to use a Storage Account to keep track of the tfstate file.

API Management Service Configuration

Create modules/apim/main.tf

API Management Service

resource "azurerm_api_management" "main" { name = "${var.project_name}-apim" location = var.location resource_group_name = var.resource_group_name publisher_name = var.publisher_name publisher_email = var.publisher_email sku_name = "Premium_1" zones = ["1", "2", "3"] identity { type = "SystemAssigned" } hostname_configuration { proxy { host_name = "api.${var.domain_name}" key_vault_id = azurerm_key_vault_certificate.api.id default_ssl_binding = true negotiate_client_certificate = false } portal { host_name = "portal.${var.domain_name}" key_vault_id = azurerm_key_vault_certificate.portal.id } developer_portal { host_name = "developer.${var.domain_name}" key_vault_id = azurerm_key_vault_certificate.developer.id } } security { enable_backend_ssl30 = false enable_backend_tls10 = false enable_backend_tls11 =

A typical APIM instance definition:

```
resource "azurermresourcegroup" "apim" {
name = "rg-apim-prod"
location = "eastus"
tags = {
Environment = "Production"
}
}

Create the APIM instance

resource "azurermapimanagement" "main" {
name = "apim-prod-001"
location = azurermresourcegroup.apim.location
resourcegroupname = azurermresourcegroup.apim.name
publishername = "My Organization"
publisher
email = "[email protected]"

Tier options: Consumption, Developer, Basic, BasicV2, Standard, StandardV2, Premium, PremiumV2

Developer is good for non-production; Standard or Premium for production

skuname = "Developer1"

Managed identity for accessing backend services securely

identity {
type = "SystemAssigned"
}

Virtual network integration optional

virtualnetworktype = "Internal"

```

Tier selection guidance:

  • Developer is good for non-production; Standard or Premium for production
  • The Developer tier has the same features as Premium but without the SLA. Use it for development and testing, then move to Standard or Premium for production.

Configuration Table

Component Terraform Resource Purpose
Resource Group azurermresourcegroup Holds all APIM related resources
Storage Account azurermstorageaccount Hosts OpenAPI specs and policy files publicly
Key Vault azurermkeyvault Stores certificates for custom domains and secrets
API Management azurermapimanagement Core APIM service with SKU and identity
Application Insights azurermapplicationinsights Stores diagnostics information

Naming Convention Table

Element Example
prefix didago
resource function apim
environment dev
region we
resourceGroupName didago-apim-dev-we
apimName didago-apim-dev-we
kvName didago-apim-kv-dev-we
appInsightsName didago-apim-appinsights-dev-we

Operational Best Practices

Use policies for cross-cutting concerns. Do not implement rate limiting, authentication, or CORS in your backend services when APIM can handle it. This keeps your backends focused on business logic.

Version your APIs. Use APIM's built-in versioning and revision features. Define API versions in Terraform so you can maintain backward compatibility.

Store sensitive values in Key Vault. Never hardcode API keys or secrets in Terraform files or APIM named values. Use Key Vault references.

Monitor everything. The Application Insights integration provides deep visibility into API usage, latency, and errors. Set up alerts for error rate spikes and latency increases.

With Terraform, you declare what you want and let the tool handle the rest.

Conclusion

Azure API Management with Terraform gives you a comprehensive, code-driven approach to API governance. From basic API proxying to advanced scenarios with JWT validation, rate limiting, and response caching, Terraform handles the full APIM configuration lifecycle. The initial setup takes some effort, but the payoff is a reproducible, auditable API platform that scales with your organization's needs.

The combination of declarative product generation via for-each, automatic removal of drifted products, consistent naming built from prefix, function, environment and region, and the supporting storage, key vault, and Application Insights resources creates a complete pipeline for building, promoting, and operating APIs as code. The Developer tier allows rapid iteration without SLA commitments, while Premium with zones and system assigned identity provides production readiness. Storing OpenAPI specs and policies in a public storage account satisfies import requirements, and key vault integration secures custom domain certificates and secrets throughout the lifecycle.

Sources

  1. jeanpaulsmit.com
  2. thecloudpanda.com
  3. oneuptime.com

Related Posts