Reusing APIM policy expression via Named Value - azure

I have a problem reusing APIM expression.
Specificially, a named value below is created like above,
name
JWTValidator
#(
#"<validate-jwt header-name='Authorization' failed-validation-httpcode='401' failed-validation-error-message='Error: expired token or invalid token' require-expiration-time='true' require-scheme='Bearer' require-signed-tokens='true'>
<openid-config url='xxx' />
<audiences>
<audience>xxx</audience>
</audiences>
<issuers>
<issuer>https://xxx</issuer>
</issuers>
</validate-jwt>"
)
and the policy below:
<policies>
<inbound>
<base />
{{JWTValidator}}
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
However, the policy element is removed and not inserted.
Any idea?
Is there a better way to reusing policy?
Upate
I want to define JWTValidator as Named value, and use it on Product level if possible, otherwise, API level.
Upate 2
I have changed to below, however, {{JWTValidator}} is auto-removed when it is saved.
Please note that the value of JWTValidator is saved successfully, which might mean the syntax is correct.
<policies>
<inbound>
{{JWTValidator}}
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-properties
https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions
Reusing APIM policy expressions
https://feedback.azure.com/forums/248703-api-management/suggestions/16951852-code-re-use-in-api-policies-using-of-custom-functi

This is actually UI issue. You should be able to see in browser dev tools that PUT request to save policy succeeds and returns proper saved content. It is UI merely removing property reference before presenting policy. Will be fixed soon.
Property may be used to fill in value of XML attribute or XML element in full. In your case you're trying to set value of in part with static element and in another part with property value. That is not supported, unfortunately. In other words, this is supported:
<inbound>
{{JWTValidator}}
</inbound>
and this is not:
<inbound>
<base />
{{JWTValidator}}
</inbound>
Property can only replace element value as a whole, and it cannot be used side by side with another element, like in your example, next to <base/>.
In you case I feel that it would be best to place this policy at outer scope: at API level if you need it applied to many operations, at Product/Global level if it's for multiple APIs. And have policy to apply it conditionally based on context.Operation.Id/context.Api.Id.

Related

How to create and use list in Azure API-manager policy?

Background:
I am defining an API in Azure API Management.
I have defined a policy on "All operations" level. This policy does a couple of things. One of the things it does is setting a variable in the context object, so I can re-use the variable in when condition.
What I need help with:
How can I define a list in Azure API-M policy, which I can refer to in when condition?
Code example:
All operations policy:
<policies>
<inbound>
<base />
<set-variable name="someList" value="[a,b,c,d]" />
<when condition="#(context.Variables["someList"].Contains("a"))"
</when>
<otherwise>
</otherwise>
</inbound>
</policies>
It seems that my issue is that the variable "someList" is not recognized as an array, but as a String = "[a,b,c,d]". So basically, it will return true if the condition would say Contains("[").
I have also tried to store value as named values, but named values can't contain a Array as value.
What I want to achieve is keeping a list of subscriptions, so that I can match incoming subscription key in request to a list of pre-defined subscription keys.
Try parsing the context.Variables['somelist'] as a list before using .ContainsKey('key').
I never needed to parse a list, so you have to google it how to do it, use keywords like C# and Dictionary or List for this, but i've done some parsing as JObject and int like this.
#{
int myvar = context.Variables["var"];
return myvar > 5;
}
And
#((int)context.Variables("year") < 2022)
Api management offers you the GetValueOrDefault<Type>("ParameterName", "DefaultValueIfNull") method to let you capture a header, variable or query and parse it directly, you'll find it here.
https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions
Try parsing the value of someList to JArray in the condition.
<policies>
<inbound>
<base />
<set-variable name="someList" value="[a,b,c,d]" />
<when condition="#(JArray.Parse((string)context.Variables["someList"]).Contains("a"))">
</when>
<otherwise>
</otherwise>
</inbound>

Set-AzApiManagementPolicy - How to set policy on multiple Operations

I am trying to understand how to apply custom policies (ie. Caching) to target each operations within an given API. Below is a sample cache policy file (xml) for a "Get Test1" policy:
<policies>
<inbound>
<base />
<cache-lookup vary-by-developer="false" vary-by-developer-groups="false" allow-private-response-caching="false" must-revalidate="false" downstream-caching-type="none" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<cache-store duration="3600" />
</outbound>
<on-error>
<base />
</on-error>
So based on the above, I have several other operations within the same API that I want to apply unique policy to (Get Test2, Get Test3...). My understanding is that I need to execute a new Set-AzApiManagementPolicy for each operation I want to apply a policy to. Is this correct? This seem a bit tedious.
Can Set-AzApiManagementPolicy take a single XML file with various scopes defined within it?
Thanks

set backend url after extracting it from header azure apim

Hi I am trying to implement such a functionality where i need to make sure that the api is going through gateway so have created a apim and i need to extract header from the call and route to that call :
so far i have done this policy:
<policies>
<inbound>
<set-variable name="host" value="#(context.Request.Headers.GetValueOrDefault("uri","http://google.com/"))" />
<set-backend-service base-url="${{ variables.host}}" />
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
but its giving error :
Error in element 'set-backend-service' on line 16, column 4: Value is not a valid absolute URL.
Error in element 'set-backend-service' on line 16, column 4: Value is not a valid absolute URL.
This probably means that the value of your uri header does not include https://. A valid absolute URL contains all the information necessary to locate a resource, including the protocol (HTTPS).
You can also set the value directly in the set-backend-service without using the set-variable first. Like:
<set-backend-service base-url="#(context.Request.Headers.GetValueOrDefault("header-name","optional-default-value"))" />
<set-backend-service base-url="#((string)context.Variables["host"])" />

Trouble with Azure API Management TRACE policy rule not expanding variables in the message

Following the instructions from this page https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions and this page https://learn.microsoft.com/en-us/azure/api-management/api-management-advanced-policies#Trace , I am trying to define a TRACE policy rule in my inbound APIM API policy.
My rule looks like this but when it shows up in Application Insights, the variable hasn't expanded, not even if I use the simplest rule #(true) . https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions
Here is my policy:
<policies>
<inbound>
<base />
<trace source="me-apim.azure-api.net" severity="information">
<message>Requesting User: #(context.User)</message>
</trace>
</inbound>
<backend>
<forward-request />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Any idea what I might be doing wrong that causes the variable to not expand?
UPDATE: I found I was able to get this one to work. This suggests to me that maybe I am merely interacting with the context object incorrectly?
<trace source="me-apim.azure-api.net" severity="information">
<message>#( string.Join(":", "Current Time", DateTime.UtcNow) )</message>
</trace>
NOTE: Super helpful things I learned while working on this:
Using VS Code as an APIM debugger works like a charm.
The Azure ADO UX has a "snippets" thing that is really helpful when creating policy rules.
Yes, such variable expansion does not work at all, no matter the policy you try. The whole value should be either a constant or expression, not text with expression inside it. I.e., instead of:
<message>Requesting User: #(context.User)</message>
you could use as you found out:
<message>#( string.Join(":", "Current Time", DateTime.UtcNow) )</message>
Or a bit simpler using string interpolation:
<message>#($"Current Time:{DateTime.UtcNow})</message>
Or more specifically to original problem:
<trace source="me-apim.azure-api.net" severity="information">
<message>#("Requesting User: " + context.User.Id)</message>
</trace>
or:
<trace source="me-apim.azure-api.net" severity="information">
<message>#($"Requesting User: {context.User.Id}")</message>
</trace>

Policy to Avoid duplicate https calls on an Azure Api endpoint

I have some troubles regarding some duplicate calls on my Azure Api Endpoint. For example, I see that my endpoint :
tennis/getballs
has been called twice
First : 2020-11-02T18:07:19.8261667Z
Second :2020-11-02T18:07:19.881239Z
I want to avoid this second call which has the same payload (like "I don't do this call because it's too close than the first call", it's ok if it's 5 seconds later). I'm looking for something with the Policy but I didn't find something interesting.
Accourding to your description, maybe you want to add rate limit to your api. I think this document will help you.
This part describes how to prevent your api being called within short time. You can modify the policy to meet your custom request.
Enter api management webpage then select your target API > All operations > Design.
In the Inbound processing section, select the code editor (</>) icon.
Position the cursor inside the element and select Show snippets at the top right corner.
In the right window, under Access restriction policies, select + Limit call rate per key.
Modify your rate-limit-by-key code (in the element) to the following code:
calls="3" renewal-period="15" means you can call this api no more than 3 times within 15 seconds.
enter image description here
<policies>
<inbound>
<rate-limit-by-key calls="3" renewal-period="15" counter-key="#(context.Subscription.Id)" />
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<set-header name="X-Powered-By" exists-action="delete" />
<set-header name="X-AspNet-Version" exists-action="delete" />
<redirect-content-urls />
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
After editing, you can call your api then you will find 429 Too many requests when calling too many times as your configuration.

Resources