Azure: Service API already exists

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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.