Goal: Publish/secure/monitor three backend APIs behind APIM for ABC Inc:

Features included:

1 — High-level architecture (textual diagram)

Clients (Mobile, Web, Partner Apps)
Azure API Management Gateway (APIM) (Public / Private + optional Self-Hosted)
→ Backend Services: Billing (App Service), Meter (Functions), Account (AKS)

Supporting services:

2 — Create APIM (Portal, CLI or Bicep)

Important choices

Quick Azure CLI (creates APIM in basic form — replace placeholders)

RG="aw-rg"
LOCATION="eastus"
APIM_NAME="aw-apim-prod"
PUBLISHER_EMAIL="[email protected]"
PUBLISHER_NAME="ABC Inc"

az group create -n $RG -l $LOCATION

az apim create \
  --name $APIM_NAME \
  --resource-group $RG \
  --location $LOCATION \
  --publisher-email $PUBLISHER_EMAIL \
  --publisher-name "$PUBLISHER_NAME" \
  --sku-name Developer \
  --capacity 1

For Premium use --sku-name Premium and consider --virtual-network-type Internal to connect to VNet.

Bicep (skeleton) — APIM instance (useful for CI/CD)

param apimName string = 'aw-apim-prod'
param location string = resourceGroup().location
param publisherEmail string
param publisherName string

resource apim 'Microsoft.ApiManagement/service@2023-01-01' = {
  name: apimName
  location: location
  sku: {
    name: 'Premium' // or Developer/Standard
    capacity: 1
  }
  properties: {
    publisherEmail: publisherEmail
    publisherName: publisherName
    // virtualNetworkType: 'External' | 'Internal' // if Premium + VNet
  }
}

3 — Add APIs (Portal steps + CLI)

Portal (easiest):

  1. Open APIM → APIs → Add API

  2. Choose:

    • OpenAPI to import swagger of Billing API (recommended)

    • HTTP to define manually

  3. Set Frontend endpoint (path) e.g., /billing and Backend to App Service URL.

CLI (import OpenAPI)

az apim api import --resource-group $RG --service-name $APIM_NAME \
  --path billing --api-id billing-api \
  --specification-format OpenApi --specification-url "https://billing.azurewebsites.net/swagger/v1/swagger.json"

4 — Bind custom domain & TLS (optional but recommended)

Set up custom DNS & TLS cert (from Key Vault or App Service cert), then configure Custom domains in APIM (Management → Custom domains). For production use custom domain + certificate in Key Vault and APIM with managed identity to access certificate.

5 — Secure APIs: Subscription keys vs OAuth2/JWT

Two common approaches:

Configure OAuth 2.0: (Portal)

  1. APIM → Security → OAuth 2.0 + OpenID Connect

  2. Add an OpenID Connect provider:

    • Name: AzureADB2C

    • Metadata endpoint: https://<tenant>.b2clogin.com/<tenant>.onmicrosoft.com/<policy>/v2.0/.well-known/openid-configuration

  3. In each API operation, add an authorization requirement or apply a policy to validate JWT.

6 — Key APIM Policies (inbound / backend / outbound)

Policies are XML fragment attached at API, operation or product level.

Common policy snippets (copy-paste into APIM policy editor)

A. Validate JWT (Azure AD / B2C)

<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized">
  <openid-config url="https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/v2.0/.well-known/openid-configuration" />
  <required-claims>
    <claim name="aud">
      <value>{api-client-id}</value>
    </claim>
  </required-claims>
</validate-jwt>

Place in <inbound>.

B. Subscription key check (optional — APIM enforces by default when product requires subscription)

APIM will check Ocp-Apim-Subscription-Key if the product requires subscription. No explicit policy needed unless you want custom header name/logic.

C. Rate-limiting / Throttling

<rate-limit calls="100" renewal-period="60" />          <!-- 100 calls per 60 seconds globally -->
<!-- or per subscription -->
<quota calls="1000" renewal-period="3600" remaining-quota-header-name="x-remaining-quota" />

D. Caching (response caching)

<cache-lookup vary-by-developer="false">
  <vary-by-header name="Accept" />
  <vary-by-query-parameter name="month" />
</cache-lookup>

<!-- backend call -->

<cache-store duration="60" />

E. Response Transformation (mask PII)

<set-body template="liquid">
  {
    "customerId": "{{body.customerId}}",
    "name": "{{body.name}}",
    "maskedSsn": "{{body.ssn | slice:0,3 }}***-**-****",
    "balance": "{{body.balance}}"
  }
</set-body>

Or use set-header, remove-header, set-query-parameter, rewrite-uri to manipulate request/response.

F. IP restriction (allowlist)

<check-header name="X-Forwarded-For" exists-action="skip">
  <value>203.0.113.10</value>
</check-header>

<!-- Alternatively use 'validate-client-certificate' or Azure Firewall + APIM policies to block -->

G. Add Correlation ID

<set-header name="X-Correlation-ID" exists-action="override">
  <value>@(context.RequestId)</value>
</set-header>

H. Log to Event Hub (audit)

<log-to-eventhub logger-id="eventHub-logger">
  <data>Request: @(context.Request.Method) @(context.Request.Url)</data>
</log-to-eventhub>

(Requires Event Hub logger configured in APIM settings)

7 — Policy application locations & scoping

8 — Developer Portal & Products

  1. APIM → Products → Create a product public or partners.

  2. Add APIs to product and require subscription.

  3. Developers can sign up in Developer Portal and get subscription keys (trial or approved).


9 — Self-hosted Gateway (hybrid / on-prem)

Use this when backend is on-prem or you want local routing but centralized control.

High level:

Docker run example (simplified):

docker run -d --name apim-gateway \
  -e GATEWAY_ID="<gateway-id>" \
  -e SIDECAR_URL="https://<gateway-config-url>" \
  mcr.microsoft.com/azure-api-management/gateway:latest

Self-hosted gateway uses certificates for trust and can operate without public internet access to your backend.

10 — VNet / Private Endpoint (secure internal APIs)

Premium tier only: configure virtualNetworkType=Internal and set up integration to a VNet. Use Private Endpoint to secure APIM to internal network; set backend (App Service) with VNet integration.

11 — Monitoring & Diagnostics

Enable Diagnostic settings (APIM → Diagnostic settings):

Log sampling: be mindful to avoid logging sensitive PII.

Capture RU/usage-like metrics: APIM exposes metrics for throughput, latency, bandwidth and calls.

CLI to enable Application Insights logger (example):

az resource update --ids /subscriptions/$SUBS_ID/resourceGroups/$RG/providers/Microsoft.ApiManagement/service/$APIM_NAME \
  --set properties.publisherEmail="[email protected]"
# configure diagnostics via ARM/Bicep for Application Insights (more verbose)

12 — CI/CD for APIM (ARM/Bicep + DevOps)

Useful tool: API Management DevOps Resource Kit (extractor + publisher) — turn APIM configuration into ARM templates for CI.

13 — Keys / Secrets / Certificate Best Practices

14 — Caching & Performance tuning tips

15 — Failover & Multi-region

16 — Example production policy (Composite: JWT + RateLimit + Cache + Transform)

<policies>
  <inbound>
    <base/>
    <!-- 1. Validate JWT -->
    <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized">
      <openid-config url="https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/v2.0/.well-known/openid-configuration" />
      <required-claims>
        <claim name="aud">
          <value>{api-client-id}</value>
        </claim>
      </required-claims>
    </validate-jwt>

    <!-- 2. Rate limit per-user (by subscription) -->
    <rate-limit-by-key calls="120" renewal-period="60" counter-key="@(context.Subscription?.Id ?? context.Request.IpAddress)" />

    <!-- 3. Cache GET responses for 60s -->
    <choose>
      <when condition="@(context.Request.Method == "GET")">
        <cache-lookup vary-by-developer="false" />
      </when>
    </choose>

    <!-- 4. Add Correlation ID -->
    <set-header name="X-Correlation-ID" exists-action="override">@((string)context.RequestId)</set-header>
  </inbound>

  <backend>
    <forward-request />
  </backend>

  <outbound>
    <!-- store in cache if GET -->
    <choose>
      <when condition="@(context.Request.Method == "GET")">
        <cache-store duration="60" />
      </when>
    </choose>

    <!-- mask PII using Liquid template (simplified) -->
    <set-body template="liquid">
      {
        "customerId":"{{body.customerId}}",
        "name":"{{body.name}}",
        "maskedSsn":"{{body.ssn | slice:0,3 }}***-**-****",
        "balance":"{{body.balance}}"
      }
    </set-body>
  </outbound>

  <on-error>
    <set-status code="500" reason="Internal Server Error" />
  </on-error>
</policies>

17 — .NET C# Test Client Examples

A — Call APIM using Subscription Key (simple)

using System;
using System.Net.Http;
using System.Threading.Tasks;

public static class ApimClient
{
    private static readonly HttpClient _client = new HttpClient();
    private const string APIM_BASE = "https://api.abcinc.com"; // APIM public endpoint

    public static async Task CallBillingAsync(string customerId)
    {
        var req = new HttpRequestMessage(HttpMethod.Get, $"{APIM_BASE}/billing/{customerId}");
        req.Headers.Add("Ocp-Apim-Subscription-Key", Environment.GetEnvironmentVariable("APIM_SUB_KEY") ?? "your-sub-key-here");

        var res = await _client.SendAsync(req);
        Console.WriteLine($"Status: {res.StatusCode}");
        var content = await res.Content.ReadAsStringAsync();
        Console.WriteLine(content);
    }
}

B — Call APIM using Azure AD (OAuth2) — get access token & call (recommended for user flows)

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.Identity.Client; // or use MSAL

public static class ApimAadClient
{
    private static readonly HttpClient _http = new HttpClient();

    public static async Task CallWithAadAsync(string customerId)
    {
        // MSAL confidential client flow (server-to-server)
        var tenantId = "<tenant-id>";
        var clientId = "<client-id>";   // app registration
        var clientSecret = "<client-secret>";
        var apimScope = "https://management.azure.com/.default"; // or custom API scope

        var app = ConfidentialClientApplicationBuilder
            .Create(clientId)
            .WithClientSecret(clientSecret)
            .WithAuthority($"https://login.microsoftonline.com/{tenantId}")
            .Build();

        // Acquire token for APIM backend scope (if APIM validates issuer & audience)
        string[] scopes = new[] { "api://<apim-backend-client-id>/.default" }; // depends on API/AAD app
        var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();

        var request = new HttpRequestMessage(HttpMethod.Get, $"https://api.abcinc.com/billing/{customerId}");
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);

        var resp = await _http.SendAsync(request);
        resp.EnsureSuccessStatusCode();
        Console.WriteLine(await resp.Content.ReadAsStringAsync());
    }
}

For user-login scenarios use Authorization Code flow (MSAL browser + bearer token).

18 — Self-hosted Gateway: quick how-to

  1. APIM → Gateways → Add a Gateway → give it a name, assign to an APIM service.

  2. Click on the gateway → Get gateway configuration (download).

  3. Provision a VM / Kubernetes node and run the gateway container with that config. Gateway then receives API definitions and policies from central APIM and performs routing locally.

Use cases:

19 — Real Case Scenarios (ABC Inc)

Scenario A — Public Mobile App (High concurrency)

Scenario B — Partner Batch Upload (Data Ingest)

Scenario C — On-prem SCADA Integration

Scenario D — Regulatory Audit & Masking

Scenario E — Multi-region & Disaster Recovery

20 — Troubleshooting & Tips

21 — Security & Governance checklist

22 — Interview Questions (Beginner → Advanced)

Beginner

  1. What is Azure API Management? What components does it include?

  2. How do subscription keys work? How do you use them?

  3. What is the difference between API version and revision?

  4. When should you use APIM vs Azure Application Gateway?

Intermediate

  1. How does APIM policy engine work? Give an example policy.

  2. What approaches are available for securing APIs in APIM? (Subscription key, OAuth/JWT, client certificate)

  3. How to import an OpenAPI spec into APIM? CLI or Portal steps?

  4. Explain caching in APIM. When to cache responses and what are the caveats?

Advanced

  1. Explain how a self-hosted APIM gateway works. When would you use it?

  2. How would you integrate APIM with Azure AD or Azure AD B2C for user authentication?

  3. How to implement per-user rate limiting? (rate-limit-by-key)

  4. Explain how to do private (internal) APIs using APIM and VNet integration.

  5. How would you manage APIM configurations across environments (dev/stage/prod)?

  6. How to avoid logging PII in diagnostics while still capturing useful telemetry?

  7. How does APIM handle multi-region deployments and failover? What tier is required?

Practical Scenario Questions

23 — Useful Resources & Tools (short list)