Azure API Management: Gateway Architecture and Policy Design

Kicking off a new run of daily Azure posts, this time going deeper into the services the first series did not cover. First up is the front door of every serious API estate: Azure API Management. APIM is three things in one resource: a gateway that sits in the request path, a control plane for publishing and versioning APIs, and a developer portal for the humans who consume them. Most teams use ten percent of it. Let us use more.

Picking a Tier

The classic tiers (Developer, Basic, Standard, Premium) are being joined by the v2 family (Basic v2, Standard v2, Premium v2) which deploy in minutes instead of half an hour and simplify networking with VNet integration for outbound and private endpoints for inbound. The decision points: Premium tiers buy you multi region gateways, availability zones, and full VNet injection; Standard v2 covers most production APIs that need private backend reachability without the Premium price; Developer is for non production only, no SLA. The consumption tier still exists for serverless burst scenarios but its policy and networking limits rule it out as the estate gateway.

resource "azurerm_api_management" "this" {
  name                = "apim-contoso-prod"
  location            = "westeurope"
  resource_group_name = azurerm_resource_group.api.name
  publisher_name      = "Contoso Platform"
  publisher_email     = "platform@contoso.com"
  sku_name            = "StandardV2_1"

  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_api_management_api" "orders" {
  name                  = "orders-api"
  resource_group_name   = azurerm_resource_group.api.name
  api_management_name   = azurerm_api_management.this.name
  revision              = "1"
  display_name          = "Orders API"
  path                  = "orders"
  protocols             = ["https"]
  service_url           = "https://orders-api.internal.contoso.com"

  import {
    content_format = "openapi+json"
    content_value  = file("${path.module}/specs/orders.openapi.json")
  }
}

Import from OpenAPI specs kept in the repo, never hand build operations in the portal. The spec is the contract, CI validates it, and APIM reflects it.

The Policy Pipeline

Policies are XML documents executed in four sections: inbound before the backend call, backend around it, outbound before the response returns, and on-error when anything throws. They apply at four scopes (global, product, API, operation) with the base element controlling inheritance order. This is where APIM earns its place in the request path:

<policies>
  <inbound>
    <base />
    <validate-jwt header-name="Authorization" failed-validation-httpcode="401">
      <openid-config url="https://login.microsoftonline.com/TENANT/v2.0/.well-known/openid-configuration" />
      <audiences>
        <audience>api://orders</audience>
      </audiences>
      <required-claims>
        <claim name="roles" match="any">
          <value>Orders.Read</value>
        </claim>
      </required-claims>
    </validate-jwt>
    <rate-limit-by-key calls="100" renewal-period="60"
        counter-key="@(context.Request.IpAddress)" />
    <set-header name="X-Request-Id" exists-action="skip">
      <value>@(Guid.NewGuid().ToString())</value>
    </set-header>
  </inbound>
  <backend>
    <base />
  </backend>
  <outbound>
    <base />
    <set-header name="X-Powered-By" exists-action="delete" />
  </outbound>
  <on-error>
    <base />
  </on-error>
</policies>

That single policy validates Entra tokens, enforces role claims, rate limits per client IP, injects correlation IDs, and strips fingerprint headers. Other policies worth knowing: cache-lookup and cache-store for response caching, send-request for token exchange patterns, mock-response for developing against APIs that do not exist yet, and authentication-managed-identity so APIM authenticates to backends with its own identity and your backends can require Entra tokens even from the gateway.

Products, Subscriptions, and Governance

Products bundle APIs with terms: a Bronze product with tight quotas, a Partner product with higher limits and approval required. Subscriptions issue keys per consumer per product, which gives you revocation and per consumer analytics for free. Treat subscription keys as identification, not authentication; the JWT policy above does the real security, keys just tell you who is calling for quota and reporting. Version APIs explicitly (path or header versioning through APIM version sets), keep every environment’s APIM configured from the same repo with APIOps or Terraform, and route diagnostics to Application Insights with sampling tuned, because gateway logs at full fidelity on a busy estate will dominate your ingestion bill, a lesson from the Monitor post that applies double here.

Cheers
Osama

Leave a comment

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