Use Https when calling API Management

I was trying to use the Azure API Management connector in a Logic App, but I continued to get a Http 404 Resource Not Found error. I could call the very same API Management service via Postman. I could also use a Http action to call the API Management service. Question: why can’t I call the service via the Azure API Management connector?

In the end it turned out everything was OK with the API Management Service and the backend service. The only thing you will have to do, is active option Https on the service.

[box type=”info”] Activate Https when you want to call a service via the Azure API Management connector in a logic app! If you call a http endpoint you will receive a Http 404 Resource Not Found error. [/box]

Deploy Service on API Management

My colleague Eldert Grootenboer has written an excellent series of blog posts on deploying Azure API Management via Visual Studio Team Services (VSTS). This is a short summary.

API Management can be deployed in three steps.

  • Create API Management instance via ARM deployment.
  • Create users, groups and products via ARM deployment.
  • Create virtual service that you want to expose via API Management.

For now I have performed the two first steps manually, because they are one time operations. I’m not using a specific Product, but I’m just using the Starter product. After performing the first two steps, let’s turn our attention to the third step. Creating or updating API’s can be done directly in the ARM template or it can be done in the Azure Portal. I used the Azure Portal, because that seems an easier approach to me.

Next steps:

  • You can download a tool named the ARM Template Creator at this download link.
  • Compile the ARM Template Creator solution.
  • Create a PowerShell script that uses the ARM Template Creator to generate an ARM template for the API you want to expose via API Management.

The PowerShell script looks like this:

[box type=”info”]
#if you have problem with execution policy execute this in a administrator runned powershell window.
Set-ExecutionPolicy -ExecutionPolicy Unrestricted

Import-Module “.\APIManagementTemplate.dll”

#Set the name of the API Mangement instance
$apimanagementname = ‘tulipexpress’

#Set the resource group
$resourcegroupname = ‘tlx-apimgmt’
#Set the subscription id
$subscriptionid = ‘guid’
#Set the tenant to use when login ing, make sure it has the right tennant
$tenant = ‘tulipexpress.onmicrosoft.com’

#optional set filter for a specific api (using standard REST filter, with path we can select api based on the API path)
#$filter = “path eq ‘api/v1/currencyconverter'”
$filter = “path eq ‘test'”

#setting the output filename
$filenname = ‘D:\’ + $apimanagementname + ‘.json’

Get-APIManagementTemplate -APIFilters $filter -APIManagement $apimanagementname -ResourceGroup $resourcegroupname -SubscriptionId $subscriptionid -TenantName $tenant -ExportPIManagementInstance $false | Out-File $filenname

[/box]

After running the PowerShell script via the PowerShell ISE (run as administrator), the ARM template is created. The generated ARM template (json) contains a few important parameters that define the name of the API Management instance, the name of the virtual service and the name of the backend service. Note that the template also contains a few hard-coded values (like the display name of the virtual service). Replace those with parameterized values. Obviously you will also have to create a parameter file for each environment.

When using the ARM template for the first time, I could perfectly deploy the ARM template via an ARM Resource Group deployment step (don’t mind the somewhat confusing name) in VSTS. In this particular case, a policy was used to set the backend URL. That didn’t work, so I replaced it with the normal solution that sets the backend URL via the Settings tab.

A fragment of the ARM template where the service is created is shown below:

[box type=”info”]

“resources”: [
{
“comments”: “Generated for resource /subscriptions/3c855a42-60cd-42c5-b343-f46528d6a24b/resourceGroups/FEYE-TST-API/providers/Microsoft.ApiManagement/service/feye-api-mgt-tst/apis/feye-api-external-tst”,
“type”: “Microsoft.ApiManagement/service/apis”,
“name”: “[concat(parameters(‘api-mgt_name’), ‘/’ ,parameters(‘api__name’))]”,
“apiVersion”: “2017-03-01”,
“properties”: {
“displayName”: “[parameters(‘api_name’)]”<- parameterized, not hard-coded
“apiRevision”: “[parameters(‘api_apiRevision’)]”,
“description”: “”,
“serviceUrl”: “[parameters(‘api_serviceUrl’)]”, <- url to the backend-service (no policy)
“path”: “test”,
“protocols”: [
“https”
],
“authenticationSettings”: {
“oAuth2”: null,
“openid”: null
},
“subscriptionKeyParameterNames”: {
“header”: “Ocp-Apim-Subscription-Key”,
“query”: “subscription-key”
},
“isCurrent”: “[parameters(‘feye-api-external-tst_isCurrent’)]”
}

[/box]

Use basic authentication with Azure API Management

I developed the habit to unlock Azure App Services using so-called Gateway Services. As the name implies, Gateway Services are nothing more than gatekeepers. They have a fixed set of responsibilities: Give customers authorized access using basic authentication, store the posted entity in original format (Azure Storage tables for XML/Json, blobs for file attachments) and send an event message to a queue to kick off the process in a decoupled way.

I first implemented the Gateway Services via custom coding. I created a separate Web App for each customer. One customer, one Gateway. After a while I realized I could implement the Gateway Service via a Logic App using out-of-the-box API’s (so without any custom coding): Request, Azure Storage Tables and Blobs (with looping for attachments), Azure Storage Queue, Response. Fair enough. The only remaining responsibility for the Web App was to call the Logic App and apply basic authentication. The next step was to call the Logic App from Azure API Management. You can’t miss the option to import a new API from a Logic App. Nice. Now there was only one problem left. How to perform basic authentication?

When using Azure Active Directory and ADFS 3.0 you need to define an Authorization Server. You can also use a OAuth 2.0 bearer token for external identity providers like Microsoft and Google. But the Security section is not what we need here. For basic authenication, you can use an inbound policy: check-header.

[box type=”info”]
<policies>
<inbound>
<base />
<check-header name=”Authorization”
failed-check-httpcode=”401″
failed-check-error-message=”Not authorized”
ignore-case=”false”>
<value>Basic a2xhbnQ6V2Vsa29tMjAxOA==</value>
</check-header>
[/box]

The Authorization header looks quite complicated, but you can use an on-line tool or base64encode.org to generate the header. The basic authentication header is a base64 encoded string with format username:password.

When testing the API Management call to the logic app with the above policy applied, I received a rather cryptic error:
{
“error”: {
“code”: “DirectApiAuthorizationRequired”,
“message”: “The request must be authenticated only by Shared Access scheme.”
}
}

Using this excellent blog, I found out that was due to the fact that Logic Apps are not able to handle the Authorization HTTP header. So, I had to find a way to remove the Authorization header after authentication/authorization. Luckily enough that’s easy. You need to add another policy to the inbound section:

[box type=”warning”]
<set-header name=”Authorization” exists-action=”delete”/>
[/box]

Example policy implementation

I have an example policy implementation which can be used as a reference for future implementations. No rocket science, but just an example:

<policies>
<inbound>
<set-variable name=”message-id” value=”@(Guid.NewGuid())” />
<set-variable name=”operation” value=”@(context.Operation.Name)” />
<log-to-eventhub logger-id=”loghuapimtest” partition-id=”0″>
@(string.Join(“;”, “MessageId”, “Req_” + context.Variables[“message-id”],
DateTime.UtcNow.ToString(“dd-MM-yyyy hh:mm:ss.FFF”), “ApimUrl”, context.Request.OriginalUrl,
“BackendUrl”, context.Request.Url, “User”, context.User.Email, “CallerIP”,
context.Request.IpAddress))
</log-to-eventhub>
<rate-limit-by-key calls=”10″ renewal-period=”60″ counter-key=”@(context.Request.IpAddress)” />
<cache-lookup vary-by-developer=”false” vary-by-developer-groups=”false” allow-private-response-caching=”true” downstream-caching-type=”public” must-revalidate=”true”>
<vary-by-header>Authorization</vary-by-header>
</cache-lookup>
</inbound>
<backend>
<forward-request follow-redirects=”true” />
</backend>
<outbound>
<log-to-eventhub logger-id=”loghuapimtest” partition-id=”0″>
@( string.Join(“;”, “MessageId”, “Resp_” + context.Variables[“message-id”],
DateTime.UtcNow.ToString(“dd-MM-yyyy hh:mm:ss.FFF”), “ApimUrl”, context.Request.OriginalUrl,
“BackendUrl”, context.Request.Url, “StatusCode”, context.Response.StatusCode,
“Description”, context.Response.StatusReason))
</log-to-eventhub>
<cache-store duration=”60″ />
</outbound>
<on-error>
<log-to-eventhub logger-id=”loghuapimtest” partition-id=”0″>
@(string.Join(“;”, DateTime.UtcNow.ToString(“dd-MM-yyyy hh:mm:ss.FFF”),
“ApimUrl”, context.Request.OriginalUrl, “BackendUrl”, context.Request.Url,
“ErrorMsg”, context.LastError.Message, “ErrorScope”, context.LastError.Scope, “ErrorSection”, context.LastError.Section))
</log-to-eventhub>
<return-response>
<set-status code=”500″ reason=”API Management Error” />
<set-body>
@{ string inBody = context.LastError.Message;
inBody = inBody + ” (” + context.LastError.Reason + “)”;
inBody = inBody + “\n” + “APIM Url: ” + context.Request.OriginalUrl;
inBody = inBody + “\n” + “Backend Url: ” + context.Request.Url;
return inBody; }
</set-body>
</return-response>
</on-error>
</policies>

Use Google accounts in Azure API Management

Go to the Google Developer Console: https://console.developers.google.com/

Google DevCenter

Log in with your Google account, for instance: thinkshareintegrate@gmail.com.

Add a new project, i.e. twocentsapimtest. Creating a project is required for being able to enable the following two APIs (also a prerequisite):

  • Contacts API
  • Google+ API

Select the Credentials menu and then Create Credentials / OAuth Client ID.

Select Web Application and enter a name like TwoCentsAPIMTest. Set the authorized redirect uri to the redirect URL in API Management (tab Security/Identities). Example: https://twocentsapimtest.portal.azure-api.net/signin-google.

Both a clientid and a client secret are created:

  • Client-id: 799240336274-buhj2g04h9kh5ulibqkm6o5tpveb1tg4.apps.googleusercontent.com
  • Client secret: Jtpub1g4H4NV15FW218ejSsM

You will have to enter these data in API Management (tab Security/Identities).

Azure API Management

Today I gave a presentation on Azure API Management and had to report the following shortcomings:

  • Azure API Management only runs in Azure, not on premise
  • No support for SOAP webservices
  • There is governance, but no SOA Repository. That means: no service versioning and no lifecycle management
  • Limited extensibility/customization. You can for instance transform XML to JSON in an outbound policy, but you can’t perform a message transform or any other pipeline type of processing.
  • You can’t expose multiple backend webservices via one virtual service

I also compared Azure API Management to Sentinet.

Sentinet
– Professional, mature product

But
– Key feature of policy injection via WCF bindings
– Can run in the cloud, but only when being installed on a VM (IaaS)
– Limited to Microsoft platform
– Expensive

Azure API Management
– Offers API Management at a low price with low risk (cloud model)

But:
– Limited functionality (compared to Sentinet)