Azure API Management Policy- "rewrite-url" policy - azure

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>

Related

azure apim import policy - Too many characters in character literal

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:

Azure API Management Policy(Rewrite URI policy)- Template and Query Parameters

I require some help on doing a rewrite uri policy. I was able to extract information from payload and change the initial request URL. Have extracted the insurer_id from body and passed it to my backend -> mybackend.com/api/relationship/deny/{insurer_id}
I was able to extract it and send it to my backend, I have a bit of a trouble formatting once the request has been posted
I have a current payload:
{
"insurer_id": "22112",
"insurer_name": "Steve Rogers",
"status_code: [ " "Deny\",",
],
"additionalComments": "This is a test"
}
This is my Current code:
<policies>
<inbound>
<base />
<set-variable name="insurerId" value="#{
var body = context.Request.Body.As<JObject>(true);
return body["insurer_id"].Value<string>();
}" />
<set-variable name="status_code" value="#{
return context.Request.Body.As<JObject>(preserveContent:true)["status_code"].ToString(Newtonsoft.Json.Formatting.None);
}" />
<rewrite-uri template="#("/api/relationship/deny/" + context.Variables.GetValueOrDefault<string>("insurerId") + "?denyReason=" + context.Variables.GetValueOrDefault<string>("status_code"))" copy-unmatched-params="false" />
<set-backend-service base-url="https://testbackend.azure-api.net" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
What I am trying to achieve works, but when I do a post, the payload doesn't look what is to be expected.
Expected Payload:
{
"insurer_id": "22112",
"insurer_name": "Steve Rogers",
"status_code": "Deny",
"additionalComments": "This is a test"
}
Unformatted payload:
{
"insurer_id": "22112",
"insurer_name": "Steve Rogers",
"status_code": "[\"Deny\"]",
"additionalComments": "This is a test"
}
You have to read the first value of your JArray and assign it to the variable:
<set-variable name="status_code" value="#{
var body = context.Request.Body.As<JObject>(true);
var statusCode = body["status_code"] as JArray;
if(statusCode != null && statusCode.Count > 0)
{
return statusCode.FirstOrDefault();
}
return null;
}" />
But therefore you have to fix your invalid JSON:
{
"insurer_id": "22112",
"insurer_name": "Steve Rogers",
"status_code": [ "Deny"],
"additionalComments": "This is a test"
}
A query string consists of a key-value pair:
https://example.com/over/there?name=ferret
name is the key
ferret is the value for it
In your example, there's no key:
? + denyReason=context.Variables.GetValueOrDefault<string>("status_code")
and also the ? is a string. So it has to be put between quotation marks.
Fixed with sample query-key status_code:
<rewrite-uri template="#("/api/relationship/deny/" + context.Variables.GetValueOrDefault<string>("insurerId") + "?statuscode=" + context.Variables.GetValueOrDefault<string>("status_code"))" copy-unmatched-params="false" />
Complete policy:
<policies>
<inbound>
<base />
<set-variable name="insurerId" value="#{
var body = context.Request.Body.As<JObject>(true);
return body["insurer_id"].Value<string>();
}" />
<set-variable name="status_code" value="#{
var body = context.Request.Body.As<JObject>(true);
var statusCode = body["status_code"] as JArray;
if(statusCode != null && statusCode.Count > 0)
{
return statusCode.FirstOrDefault();
}
return null;
}" />
<rewrite-uri template="#("/api/relationship/deny/" + context.Variables.GetValueOrDefault<string>("insurerId") + "?statuscode=" + context.Variables.GetValueOrDefault<string>("status_code"))" copy-unmatched-params="false" />
<set-backend-service base-url="https://testbackend.azure-api.net" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>

How to use a variable when returning response in policy definition?

I'm configuring inbound policies in an instance of Azure API Management.
First, I set a variable:
<set-variable name="var1" value="" />
Then I send a request
<send-request mode="new" response-variable-name="var1" timeout="20" ignore-error="false">
Which returns a JSON. When testing I get the following message in trace tab:
GET request to 'https://my-api.azure-api.net/api/data' has been sent, result stored in 'var1' variable.
I guess the send-request policy works and the result is stored in the variable.
Then I want to return a response (still in inbound, I get 500 when trying to do it in outbound):
<return-response response-variable-name="existing response variable">
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>
{
"success": true,
"var1": context.Variables["var1"]
}
</set-body>
</return-response>
My problem is it doesn't work... It just renders context.Variables["var1"].
And so does:
#context.Variables["var1"]
#{ context.Variables.GetValueOrDefault<string>("var1") }
#context.Variables.GetValueOrDefault("var1")
All of them are rendered as written, no value is being extracted.
Edit: I also tried adding a placeholder string and then using
<find-and-replace from="Placeholder" to="context.Variables.GetValueOrDefault("var1")" />
And try to place it in inbound and outbound alike. But this policy did not launch.
It's a JSON object that I want to append to the response (small detail: in reality I have this issue with multiple variables).
My question is: how can I add my declared variable to the response?
There are two ways you can go about this. You could to use policy expressions for that: https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions. The thing to remember is that they can only be used to construct whole value for policy, not part of it, so:
<set-body>#("{\"success\": true, \"var1\": " + ((IResponse)context.Variables["var1"]).Body.As<string>() + "}"</set-body>
Or with set-body policy you could use liquid template:
<set-variable name="var1body" value="#((IResponse)context.Variables["var1"]).Body.As<string>())" />
<set-body template="liquid">
{
"success": true,
"var1": {{context.Variables["var1body"]}}
}
</set-body>
I assume you have some sidecar request going on additionally to your main request flow.
This sample adds the response from send-request to the response body of the main request:
<policies>
<inbound>
<base />
<!-- main request -->
<set-backend-service base-url="https://reqres.in" />
<rewrite-uri template="/api/users/2" />
<!-- sidecar request -->
<send-request mode="new" response-variable-name="var1" timeout="20" ignore-error="true">
<set-url>https://reqres.in/api/unkown/2</set-url>
<set-method>GET</set-method>
</send-request>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<set-body template="none">#{
var body = context.Response.Body.As<JObject>(true);
body["var1"] = ((IResponse)context.Variables["var1"]).Body.As<JObject>();
return body.ToString();
}</set-body>
</outbound>
<on-error>
<base />
</on-error>
</policies>

Get Base URL and that with version identifier via Azure APIM Policy

Is it possible to, within policy, get the base url and that with version hightlighted below:
A url is needed like below:
#(base url with version identifier)
Above is used in find-and-replace element below:
<policies>
<inbound>
<base />
<set-backend-service base-url="https://my.com/oidc" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<find-and-replace from="https://thirdparty/certs"
to="#(base url with version identifier)/certs" />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Update
How can I get the base url too? The reason is that sometimes, the versioned url is not specified, in this case, only the base url is used to repalce.
https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions#ref-iurl
You can get the base url with version using the below code snippet
#{
var methodRoute = context.Request.Url.ToString().Replace(context.Api.ServiceUrl.ToString(),"");
var frontEndServiceUrl = context.Request.OriginalUrl.ToString().Replace(methodRoute,"");
}

Decrypt bearer token in Azure API Management to get acr_values

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")">

Resources