I'm trying to import a policy to azure api management using a bicep template as follows:
resource allOpsPolicy 'Microsoft.ApiManagement/service/apis/policies#2021-12-01-preview' = {
name: 'policy'
parent: apimapi_v1
properties: {
value: loadTextContent('all-ops-policy.xml')
format: 'xml'
}
}
The content of the all-ops-policy.xml file is as follows:
<policies>
<inbound>
<base />
<choose>
<when condition="#(context.Request.OriginalUrl.ToString().EndsWith("Public") == false)">
<rewrite-uri template="#(String.Concat(context.Request.OriginalUrl.ToString(),"/", "MapServer"))" copy-unmatched-params="false" />
</when>
<otherwise />
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
When run I get the following error:
One or more fields contain incorrect values: (Code: ValidationError)
'Public' is an unexpected token. Expecting white space.
Can anyone see where I'm going wrong?
The problem was the value of the format property of the allOpsPolicy resource.
I changed from
format: 'xml'
to
format: 'rawxml'
then it worked!
Thanks to Nisha for solving this one
Please replace the single quotes ` with double quotes ".
context.Request.OriginalUrl.ToString().EndsWith('Public') == false)
context.Request.OriginalUrl.ToString().EndsWith("Public") == false
It seems there's also one single quote too much:
String.Concat(context.Request.OriginalUrl.ToString(),'/'', 'MapServer')
String.Concat(context.Request.OriginalUrl.ToString(),"/", "MapServer")
Complete policy:
<policies>
<inbound>
<base />
<choose>
<when condition="#(context.Request.OriginalUrl.ToString().EndsWith("Public") == false)">
<rewrite-uri template="#(String.Concat(context.Request.OriginalUrl.ToString(),"/", "MapServer"))" copy-unmatched-params="false" />
</when>
<otherwise />
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Issue is not reproducable:
Related
I am trying to create a policy, that will pull the insurer ID out of the input body and put it in the URL as shown in the picture below.
Trying to clean up request body and reconstruct URL so the request can successfuly post to our approve endpoint
Expected Result:
https://apimanagement.test.com/consto-123/api/relationship/approve/{id}
Request body:
{
"insurer_name": "Tony",
"insurer_id": "12345",
"comments": "This is test"
}
You have to extract the insurer_id from the request-body and store it as a variable:
<set-variable name="insurerId" value="#{
var body = context.Request.Body.As<JObject>(true);
return body["insurer_id"].Value<string>();
}" />
Afterwards you can use rewrite-uri with this variable and forward the request to the blackened.
The complete policy:
<policies>
<inbound>
<base />
<set-variable name="insurerId" value="#{
var body = context.Request.Body.As<JObject>(true);
return body["insurer_id"].Value<string>();
}" />
<rewrite-uri template="#("/api/relationship/approve/" + context.Variables.GetValueOrDefault<string>("insurerId"))" copy-unmatched-params="false" />
<set-backend-service base-url="https://rfqapiservicey27itmeb4cf7q.azure-api.net" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
I've configured an API Management policy to retry to a second region if the first encounters an error.
However, when I disable the primary function app, based in Sydney, there are no retries recorded in Application insights - only the original failing request. There are no function calls recorded in the Melbourne function app.
I've tried a bunch of different configurations. Both region function apps are running fine.
I've got the copied the code below, is there something that I'm missing or not understanding?
Thanks in advance
API Policy
<policies>
<inbound>
<base />
<choose>
<when condition="#(context.Variables.GetValueOrDefault<int>('RetryCounter', 0) == 0)">
<set-backend-service base-url="{{syndey-function-app}}" />
</when>
<otherwise>
<set-backend-service base-url="{{melbourne-function-app}}" />
</otherwise>
</choose>
</inbound>
<backend>
<retry count="1" interval="0" first-fast-retry="true" condition="#(
context.Response.StatusCode > 400
)">
<set-variable name="RetryCounter" value="#(context.Variables.GetValueOrDefault<int>('RetryCounter', 0) + 1)" />
<forward-request buffer-request-body="true" timeout="15"/>
</retry>
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
403 Error Response
<h1 id="unavailable">Error 403 - This web app is stopped.</h1>
<p id="tryAgain">
The web app you have attempted to reach is currently stopped and does not
accept any requests. Please try to reload the page or visit it again soon.
</p>
<p id="toAdmin">
If you are the web app administrator, please find the common 403 error
scenarios and resolution <a href="https://go.microsoft.com/fwlink/?linkid=2095007"
target="_blank">here</a>. For further troubleshooting tools and recommendations,
please visit Azure Portal.
</p>
I had to replace the single quotes to double quotes because of an error on save:
context.Variables.GetValueOrDefault<int>('RetryCounter', 0) => context.Variables.GetValueOrDefault<int>("RetryCounter", 0)
For testing I created two mocked services:
Returns status code 200:
https://rfqapiservicey27itmeb4cf7q.azure-api.net/echo/200/test
Returns status code 403:
https://rfqapiservicey27itmeb4cf7q.azure-api.net/echo/403/test
The retry section in the backend section will never return to the inbound section. So the choose condition was only hit at the very beginning.
Therefore, the choose condition has to be moved to the backend section.
<policies>
<inbound>
<base />
<set-backend-service base-url="https://rfqapiservicey27itmeb4cf7q.azure-api.net/echo/403" />
<!--
<set-backend-service base-url="{{syndey-function-app}}" />
-->
</inbound>
<backend>
<retry count="1" interval="0" first-fast-retry="true" condition="#(
context.Response.StatusCode > 400
)">
<set-variable name="RetryCounter" value="#(context.Variables.GetValueOrDefault<int>("RetryCounter", 0) + 1)" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault<int>("RetryCounter", 0) > 1)">
<set-backend-service base-url="https://rfqapiservicey27itmeb4cf7q.azure-api.net/echo/200" />
<!--
<set-backend-service base-url="{{melbourne-function-app}}" />
-->
</when>
</choose>
<forward-request buffer-request-body="true" timeout="15" />
</retry>
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
My sample operation executes as expected:
https://rfqapiservicey27itmeb4cf7q.azure-api.net/sample/test
The first hit in the backend requests the 403 resource:
This request is failing, and the retry policy sets the 200 in the backend section:
This second request is successful when will return 200 and leave the backend section:
I have been trying to change the cookie path of my Azure APIM API response. The response from the APIM looks like this Set-Cookie: ssnid=2d4cfdd62638436481130eb0fad7a889; path=/; secure; HttpOnly. I checked the transform policies available here but still haven't figured out a solution.
I haven't run this but this should generally work:
<policies>
<inbound>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<set-header name="Set-Cookie" exists-action="override">
<value>#{
var cookie = context.Response.Headers.GetValueOrDefault("Set-Cookie","");
return cookie.Replace("path=/;", "path=/myNewPath;");
}
</value>
</set-header>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
How to prevent large file requests using Azure APIM?
Example: Block any POST request having file size > 50MB
You can apply the following policy for all your APIs. For each POST request, the policy will check the body size, and if the size is above 50MB, it will return status 413 - Payload Too Large.
<policies>
<inbound>
<base />
<choose>
<when condition="#(context.Request.Method == "POST")">
<set-variable name="bodySize" value="#(context.Request.Headers["Content-Length"][0])" />
<choose>
<when condition="#(int.Parse(context.Variables.GetValueOrDefault<string>("bodySize"))<52428800)">
<!--let it pass through by doing nothing-->
</when>
<otherwise>
<return-response>
<set-status code="413" reason="Payload Too Large" />
<set-body>#{
return "Maximum allowed size for the POST requests is 52428800 bytes (50 MB). This request has size of "+ context.Variables.GetValueOrDefault<string>("bodySize") +" bytes";
}
</set-body>
</return-response>
</otherwise>
</choose>
</when>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
There are policies called quota-by-Key and quota-by-subscription , which will help the users to block the calls that exceed the bandwidth specified. Please verify this link for more details.enter link description here
Is there any way to decrypt a bearer token in an API management policy in order to create a condition it's acr_values, for example a tenant.
Looking at the MS documentation it does not seem possible, I would be looking to achieve something like:
<when condition="#(context.Request.Headers["Authorization"] --DO MAGIC HERE-- .acr_values["tenant"] == "contoso" ">
<set-backend-service base-url="http://contoso.com/api/8.2/" />
</when>
Alternatively something like the example here but for setting the backed service:
http://devjourney.com/blog/2017/03/23/extract-jwt-claims-in-azure-api-management-policy/
Documentation I've read:
https://learn.microsoft.com/en-us/azure/api-management/api-management-transformation-policies#example-4
https://learn.microsoft.com/en-us/azure/api-management/policies/authorize-request-based-on-jwt-claims?toc=api-management/toc.json#policy
Did you try .AsJwt() method (https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions#ContextVariables):
<policies>
<inbound>
<base />
<set-header name="tenant" exists-action="append">
<value>#{
var jwt = context.Request.Headers.GetValueOrDefault("Authorization").AsJwt();
return jwt?.Claims.GetValueOrDefault("tenant") ?? "unknown";
}</value>
</set-header>
<choose>
<when condition="#(context.Request.Headers.GetValueOrDefault("tenant", "unknown") == "some-tenant" )">
<set-backend-service base-url="http://contoso.com/api/8.2/" />
</when>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
Also I'm not sure if you need it as a header to backend request, if not consider using set-variable policy.
Ok so I got it working in a very hacky way, you can set vales of the decrypted token in the header and then set conditions on that header.
<policies>
<inbound>
<base />
<set-header name="tenant" exists-action="append">
<value>#{
string tenant = "unknown";
string authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
if (authHeader?.Length > 0)
{
string[] authHeaderParts = authHeader.Split(' ');
if (authHeaderParts?.Length == 2 && authHeaderParts[0].Equals("Bearer", StringComparison.InvariantCultureIgnoreCase))
{
Jwt jwt;
if (authHeaderParts[1].TryParseJwt(out jwt))
{
tenant = (jwt.Claims.GetValueOrDefault("tenant", "unknown"));
}
}
}
return tenant;
}</value>
</set-header>
<choose>
<when condition="#(context.Request.Headers.GetValueOrDefault("tenant", "unknown") == "some-tenant" )">
<set-backend-service base-url="http://contoso.com/api/8.2/" />
</when>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
A few years have passed since this has been answered, but as I found a less verbose solution, without actually modifying the request headers, i thought it would be nice to share for others:
<set-variable name="tenant" value="#{
var authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
return authHeader.AsJwt()?.Claims.GetValueOrDefault("tenant", "");
}" />
...
<choose>
<when condition="#(context.Variables.GetValueOrDefault("tenant", "") == "your-tenant-id")">