Appearance
Introduction to APIM Policy Scopes
This document explains the different policy scopes available in Azure API Management (APIM) and provides examples for each scope. Understanding policy scopes is essential for properly configuring how your APIs handle requests, validate tokens, transform data, and enforce security measures.
For comprehensive documentation on APIM policies, refer to the Microsoft documentation.
All policies below are managed in the infrastructure repository as part of the API configuration.
Policy Scopes
API Management enables you to define policies at the following scopes, presented here from broadest to narrowest:
- Global - Applies to all APIs
- Workspace - Applies to all APIs associated with a selected workspace (not used in our setup)
- Product - Applies to all APIs associated with a selected product
- API - Applies to all operations in an API
- Operation - Applies to a single operation in an API
When configuring a policy, you must first select the scope at which the policy applies. Policies defined at narrower scopes inherit from broader scopes using the <base /> element, which includes the parent policy configuration.
WARNING
As a best practice, include a <base /> element at the beginning of each policy section to inherit policies from the parent scope
Policy Structure
Each policy definition consists of four main sections:
- inbound - Throttle, authorize, validate, cache, or transform the requests
- backend - Control if and how the requests are forwarded to services
- outbound - Customize the responses
- on-error - Handle exceptions and customize error responses
Our Policies config by Scope
Global Scope
Policies at the global scope apply to all APIs in APIM. This is typically used for organization-wide settings like CORS for the developer portal.
INFO
The terminate-unmatched-request="false" attribute is important for global CORS policies and product-level CORS policies to coexist without conflict with API policies.
xml
<policies>
<inbound>
<cors allow-credentials="true" terminate-unmatched-request="false">
<allowed-origins>
<origin>https://equation-acceptance.developer.azure-api.net</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
</inbound>
<backend>
<forward-request />
</backend>
<outbound />
<on-error>
<choose>
<when condition="@(context.Response != null && context.Response.StatusCode == 404)">
<set-status code="404" reason="Routes Not Found in APIM" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("statusCode", 404),
new JProperty("message", "The requested API endpoint could not be found in API Management. Please verify the URL or route.")
).ToString();
}</set-body>
</when>
</choose>
</on-error>
</policies>This file's content is sync with infrastructure repository at /global_policy/global-policy.xml.tpl
Product Scope
Policies at the product scope apply to all APIs associated with a specific product. This is useful for grouping APIs that share common requirements like CORS origins or rate limits.
xml
<policies>
<inbound>
<base />
<cors allow-credentials="true" terminate-unmatched-request="false">
<allowed-origins>
<origin>https://localhost:3000</origin>
<origin>http://localhost:9999</origin>
<origin>https://equation-excel.haskoning.app</origin>
<origin>https://equation-excel-acceptance.haskoning.app</origin>
</allowed-origins>
<allowed-methods>
<method>GET</method>
<method>POST</method>
<method>PUT</method>
<method>DELETE</method>
<method>HEAD</method>
<method>OPTIONS</method>
<method>PATCH</method>
</allowed-methods>
</cors>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>INFO
Note that the <base /> element in each section, which inherits policies from the parent scope (in this case, the global scope). Please check on each policy to ensure the behaviour of them are inherited or overridden policies are as expected.
See the files in the infrastructure repository under /product_policies/ for each product.
API Scope
Policies at the API scope apply to all operations within a specific API. This is the most common scope for defining authentication, backend routing, and API-specific transformations. By default, all APIs added to Equation have JWT validation and forwarding headers configured at this scope.
Example: JWT validation and custom headers for an API
xml
<policies>
<inbound>
<base />
<allowed-origins>
<origin>https://your.frontend.etc</origin>
</allowed-origins>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration" />
<issuers>
<issuer>https://sts.windows.net/{tenant-id}/</issuer>
<issuer>https://login.microsoftonline.com/{tenant-id}/v2.0</issuer>
</issuers>
</validate-jwt>
<set-backend-service backend-id="shield-runner-backend" />
<set-header name="X-Forwarded-Host" exists-action="override">
<value>@(context.Request.OriginalUrl.Host)</value>
</set-header>
<set-header name="X-Forwarded-Proto" exists-action="override">
<value>@(context.Request.OriginalUrl.Scheme)</value>
</set-header>
<set-header name="X-Forwarded-Prefix" exists-action="override">
<value>/shield-runner</value>
</set-header>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>This example demonstrates:
- Inheriting parent policies with
<base /> - Validating JWT tokens for authentication
- Setting a custom backend service
- Adding forwarding headers for proper request routing
In the infrastructure repository, you can find the API-level policies under /api_policies/. the default policy file is named default_policy.xml.tpl and is used as a template to generate a similar policy above for each endpoint.
Operation Scope
Policies at the operation scope apply to a single endpoint within an API. This is useful for endpoint-specific requirements like different rate limits or response transformations.
Operation-level policies follow the same structure as API-level policies but provide the finest level of control. Use this scope sparingly to avoid overly complex configurations.
Best Practices
- Start Broad, Refine Narrow: Define common policies at broader scopes (global or product) and override or extend them at narrower scopes (API or operation) as needed.
- Use
<base />Consistently: Always include<base />in child policies to inherit parent configurations unless you explicitly want to replace them entirely. - Document Policy Intent: Add comments in your policy XML to explain the purpose of complex configurations.
- Test Thoroughly: Changes to policies can affect multiple APIs or operations, so test carefully in non-production environments first.