Azure API Management Policy Fragments

When you work with Azure API Management on a regular basis, you probably are familiar with policies. Policies allow you to perform actions or adjustments on the incoming request before it's sent to the backend API, or adjust the response before returning to the caller.

Policies can be applied on various levels, so called scopes, and each lower level can inherit the policy of a higher level.

  • Global level => executed for all APIs
  • Product level => executed for all APIs under a product
  • API level => executed for all operations under an API
  • Operation level => executed for this single operation

Policy scopes

Maintenance and reuse issues

The main problems with policies always have been maintenance and reuse. Policy code is quite hidden within the portal (especially with so many scope levels where it can reside), so it's hard to see where a policy is used. When a certain piece of policy is used in multiple places, it's even harder to keep track where they're used and keeping them consistent. Bug fixing is difficult and cumbersome, as you need to find out all the places where you need to fix it.

Microsoft has acknowledged this to be an issue, and early June this year policy fragments were made general available.

Policy Fragments

When you open the portal, you can find a new section called Policy Fragments. It works similar to named values. You can create pieces of policies in there, name them and which you can reference in your policies. This means you define them once, and reuse where needed. I'm not going into the details of creating and using policy fragments, you can find an excellent explaination in the Microsoft docs.

Limitations

The thing I'd like to discuss is what you need to be aware of when starting with policy fragments. Having fragments is a real step forward, but in my view the overall experience can be improved.

Parameter support

When you define a fragment, you can reference this in your policy like this:

<include-fragment fragment-id="name-of-fragment" />

There are no other parameters you can provide but the fragment-id, which references the name of the defined fragment. In practice the policy fragment is copied over from the fragment library into your policy, kind of ‘search fragment-id and replace with content’. This means you get an exact copy of the fragment in your policy. In some scenarios this is perfect, for example if you want to apply CORS, validate certain HTTP headers, or if you want to remove some outbound HTTP headers for security reasons. However, the fact that it's a copy, also reduces the reusability.

For example, when I want to create a fragment to validate a JWT, it would be convenient if I could provide the audience or claims as parameter. Most of the policy will remain the same, but the audience and claims might be different. Currently this is not supported and means we have to create a policy for each of the scenarios, which is less maintainable.

Of course you can take an alternative approach, by setting a variable with a value which will function as parameter value. When you do this just before calling the include-fragment, you can use it in the fragment as a parameter value.

In the example below the fragment contains a policy expression, which reads the variablew set in the policy.

<fragment>
	<set-header name="MyHeader">
		<value>@((string)context.Variables["my-value"])</value>
	</set-header>
</fragment>
<policies>
    <inbound>
        <set-variable name="my-value" value="hello from policy" />
        <include-fragment fragment-id="test-from-tf" />
        <base />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

From the fragments point of view, the problem with parameters here is the fact you never know what's needed. A string? A boolean? An integer? Or an array of strings, key/value pair or dictionary? Object? You cannot support all scenarios, so in that sense I can understand the decision to use variables to hand over values. It would be nice though, if some basic parameter passing would be possible, and resort to variables for more custom solutions.

For policies which only have environment specific variables, like dev/stg/beta/prd, policy fragments will be a tremendous increase maintainability and reuse.

Policy content file

When provisioning policies via Bicep or Terraform, you can specify a content file or inline content. By specifying ‘xml’ or ‘xml-link’ you can instruct Bicep to either use inline content or content in a file. Having the policy content in a file is much more maintainable, compared to hidden in the provisioning statement itself. Unfortunately policy fragements currently only support inline content. It is possible to use the Heredoc notation, which makes the policy content much better readable. I still prefer a separate policy fragment content file though.

I was pointed at the fact that both Bicep and Terraform are able to read content from file. You can use the file contents directly in the resource, which kind of does the same as with ‘xml-link’.

This removes that limitation, but I don't really understand why two things so closely related, namely policies and policy fragments, are taking a different approach for the same thing.

Provisioning

In 2022 we don't do anything manually in the portal anymore, we use Terraform or Bicep (or similar) to provision the resources for us. In my day to day job I use Terraform and one of the backdraws is the latest and greatest on Azure is not immediately natively supported.

As that has been an issue for quite some time, the AzApi provider was developed. This bridges the gap between Terraform native and ARM templates and this was a nice opportunity to see how this works. I have experience with Terraform and ARM, and I ran into deployment issues more than once, so I hope the AzApi provider will be more stable.

This code on Github provides an example of using AzApi provider to create or update a policy fragment. For completeness sake it also contains creating an entire API Management instance, but the AzApi provider adds the policy fragment.

resource "azapi_resource" "apim-fragment" {
  type      = "Microsoft.ApiManagement/service/policyFragments@2021-12-01-preview"
  name      = "test-from-tf"
  parent_id = azurerm_api_management.apim.id

  body = jsonencode({
    properties = {
      description = "test-from-tf"
      format      = "rawxml"
      value       = <<XML
        <fragment>
          <set-header name="MyHeader">
            <value>MyValue</value>
          </set-header>
        </fragment>
      XML
    }
    })
}

Running this indeed creates a policy fragment in the APIM instance, and it works quite well. Updating the policy fragment also works as expected.

Closing note

Policy fragments are a big step forward, but there are some things to be aware of.

If you have any questions, you can always reach out to me on Twitter.