| name | apim-policies |
| description | Guide for creating Azure API Management (APIM) XML policies. Use when users want to create, modify, or understand APIM policies including inbound/outbound processing, authentication, rate limiting, caching, transformations, and policy expressions. This skill provides policy syntax, examples, and C# policy expressions for request/response manipulation. |
APIM Policies
This skill provides guidance for creating Azure API Management XML policies.
Policy Document Structure
Every APIM policy document follows this structure:
<policies>
<inbound>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
The <base /> element inherits policies from parent scopes (Global → Product → API → Operation).
Policy Categories Quick Reference
| Category | Common Policies | Section |
|---|
| Authentication | authentication-managed-identity, validate-azure-ad-token, validate-jwt | inbound |
| Rate Limiting | rate-limit-by-key, quota-by-key | inbound |
| Caching | cache-lookup, cache-store | inbound/outbound |
| Routing | set-backend-service, forward-request, retry | inbound/backend |
| Transformation | set-header, set-body, set-variable, rewrite-uri | any |
| Control Flow | choose, return-response, retry, wait | any |
Essential Policies
Set Backend Service
Route requests to a specific backend:
<set-backend-service backend-id="my-backend" />
Authentication with Managed Identity
Authenticate to Azure services using APIM's managed identity:
<authentication-managed-identity resource="https://cognitiveservices.azure.com"
output-token-variable-name="managed-id-access-token" ignore-error="false" />
<set-header name="Authorization" exists-action="override">
<value>@("Bearer " + (string)context.Variables["managed-id-access-token"])</value>
</set-header>
Validate Azure AD Token
Validate JWT tokens from Microsoft Entra ID:
<validate-azure-ad-token tenant-id="{tenant-id}">
<client-application-ids>
<application-id>{client-app-id}</application-id>
</client-application-ids>
</validate-azure-ad-token>
Conditional Logic (Choose)
Apply policies based on conditions:
<choose>
<when condition="@(context.Request.Headers.GetValueOrDefault("X-Custom","") == "value")">
</when>
<otherwise>
</otherwise>
</choose>
Return Custom Response
Return an immediate response without calling the backend:
<return-response>
<set-status code="403" reason="Forbidden" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{"error": "Access denied"}</set-body>
</return-response>
Retry Logic
Retry failed requests with conditions:
<retry count="3" interval="1" first-fast-retry="true"
condition="@(context.Response.StatusCode == 429 || context.Response.StatusCode >= 500)">
<forward-request buffer-request-body="true" />
</retry>
Policy Expressions
Policy expressions use C# syntax within @() for single statements or @{} for multi-statement blocks.
Common Expressions
@(context.Request.Headers.GetValueOrDefault("header-name", "default"))
@(context.Request.Url.Query.GetValueOrDefault("param-name", "default"))
@(context.Request.MatchedParameters.GetValueOrDefault("param-name", "default"))
@(context.Subscription.Id)
@(context.Request.IpAddress)
@(context.Request.Body.As<JObject>(preserveContent: true)["property"]?.ToString())
@(context.Request.Headers.ContainsKey("header-name"))
@(context.Variables.GetValueOrDefault<string>("var-name", "default"))
Multi-Statement Expression
<set-variable name="result" value="@{
string[] value;
if (context.Request.Headers.TryGetValue("Authorization", out value))
{
if(value != null && value.Length > 0)
{
return Encoding.UTF8.GetString(Convert.FromBase64String(value[0]));
}
}
return null;
}" />
Allowed .NET Types and Members (CRITICAL)
Policy expressions may only reference the .NET Framework types and members on APIM's allow-list. Anything outside the list causes a deploy-time ValidationError: One or more fields contain incorrect values with no further detail, which is hard to diagnose.
Before using a type or member in a policy expression, verify it appears on the official allow-list. Common pitfalls:
- Whole namespaces are absent.
System.Globalization (e.g. DateTimeStyles, CultureInfo), System.Threading, System.IO (except StringReader/StringWriter), System.Net.Http, System.Reflection, and System.Diagnostics are not allowed.
System.DateTime is restricted. Allowed members include Parse, UtcNow, Now, AddSeconds, Subtract, ToString, Ticks. Not allowed: TryParse, TryParseExact, ParseExact, ToUniversalTime, ToLocalTime, SpecifyKind. For round-trip-safe time math, prefer System.DateTimeOffset (All members allowed) and ToUnixTimeSeconds() / ToUnixTimeMilliseconds().
System.Enum is restricted to Parse, TryParse, ToString. No GetValues, GetNames, IsDefined.
System.Text.RegularExpressions.Regex is restricted to the constructor plus IsMatch, Match, Matches, Replace, Unescape, Split. No CompileToAssembly, CacheSize.
- Numeric primitives are fully allowed, so
int.TryParse, long.TryParse, double.TryParse are safe.
- JSON via
Newtonsoft.Json is the only supported JSON library — do not use System.Text.Json.
When a member you need is not allowed, refactor to an equivalent that is. Examples:
| Disallowed | Allowed replacement |
|---|
DateTime.TryParse(s, ..., DateTimeStyles.RoundtripKind, out dt) | Store as Unix epoch via DateTimeOffset.UtcNow.ToUnixTimeSeconds(), parse with long.TryParse |
DateTime.ParseExact(s, fmt, CultureInfo.InvariantCulture) | DateTime.Parse(s) (allowed) or epoch-based representation |
Enum.GetValues(typeof(T)) | Hard-code the comparison values or store as a string |
System.Text.Json.JsonSerializer.Deserialize<T>(s) | JsonConvert.DeserializeObject<T>(s) |
If you are unsure whether a member is allowed, fetch the allowed types table and confirm before writing the expression.
Reference Documentation
- Shared policies in this repo:
shared/apim-policies/ (reusable policy XML fragments)
Official Documentation