Azure Blob List "Value for one of the query parameters specified in the request URI is invalid" when using API Management service - azure

I'm using API Management service to make a simple interface between a c# desktop application and a Azure Blob storage account.
API Management service is providing the authentication via a Subscription Key. This works fine for downloading blobs.
I also want to list the contents of an individual container / folder (prefix) using the same method.
I've tested the following HTTP call outside the API Management service, it works correctly.
https://<account>.blob.core.windows.net/files?restype=directory&comp=list&prefix=globalpackages/1.0
When I use this in API Management service, I'm returned an error:
<Error>
<Code>InvalidQueryParameterValue</Code>
<Message>Value for one of the query parameters specified in the request URI is invalid.
RequestId:917b0aa8-101e-006b-0709-8e85b0000000
Time:2022-07-02T11:47:29.0834806Z</Message>
<QueryParameterName>comp</QueryParameterName>
<QueryParameterValue>list</QueryParameterValue>
<Reason />
</Error>
My processing rules:
<policies>
<inbound>
<base />
<set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" />
<set-header name="Sec-Fetch-Site" exists-action="delete" />
<set-header name="Sec-Fetch-Mode" exists-action="delete" />
<set-header name="Sec-Fetch-Dest" exists-action="delete" />
<set-header name="Accept" exists-action="delete" />
<set-header name="Accept-Encoding" exists-action="delete" />
<set-header name="Referer" exists-action="delete" />
<set-header name="X-Forwarded-For" exists-action="delete" />
<set-header name="x-ms-version" exists-action="override">
<value>#{string version = "2017-11-09"; return version;}</value>
</set-header>
<set-backend-service base-url="#{
return String.Format("https://<storageaccount>.blob.core.windows.net/files?restype=directory&comp=list&prefix=globalpackages/1.0");
}" />
<authentication-managed-identity resource="https://storage.azure.com/" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
what am I doing wrong?

Based on the documentation available here, the query parameter should be restype=container instead of restype=directory if you need to list blobs inside a blob container.
Once you make this change, you should be able to list the blobs.

Related

URL Based routing on Azure

I am trying to do URL based routing for my APIs but I am not able to achieve as my API endpoints contain a wildcard in the middle of the URL, like:
/prodapi/v1.0/{appId}/level
/prodapi/v1.0/{appId}/products
appId is my customers white label ID, so it's unique for all our customers.
So far I have tried :
Azure application gateway [ But you can only handle at the end of your URL]
Azure Fron door [Have the same settings]
API management [ Not allowing me to do wildcard]
Can someone help me with any azure native or Out of the box solution.
If you want to route the traffic depending on a specific path parameter, you could use Api management service and write a inbound policy as such:
<policies>
<inbound>
<base />
<set-variable name="appId" value="#(context.Request.MatchedParameters["appId"])" />
<choose>
<when condition="#(int.Parse(context.Variables.GetValueOrDefault<string>("appId")) == 1)">
<set-backend-service base-url="https://google.com/" />
</when>
<when condition="#(int.Parse(context.Variables.GetValueOrDefault<string>("appId")) == 2)">
<set-backend-service base-url="https://twitter.com/" />
</when>
<otherwise>
<set-backend-service base-url="https://facebook.com/" />
</otherwise>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Replacing the backend-service with your prefer url ofc :)
API management is very powerful, so I'm sure that you'll find the solution there if this is not what you were looking for.

Azure API Management: Adding multiple Backend policies not allowed

I'm trying to add multiple Backend policies to my Azure APIM Management endpoint, but I'm getting an error that I can only have one policy: "Error in element 'backend' on line xx, column 6: backend section allows only one policy to be specified"
<backend>
<forward-request />
<set-header name="Content-Type" exists-action="append">
<value>application/json</value>
</set-header>
<set-header name="x-correlation-id" exists-action="append">
<value>asdf-qwer-1234-zxcv</value>
</set-header>
</backend>
Is it possible to set multiple backend policies? If so, how can I do this? If not, what are some possible workarounds?
Thank you Vova and DeepDave-MT for the quick responses! Those are similar issues, but not necessarily related to multiple backend policies, rather related to having multiple backends.
We figured it out! A co-worker recommended moving the policies to the Inbound policies. This is rather interesting and wouldn't have guessed that.
Here's an example of what did the trick:
<policies>
<inbound>
...
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-header name="x-correlation-id" exists-action="override">
<value>asdf-qwer-1234-zxcv</value>
</set-header>
</inbound>
<backend>
<forward-request />
</backend>
<outbound>
...
</outbound>
<on-error></on-error>
</policies>

Azure API Management Conditional outbound headers

I'm using API Management to set outbound headers which was sufficient till now with a single domain using this policy block
<outbound>
<base />
<set-header name="Access-Control-Allow-Origin" exists-action="override">
<value>https://example.com</value>
</set-header>
</outbound>
However I now need to manage multiple domains for Access-Control-Allow-Origin and based on an approved list of domains. I have tried to use a conditional block but this does not work.
<outbound>
<base />
<choose>
<when condition="#(context.Request.OriginalUrl.Host == "example.com")">
<return-response>
<set-header name="Access-Control-Allow-Origin" exists-action="override">
<value>https://example.com</value>
</set-header>
</return-response>
</when>
<when condition="#(context.Request.OriginalUrl.Host == "example1.com")">
<return-response>
<set-header name="Access-Control-Allow-Origin" exists-action="override">
<value>https://example1.com</value>
</set-header>
</return-response>
</when>
<otherwise />
</choose>
</outbound>
We have an increasing list of domains which need to be handled to send a response back to the client with the header if domain from the request matches the list. Is there a better way to handle this and how can I make this work?
The CORS Policy allows listing multiple allowed origins and takes care of the response headers based on the request.

How to pass the inbound request body to the backend service in Azure API Gateway

I am trying to generate the Active Directory Token via the Azure API gateway. For that i have created a API operation on my Azure API Gateway which accepts the following body parama.
{
"client_id" :"****************",
"scope":"https://graph.windows.net/.default",
"client_secret":"****************",
"grant_type":"client_credentials"
}
Whenever I try to test this the body is set for the inboud process but not able to forward the same to the backend service which is https://login.microsoftonline.com/{{ID}}/oauth2/v2.0/token/ so I modified my inboud policy as below but still no luck.
<set-method>POST</set-method>
<set-variable name="requestBodyData" value="#(context.Request.Body.As<string>(preserveContent: true))" />
<set-header name="Content-Type" exists-action="override">
<value>"application/x-www-form-urlencoded"</value>
</set-header>
<rewrite-uri template="/" />
<set-body>#{
return "client_id=*******&scope=https://graph.windows.net/.default&client_secret=*******&grant_type=client_credentials";
}</set-body>
<!-- Don't expose APIM subscription key to the backend. -->
<set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" />
Any leads would be appriciated.
Set <set-backend-service base-url="https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token" /> under inbound attribute.
Here is a similar issue you could refer to.
Got it , removed the headed and the method from the inbound policy and updated the inbound policy as :-
<set-variable name="client_id" value="********" />
<set-variable name="scope" value="********"" />
<set-variable name="client_secret" value="********" />
<set-variable name="grant_type" value="********" />
<rewrite-uri template="/" />
<set-body>#{
return "client_id="+(context.Variables["client_id"])+"&scope="+(context.Variables["scope"])+"&client_secret="+(context.Variables["client_secret"])+"&grant_type="+(context.Variables["grant_type"]);
}</set-body>
<!-- Don't expose APIM subscription key to the backend. -->
<set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" />

Can Azure Api Management expose OpenAPI documentation?

We have some Azure Functions exposed through Api Management? Can Api Management expose a /swagger endpoint automatically, the same way the Swashbuckle package does for api's in Asp.Net.
Azure API management cannot automatically generate the swagger page. Azure API management only can provide you the API definition file. Then you can use other tools (such as Swagger UI) with the definition file to generate the page you need.
Besides, Azure API management has provided you the UI(https://youapimanagementname.portal.azure-api.net) to tell you how to use all the APIs.
You can expose your openapi documentation through the API itself.
The documentation of an API can be requested on
https://management.azure.com/subscriptions/[subscriptionid]/resourceGroups/[resourcegroupname]/Microsoft.ApiManagement/service/[servicename]/apis/[apiid]?export=true&format=openapi&api-version=2021-01-01-preview
Just create an additional operation (ex. openapi.yaml) on your API, call the url above through a custom policy and return the result. You can use the following policy
<policies>
<inbound>
<base />
<send-request mode="new" response-variable-name="result" timeout="300" ignore-error="false">
<set-url>#("https://management.azure.com/subscriptions/{{azure-subscriptionid}}/resourceGroups/{{azure-resourcegroup}}/providers/Microsoft.ApiManagement/service/" + context.Deployment.ServiceName + "/apis/" + context.Api.Id + "?export=true&format=openapi&api-version=2021-01-01-preview")</set-url>
<set-method>GET</set-method>
<authentication-managed-identity resource="https://management.azure.com/" />
</send-request>
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/yaml</value>
</set-header>
<set-body>#((string)(((IResponse)context.Variables["result"]).Body.As<JObject>()["value"]))</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
More info can be found on https://www.devprotocol.com/2021/07/20/expose-openapi-documentation-on-azure-api-management.html

Resources