Execution order of policies

 · 5 mins read

I wrote some posts about Azure API management earlier last year, but last week I was discussing some things with a colleague of mine and he asked a question neither of us could answer out of our heads:

When you send a request to Azure API Management, what order is maintained to execute the policies?

After some searching neither of us could find it, so here's a blog post to save you the trouble after some testing :-)

Scenario

We want to determine the execution order in which Azure API Management executes policies for a request. We consider the following policies are considered here:

  • Operation policy
  • API policy
  • All APIs policy
  • Product policy

Set-up

To test this specific case, I provisioned an Azure API Management instance within the relatively new Consumption tier. Using this tier I can just set this instance up in my MSDN subscription with limited Azure credit and still only get billed when I use it... Downside is that I don't have any developer portal, but for testing this is not needed.

I setup a new API "Test" with a free and easy RequestBin back-end:

I created one operation there called "post" (yes, I sometimes struggle with creativity):

I also setup a new Product called "genericProduct":

Now we just need to come up with some policies...
We know that policies often have a <base /> policy, so we need to take that into account as well.

I decided to setup the following policies:

  • Our All APIs policy:
    Here we don't have a <base /> policy, so I just add a single new query parameter.
  • Our Test API 'All Operations' policy:
    We add separate query parameters before and after the base policy.
  • Our Test API operation policy:
    We add separate query parameters before and after the base policy.
  • Our Product policy
    We add separate query parameters before and after the base policy.

These are the policies in detail:

All APIS policy

<policies>
    <inbound>
        <set-query-parameter name="policy-allapis" exists-action="append">
            <value>allapis</value>
        </set-query-parameter>
    </inbound>
    <backend>
        <forward-request />
    </backend>
    <outbound />
    <on-error />
</policies> 

Test API - All Operations policy

<policies>
    <inbound>
        <set-query-parameter name="policy-allops-before" exists-action="append">
            <value>allops-before</value>
        </set-query-parameter>
        <base />
        <set-query-parameter name="policy-allops-after" exists-action="append">
            <value>allops-after</value>
        </set-query-parameter>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies> 

Test API - post operation policy

<policies>
    <inbound>
        <set-query-parameter name="policy-operation-before" exists-action="append">
            <value>operation-before</value>
        </set-query-parameter>
        <base />
        <set-query-parameter name="policy-operation-after" exists-action="append">
            <value>operation-after</value>
        </set-query-parameter>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>  

Product policy

<policies>
    <inbound>
        <set-query-parameter name="policy-product-before" exists-action="append">
            <value>product-before</value>
        </set-query-parameter>
        <base />
        <set-query-parameter name="policy-product-after" exists-action="append">
            <value>product-after</value>
        </set-query-parameter>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies> 

So what do we see now in RequestBin?

Conclusion

We consistently see the following appearing in our traces:

3

source
"set-query-parameter"
timestamp
"2019-05-23T09:25:03.0219982Z"
elapsed
"00:00:00.1175110"
data

message
"Request URL was updated with the query value."
request

url
"http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before"
4

source
"set-query-parameter"
timestamp
"2019-05-23T09:25:03.0219982Z"
elapsed
"00:00:00.1175248"
data

message
"Request URL was updated with the query value."
request

url
"http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before"
5

source
"set-query-parameter"
timestamp
"2019-05-23T09:25:03.0219982Z"
elapsed
"00:00:00.1175298"
data

message
"Request URL was updated with the query value."
request

url
"http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before"
6

source
"set-query-parameter"
timestamp
"2019-05-23T09:25:03.0219982Z"
elapsed
"00:00:00.1175350"
data

message
"Request URL was updated with the query value."
request

url
"http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before&policy-allapis=allapis"
7

source
"set-query-parameter"
timestamp
"2019-05-23T09:25:03.0219982Z"
elapsed
"00:00:00.1175417"
data

message
"Request URL was updated with the query value."
request

url
"http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before&policy-allapis=allapis&policy-product-after=product-after"
8

source
"set-query-parameter"
timestamp
"2019-05-23T09:25:03.0219982Z"
elapsed
"00:00:00.1175458"
data

message
"Request URL was updated with the query value."
request

url
"http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before&policy-allapis=allapis&policy-product-after=product-after&policy-allops-after=allops-after"
9

source
"set-query-parameter"
timestamp
"2019-05-23T09:25:03.0219982Z"
elapsed
"00:00:00.1175504"
data

message
"Request URL was updated with the query value."
request

url
"http://requestbin.fullcontact.com/1bipq9x1/post?policy-operation-before=operation-before&policy-allops-before=allops-before&policy-product-before=product-before&policy-allapis=allapis&policy-product-after=product-after&policy-allops-after=allops-after&policy-operation-after=operation-after" 

I tried several dozens of requests and different requestbins, the order always stays the same.

So, it seems we can conclude this is the order in which APIM executes policies:

  1. Operation policy before <base />
  2. All Operations policy before <base />
  3. Product policy before <base />
  4. All APIs policy
  5. Product policy after <base />
  6. All Operations policy after <base />
  7. Operation policy after <base />

As usual, finding the solution proved to be much quicker than writing the blog post :-)

Please place any feedback in the comments below. I hope you have a great day!
Cheers, Pieter

Noteworthy: initially I was looking at RequestBin itself to see the order of the query parameters. However, this doesn't match with the query parameters I saw in the APIM traces. They are in a different order or even alphabetically. Don't depend on it too much!