| name | policy-authoring |
| description | Conventions and patterns for creating policy authoring types in the Azure API Management policy toolkit. Use this skill when creating or modifying config records in src/Authoring/Configs/ or adding methods to section context interfaces. |
Policy Authoring Patterns
This skill describes how to create authoring types (config records and context interface methods) for policies in the Azure API Management policy toolkit.
Config Record โ src/Authoring/Configs/{PolicyName}Config.cs
File Template
namespace Microsoft.Azure.ApiManagement.PolicyToolkit.Authoring;
public record {PolicyName}Config
{
[ExpressionAllowed]
public required {Type} {PropertyName} { get; init; }
public {Type}? {OptionalPropertyName} { get; init; }
}
Related Attributes
[Document] (src/Authoring/Attributes/DocumentAttribute.cs) โ Marks a class as a policy document. Optional name parameter; Scope and Type properties control document scope and type.
[Expression] (src/Authoring/Attributes/ExpressionAttribute.cs) โ Marks a method as a policy expression (captures source file path via [CallerFilePath]). Used by the compiler to identify expression helper methods.
[ExpressionAllowed] (src/Authoring/Attributes/ExpressionAllowedAttribute.cs) โ Marks config properties or parameters that accept policy expressions.
Property Rules
- Required properties: use the
required keyword + init setter.
- Optional properties: use a nullable type (
?) + init setter. Do not use required.
- Expression-enabled properties: decorate with
[ExpressionAllowed] attribute. Add "Policy expressions are allowed." to the XML doc.
- Constrained properties (enums, allowed values): use either:
- A C#
enum type (preferred for fixed, type-safe values)
- A
string property with XML doc listing allowed values (for values documented outside code)
- Validation is applied at compilation time by the compiler class, not at authoring time. The config record accepts any value; the compiler reports diagnostics if invalid.
- Sub-configs (child element arrays): define as separate
public record types in the same file.
- Sub-config naming convention: Use descriptive nouns that reflect their purpose (e.g.,
ApiRateLimit, AddressRange, MatchCondition), not generic names like Item or Element.
- Every public type and member must have
/// <summary> XML documentation covering:
- Purpose
- Required vs optional status
- Default value (if any)
- Expression support (yes/no)
- Allowed values / enums (if applicable)
Real Examples
Simple attributes with expression support โ RateLimitByKeyConfig.cs:
public record RateLimitByKeyConfig
{
[ExpressionAllowed]
public required int Calls { get; init; }
[ExpressionAllowed]
public required int RenewalPeriod { get; init; }
[ExpressionAllowed]
public required string CounterKey { get; init; }
[ExpressionAllowed]
public bool? IncrementCondition { get; init; }
public string? RetryAfterHeaderName { get; init; }
}
Nested sub-records โ RateLimitConfig.cs:
public record RateLimitConfig
{
public required int Calls { get; init; }
public required int RenewalPeriod { get; init; }
public string? RetryAfterHeaderName { get; init; }
public ApiRateLimit[]? Apis { get; init; }
}
public record ApiRateLimit : EntityLimitConfig
{
public OperationRateLimit[]? Operations { get; init; }
}
public abstract record EntityLimitConfig
{
public string? Name { get; init; }
public string? Id { get; init; }
public required int Calls { get; init; }
public required int RenewalPeriod { get; init; }
}
Typed child arrays โ IpFilterConfig.cs:
public record IpFilterConfig
{
[ExpressionAllowed]
public required string Action { get; init; }
[ExpressionAllowed]
public string[]? Addresses { get; init; }
public AddressRange[]? AddressRanges { get; init; }
}
public record AddressRange
{
[]
From { ; ; }
[]
To { ; ; }
}
Constrained values with enum โ AdvancedPolicyConfig.cs (example pattern):
public enum CachingBehavior
{
Store,
Validate,
Bypass
}
public record AdvancedPolicyConfig
{
public required CachingBehavior Behavior { get; init; }
}
Context Interface Methods
Add method signatures to the appropriate section interfaces. Policies may appear in one or more sections:
src/Authoring/IInboundContext.cs
src/Authoring/IOutboundContext.cs
src/Authoring/IBackendContext.cs
src/Authoring/IOnErrorContext.cs
src/Authoring/IFragmentContext.cs
Method Signature Template
void {MethodName}({PolicyName}Config config);
Rules
- The method name is PascalCase and matches the config name minus the
Config suffix (e.g., RateLimit for RateLimitConfig).
- All section interfaces extend
IHaveExpressionContext, giving access to ExpressionContext.
- For parameters that are expressions (not config objects), use
[ExpressionAllowed] on the parameter itself.
- Some policies use direct parameters instead of a config object (e.g.,
void SetMethod(string method)). Use this pattern only for policies with 1โ2 simple parameters and no optional fields.
IFragmentContext Duplication
IFragmentContext currently duplicates method signatures from the section-specific interfaces (see the //TODO comment at the top of the file). Every time you add a policy method to any section interface, you must also add the same signature to IFragmentContext.cs. Copy the exact same method signature verbatim. Do not attempt to refactor this pattern.