Recently, I created a custom Logic Apps connector within Azure API Management. This blog explains the choices I needed to make and the challenges I’ve faced.
Scenario
In some scenarios, you need to create a custom connector for Logic Apps. You can leverage the Azure API Management consumption tier to achieve this, in case your backend system has an API. Creating your custom connector like this, comes with some advantages:
- Better performance: you control the throttling, not Microsoft
- Full control: you control the complete lifecycle of the connector, not Microsoft
- Better security: you can leverage Managed Service Identity to connect to systems that support it
- Cheaper: you only pay for native Logic Apps actions and the first 1M APIM executions are free
Swagger
In the end, you need to create a Swagger definition that is optimized for Logic Apps. This Visual Studio Code extension was very helpful during the development, as you can immediately preview the Swagger UI result. For Logic Apps, these are the tweaks you can do:
- Provide a summary for each operation
- Provide a description for each operation and request/response parameter
- Provide an x-ms-summary for each request/response parameter
- Provide an x-ms-visibility to control the user experience
Parameters
In my case, I’ve created a connector for Azure Data Lake Store Gen2. The most logical way to pass parameters would have been through the uri path (e.g. api/{FileSystemName}/{FolderPath}/{FileName}). However, because the FolderPath potentially contains slashes – which is not supported in Open API specifications – I needed to find another solution. Most Logic Apps connectors use body parameters, but this forces base64 encoding/decoding of the file content. I prefered not the touch the body content, so the HTTP headers were my only option.
APIM subscription key
I wanted to rely on the default APIM subscription key frontdoor security. Swagger has out-of-the-box support for describing this kind of api-key security, however it turned out that Logic Apps does not support this. As an alternative, I decided to add this as a property to each operation.
If you have a detailed look, you see that the APIM subscription key is passed via the HTTP Authorization header. Why? Because Logic Apps obfuscates this HTTP header by default.
Of course, you also need to configure API Management to use this HTTP header for the subscription key. This can be done in the API settings.
Swagger endpoint
I was in the need for an endpoint to host the Swagger definition. This endpoint should not be secured, so that’s why this had to be hosted in a separate API. CORS must be allowed and the Swagger definition is returned as a mocked response. Let me know if you know a better option!
<inbound> <base /> <cors> <allowed-origins> <origin>*</origin> </allowed-origins> <allowed-methods> <method>GET</method> </allowed-methods> <allowed-headers> <header>*</header> </allowed-headers> </cors> <mock-response status-code="200" content-type="application/json" /> </inbound>
Backend security
In my scenario, I wanted to leverage Managed Service Identity to authenticate against Azure Data Lake Store. I needed to reuse the bearer token in the backend call, but also in a send-request policy. That why I’ve requested the MSI token once and stored it as a bearer token in a variable.
<authentication-managed-identity resource="https://storage.azure.com/" output-token-variable-name="msiToken" ignore-error="false" /> <set-variable name="adBearerToken" value="@("Bearer " + (string)context.Variables["msiToken"])" />
Conclusion
My custom connector is nothing more than a good Swagger definition, with some API Management policies in the back that perform the conversions towards the backend API’s. I had to take some shortcuts, which are described in the blog. Do you know other ways to achieve similar things? Please let me know in the comments section!
Thank you
Toon