Update 2023/01/04: I was wrong. Microsoft Support pointed out that each external APIM instances is assigned a globally unique DNS entry and mine was just too generic. Giving the API management resource a unique name for the region it runs in (eg. by appending a random number) solves the issue.
I’m writing a Terraform template that sets up an Azure API management [1] instance. The declaration is rather simple, according to Terraform’s documentation [2]:
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "West Europe"
}
resource "azurerm_api_management" "example" {
name = "example-apim"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
publisher_name = "My Company"
publisher_email = "company@terraform.io"
sku_name = "Developer_1"
}
At the time of writing, the Terraform CLI version is 1.3.6 and azurerm provider version is 2.99.0. When I apply the template, it fails with the error “Service API already exists”. The debug logs show that Terraform first queries Azure RM for the API management instance, doesn’t find it and then tries to create it which fails with an HTTP 409 “conflict” that the API management already exists.
The internet’s vast wisdom says that if a recently deleted API management instance had the same name (as is often the case with automated deployments), said instance may be in the “soft deleted” state for up to 48 hours, which would prevent another resource with the same name to be created. But this wasn’t the case here.
The deployment works when an dummy API is deployed alongside with the API management – I don’t know why this happens, nor could I find any documentation on that. The example below is borrowed, unfortunately I couldn’t find out the author or original publication source. If anyone knows the source, please let me know:
resource "azurerm_api_management" "apim_service" {
name = "workload-example--apim-service"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
publisher_name = "Publisher"
publisher_email = "publisher@xeample.com"
sku_name = "Developer_1"
virtual_network_type = "External"
virtual_network_configuration {
subnet_id = data.azurerm_subnet.default.id
}
policy {
xml_content = <<XML
<policies>
<inbound />
<backend />
<outbound />
<on-error />
</policies>
XML
}
}
resource "azurerm_api_management_api" "api" {
name = "workload-example-api"
resource_group_name = azurerm_resource_group.rg.name
api_management_name = azurerm_api_management.apim_service.name
revision = "1"
display_name = "workload-example-api"
path = "example"
protocols = ["https", "http"]
description = "An example API"
import {
content_format = "openapi"
content_value = <<TEXT
openapi: 3.0.0
info:
title: customer
version: '1.0'
servers:
- url: 'https://api.example.com'
paths:
'/customers/{customer_id}':
parameters:
- schema:
type: integer
name: customer_id
in: path
required: true
get:
summary: customer
tags: []
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
customer_id:
type: integer
customer_name:
type: string
operationId: get-customers-customer_id
description: Retrieve a specific customer by ID
components:
schemas: {}
TEXT
}
}
resource "azurerm_api_management_product" "product" {
product_id = "workload-example-product"
resource_group_name = azurerm_resource_group.rg.name
api_management_name = azurerm_api_management.apim_service.name
display_name = "workload-example-product"
subscription_required = true
approval_required = false
published = true
description = "An example Product"
}
resource "azurerm_api_management_group" "group" {
name = "workload-example-group"
resource_group_name = azurerm_resource_group.rg.name
api_management_name = azurerm_api_management.apim_service.name
display_name = "workload-example-group"
description = "An example group"
}
resource "azurerm_api_management_product_api" "product_api" {
resource_group_name = azurerm_resource_group.rg.name
api_management_name = azurerm_api_management.apim_service.name
product_id = azurerm_api_management_product.product.product_id
api_name = azurerm_api_management_api.api.name
}
resource "azurerm_api_management_product_group" "product_group" {
resource_group_name = azurerm_resource_group.rg.name
api_management_name = azurerm_api_management.apim_service.name
product_id = azurerm_api_management_product.product.product_id
group_name = azurerm_api_management_group.group.name
}
References
Azure API management
https://azure.microsoft.com/en-us/products/api-management/
Terraform Azure API management resource documentation
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management
Azure API management soft deleted state
https://learn.microsoft.com/en-us/cli/azure/apim/deletedservice?view=azure-cli-latest