I'm creating an APIM in Azure using Terraform. So far I've been able to create the APIM instance, the API and an operation within the API. Because I want each operation for the API to point to an individual Logic App, my understanding is I cannot set this as an azurerm_api_management_backend and instead need to set it in the operation policy in XML.
This is what my operation policy looks like:
resource "azurerm_api_management_api_operation_policy" "apim1_ss_cmpcomplaints_api_dev_get_policy" {
api_name = azurerm_api_management_api.apim1_ss_api_dev.name
api_management_name = azurerm_api_management.test-apimManagement.name
resource_group_name = azurerm_resource_group.apimResourceGroup.name
operation_id = "get-complaints"
xml_content = <<XML
<policies>
<inbound>
<base />
<set-method id="apim-generated-policy">GET</set-method>
<set-backend-service id="apim-generated-policy" backend-id="/subscriptions/xxx/resourceGroups/hm-iac-msdn-neu-rg/providers/Microsoft.Logic/workflows/testLogicApp" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
XML
}
I have created the Logic App and retrieved the resource ID from the Azure CLI and included it in the set-backend-service node. But despite getting the ID from the CLI, I am getting the following response:
│ Error: creating or updating API Operation Policy (Resource Group "apim-resource-group" / API Management Service "harry-test-apim" / API "test_api_dev" / Operation "get-complaints"): apimanagement.APIOperationPolicyClient#CreateOrUpdate: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="ValidationError" Message="One or more fields contain incorrect values:" Details=[{"code":"ValidationError","message":"Error in element 'set-backend-service' on line 5, column 10: Backend with id '/subscriptions/xxx/resourceGroups/hm-iac-msdn-neu-rg/providers/Microsoft.Logic/workflows/testLogicApp' could not be found.","target":"set-backend-service"}]
I'd prefer to set this using the resource ID instead of using base-url.
The backend-id is different to the ARM id. It's not easy to find, so the solution is to generate the policy in the APIM front-end and then apply that in code afterwards.
Related
I have created an Azure Service Fabric application and deployed to the Azure cluster. I have created an API Management service, created Backend pointing to my Service Fabric Instance. I am having a simple GET API in the service fabric. I have created inbound processing policy like below, but its not working - getting 500 internal server error.
<policies>
<inbound>
<base />
<set-backend-service backend-id="dev-sfc-backend" sf-resolve-condition="#(context.LastError?.Reason == "BackendConnectionFailure")" sf-service-instance-name="fabric:/M_HelloModelServices/M_HelloModelService" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
Below is the trace while testing API.
service-fabric-backend (1,014.098 ms){
"messages": [
"Unable to connect to the remote server",
"Error occured while calling backend service.",
"No connection could be made because the target machine actively refused it <IP>:20000"
]}
Could someone configured API Management service for Service Fabric Cluster please share how we can configure this.
I'm using Azure API Management with some rate limiting based on subscription. I need to send to the user in the response headers the number of remaining calls. I know that I should set some values in the outbound policy but I do not know how to do it exactly. This is my policy XML if any one can help.
<policies>
<inbound>
<base />
<set-variable name="remainingCalls" value="remaining-calls-variable-name" />
<quota-by-key calls="5" renewal-period="86400" counter-key="#(context.Subscription?.Key ?? "anonymous")" increment-condition="#(context.Response.StatusCode >= 200 && context.Response.StatusCode < 300)" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<set-header name="remainingCalls" exists-action="append">
<value>#(context.Response.Headers.GetValueOrDefault("remaining-calls-header-name","2"))</value>
</set-header>
</outbound>
<on-error>
<base />
</on-error>
</policies>
As per the Azure Documentation, You can set rate-limit by subscription only in inbound section & the policy scope should be either product, api or operation.
Here is the sample example, where the per subscription rate limit is 30 calls per 90 seconds. After each policy execution, the remaining calls allowed in the time period are stored in the variable remainingCallsPerSubscription.
<policies>
<inbound>
<base />
<rate-limit calls="30" renewal-period="90" remaining-calls-variable-name="remainingCallsPerSubscription"/>
</inbound>
<outbound>
<base />
</outbound>
</policies>
Note: This policy can be used only once per policy document.
Policy expressions cannot be used in any of the policy attributes for this policy.
I've contacted Microsoft Azure support for this request and they were able to guid me to a possible workaround that may be helpful and in my particular use case it is good solution. For quota policy and as mentioned by #Venkatesh-MAT it is not supported to retrieve remaining quota information in response header as rate-limit policy. However there is a separate REST API for this purpose. This is documentation for the same https://learn.microsoft.com/en-us/rest/api/apimanagement/current-ga/quota-by-counter-keys/list-by-service.
The API in this documentation requires bearer token as authentication. To be able to generate the bearer token you can simply use azure cli to get token for the resource using command az account get-access-token --resource https://management.azure.com or if you need to do it programmatically you have to follow below steps:
Set principle role using azure cli with subscription scope to create service principle that have access on this resource scope (az ad sp create-for-rbac -n "principle-1" --role contributor –scopes /subscriptions/{subscriptionID}/resourceGroups/{resourcegroup}/providers/Microsoft.ApiManagement/service/{API management Service name} /quotas/{subscription key})
Use Client ID, client secret & tenant ID generated from above step to call this API https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token with body type x-www-form-urlencoded and body key value as below
KEY: grant_type VALUE: client_credentials
KEY: client_id VALUE: appid generated from step number 1
KEY: scope VALUE: https://management.azure.com/.default
KEY: client_secret VALUE: password generated from step number 1
Then use the output access token to get quota policy consumption.
I already created an API in Azure with folowing code.
resource "azurerm_api_management_api" "api-foo" {
name = "Example"
resource_group_name = data.azurerm_resource_group.rg.name
api_management_name = data.azurerm_api_management.apim_service.name
revision = "1"
display_name = "example-foo"
path = "apifoo"
protocols = ["https", "http"]
description = "some text here"
import {
content_format = "openapi-link"
content_value = "https://publicapi.azurewebsites.net/swagger/v1/swagger.json"
}
}
Also a root level policy.
resource "azurerm_api_management_api_policy" "api-foo" {
api_name = azurerm_api_management_api.api-foo.name
api_management_name = data.azurerm_api_management.apim_service.name
resource_group_name = data.azurerm_resource_group.rg.name
#operation_id = azurerm_api_management_api_operation.api-foo.operation_id
xml_content = <<XML
<policies>
<inbound>
<set-backend-service base-url="https://publicapi.azurewebsites.net" />
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
XML
}
Now I have more swager definitions with new enpoints and I want to add them in same Api but I don´t know how to achieve this with terraform. Also don´t want to remove the one I already have. If I replace content_value with new swager file I will remove the one I already have.
Is there a way to achieve this? Also, how can I get the Operation Id in the output after it gets created? I would want that The ID of new operation get asociated to some policy but not as a root level. Just operation policy level (a new module will have to be created for this new policy)
You cannot add a different swagger/openapi definition to the same api in an Azure api management instance. By importing an api definition, the api operations are automatically generated by the Azure api management service below the api that you selected as the target.
You can either create a new api in the same Azure api management instance (with a different api suffix than the first one) and import your new definition there, or you can manually combine the api definitions into one file (assuming the apis share the same base api URLs) and replace your existing api with it.
A third but ugly option would be to add the operations from your 2nd api definition manually below the first api in your Azure api management instance, but this will be cumbersome as you also have to manually adjust the policy for each operation to point its backend to the 2nd api definition base URL.
Is it possible to create a policy that limites request rate over a period for all API instances?
Can the policy below be used for the requirement?
Please note I want the policy to be define in one place (global, that is, "All APIs"), not repeated for every API instance.
<policies>
<inbound>
<base />
<rate-limit calls="20" renewal-period="90" />
</inbound>
<outbound>
<base />
</outbound>
</policies>
Policy sections: inbound
Policy scopes: product
https://learn.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies#LimitCallRate
yes. You can add this policy inside "ALL APIS" policy section. It will get effect for all the APIs. Its's correct
I am trying to deal with an Backend API(REST) with no swagger documents to be accessed through Azure APIM.
I want all the calls directed to the Backend API with me not manually creating GET/PUT/POST for all the resources. My inbound policies for all operations is below
'
<policies>
<inbound>
<set-variable name="requestPath" value="#(context.Request.Url.Path)" />
<base/>
<set-backend-service base-url="https://*****/****/" />
<rewrite-uri template="#(context.Request.Url.Path)" copy-unmatched-params="true" />
</inbound>
<backend>
<base/>
</backend>
<outbound>
<base/>
</outbound>
</policies>
`
However when hit the APIM i was given
{
"statusCode": 404,
"message": "Resource not found"
}
Any help is appreciated Thanks
The easy way to do this is to create one operation for each HTTP method you support and use /* for the template. This will match any inbound path/query and forward it to the backend.