Create new operation in Azure APIM api with terraform - azure

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.

Related

Unauthorized Issue while Authenticating Azure Function with APIM

I created 1 Azure Function app (.NET Core 6) with a few functions.
Function Details:
ListUsers - GET Method with Routing(v1/listusers)
URL: https://samplefunction1.azurewebsites.net/api/v1/listusers
Step1: I registered a new Azure B2C Application for function app in B2C Tenant with Redirect URL as JWT MS (For testing purpose) and created B2C_1A_SIGNUP_SIGNIN custom policy in to get token.
Step2: I enabled Managed Identity (System Identity) ON for function app.
Step3: I provided authentication to azure function app with identity provider OPENID Connect and configured metadataURL, Client Id ,CLient Secret etc., of Step1 endpoint details
Step4: I Created APIM resource, enabled managed Identity(System Identity) and imported Function App and can see apim-Samplefunction host keys under function app keys.
Step4: I Created APIM resource and imported Function App added APIM Policy in under all operations and enabled CORS.
//Resource Id is taken from Step1 (ClientID)
<authentication-managed-identity resource="63b20196-e62b-4cf0-a60e-9e895ee5f1a2" />
Step5: I tested function URL in postman with authentication and host key and its success.
URL: GET METHOD
https://samplefunction1.azurewebsites.net/api/v1/listusers?code=<<HOST KEY Taken from Azure Function APP>>
Header:
Authorization : Bearer <<Token received from JWT.MS web page >>
APIM URL: When i tried same token with APIM Url, I am getting errorYou do not have permission to view this directory or page. with authentication-managed-identity policy with Azure Function App Client Id
I created another B2C application registration for APIM and used authentication-managed-identity policy with Azure APIM Client Id. At this time I am getting Internal Server Error.
My Queries:
Which client id I have to use to authorize APIM Url?
Does both APIM B2C & Azure Function B2C Registration is required?
Get Method
https://sample-apim-poc.azure-api.net/urlsuffix/v1/listusers
Headers:
Ocp-Apim-Subscription-Key:d40b5dfe106d40c0b234cec702173761
Authorization:Bearer <<Token received from JWT.MS web page >>
Ocp-Apim-Trace:true
Content-Type:application/json
Inbound Policy settings for All Operations:
<!--
IMPORTANT:
- Policy elements can appear only within the <inbound>, <outbound>, <backend> section elements.
- To apply a policy to the incoming request (before it is forwarded to the backend service), place a corresponding policy element within the <inbound> section element.
- To apply a policy to the outgoing response (before it is sent back to the caller), place a corresponding policy element within the <outbound> section element.
- To add a policy, place the cursor at the desired insertion point and select a policy from the sidebar.
- To remove a policy, delete the corresponding policy statement from the policy document.
- Position the <base> element within a section element to inherit all policies from the corresponding section element in the enclosing scope.
- Remove the <base> element to prevent inheriting policies from the corresponding section element in the enclosing scope.
- Policies are applied in the order of their appearance, from the top down.
- Comments within policy elements are not supported and may disappear. Place your comments between policy elements or at a higher level scope.
-->
<policies>
<inbound>
<base />
<cors allow-credentials="false">
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods>
<method>GET</method>
<method>POST</method>
</allowed-methods>
</cors>
<authentication-managed-identity resource="13b20196-e62b-4cf0-a60e-9e895ee5f1a2" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>

Set Response Header With remaining-calls Azure API Management

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.

API Management - Set Backend Service as Operation Policy in Terraform

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.

Azure API Management/Portal: activate Mocking for "Try Out" in API Portal

I was wondering if there is the possibility to activate mocking for calls to an Azure API Management based API from the API Portal.
I don't want developers who browse the interface catalog and use the "try it" functionality to actually CRUD records from/in the backends. They should only receive a set of predefined data so they can develop against it without messing up the data in the backends.
Example
API is created on Azure API Management and productive.
This API is listed in the corresponding Azure API Portal -
a developer now finds that API and wants to try it out:
POST /SalesQuote would create a Sales Quotation in SAP. A developer uses the "try it" button in the developer portal to see the behaviour. The "send" button now actually creates a record in the backend.
Can this be prevented? If so - how? Would this be something that could be achieved within the API policy (i.e. mock data for origin = api portal url)?
The only other option I can think of would be disabling the try it feature - rather a bad solution.
EDIT: general "mocking-enabled" is of course for a productive API no option.
Thanks for your help!
Thanks to Nacho I researched the policy features more deeply and played around a bit. I came up with the following:
<inbound>
<base />
<choose>
<when condition="#(context.User.Groups.Any(Group => Group.Name == "developer-internal"))">
<mock-response status-code="201" content-type="application/json" />
</when>
</choose>
</inbound>
This should enable mocking only if the calling user is member of the group "developer-internal" (which are all developer portal users via AAD mapping).
As an alternative I am validating the re-routing option to our test backend instead of mocking the response which would look like this:
<inbound>
<base />
<choose>
<when condition="#(context.User.Groups.Any(Group => Group.Name == "developer-internal"))">
<!--mock-response status-code="201" content-type="application/json" /-->
<set-backend-service base-url="https://esb-test.example.com/restv2/CostObject" />
</when>
</choose>
</inbound>
Yes you can, you have to add a policy in the outbound of the API, normally I use it to expose apis that are in development process.
Go to the "Enable response mocking" part of this link:
https://learn.microsoft.com/en-us/azure/api-management/mock-api-responses

Create request rate limiting for all API instances under APIM

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

Resources