Azure API Management (APIM)

Azure APIM CLI commands and policy snippets for API gateway configuration, security, and rate limiting.

Service Management (CLI)

# Create an APIM instance
az apim create \
  --name myAPIM \
  --resource-group myRG \
  --publisher-email admin@example.com \
  --publisher-name "My Org" \
  --sku-name Consumption

# List APIM instances
az apim list -o table

# Import API from OpenAPI spec
az apim api import \
  --resource-group myRG \
  --service-name myAPIM \
  --api-id my-api \
  --path /myapi \
  --specification-format OpenApiJson \
  --specification-url "https://example.com/swagger.json"

# List APIs
az apim api list -g myRG -n myAPIM -o table

Common Policies

Rate Limiting

<inbound>
    <!-- 100 calls per 60 seconds per subscription -->
    <rate-limit calls="100" renewal-period="60" />

    <!-- OR quota: 10000 calls per 7 days -->
    <quota calls="10000" renewal-period="604800" />
</inbound>

JWT Validation

<inbound>
    <validate-jwt header-name="Authorization" require-scheme="Bearer"
                  failed-validation-httpcode="401"
                  failed-validation-error-message="Unauthorized">
        <openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration" />
        <audiences>
            <audience>api://my-api-client-id</audience>
        </audiences>
        <issuers>
            <issuer>https://sts.windows.net/{tenant-id}/</issuer>
        </issuers>
        <required-claims>
            <claim name="roles" match="any">
                <value>Api.Read</value>
                <value>Api.Write</value>
            </claim>
        </required-claims>
    </validate-jwt>
</inbound>

CORS

<inbound>
    <cors allow-credentials="true">
        <allowed-origins>
            <origin>https://myapp.example.com</origin>
        </allowed-origins>
        <allowed-methods preflight-result-max-age="300">
            <method>GET</method>
            <method>POST</method>
            <method>PUT</method>
            <method>DELETE</method>
            <method>OPTIONS</method>
        </allowed-methods>
        <allowed-headers>
            <header>Authorization</header>
            <header>Content-Type</header>
        </allowed-headers>
    </cors>
</inbound>

Caching

<inbound>
    <cache-lookup vary-by-developer="false"
                  vary-by-developer-groups="false"
                  downstream-caching-type="none">
        <vary-by-query-parameter>version</vary-by-query-parameter>
    </cache-lookup>
</inbound>
<outbound>
    <cache-store duration="3600" />
</outbound>

Request/Response Transformation

<inbound>
    <!-- Add header -->
    <set-header name="X-Request-ID" exists-action="skip">
        <value>@(context.RequestId.ToString())</value>
    </set-header>

    <!-- Rewrite URL -->
    <rewrite-uri template="/api/v2/{path}" />
</inbound>
<outbound>
    <!-- Remove internal headers -->
    <set-header name="X-Powered-By" exists-action="delete" />
    <set-header name="X-AspNet-Version" exists-action="delete" />
</outbound>

Backend Circuit Breaker

<backend>
    <retry condition="@(context.Response.StatusCode >= 500)"
           count="3" interval="1" first-fast-retry="true">
        <forward-request buffer-request-body="true" timeout="30" />
    </retry>
</backend>