Easily configure Azure API Management with bicep and az-cli
A while ago Tom Meulenberg wrote a blogpost about API Management and the challenges he faced. With this blogpost I will help him make things easier next time by splitting managing the configuration of API Management from defining the api’s.
One of his observations is the verbosity of the arm-template. That is mainly caused by all the operations he added in the template. Using a new (but still under development) az-cli subcommand (apim) these operations can be removed from the template, making the template a lot cleaner. This requires a clean separation between your api definition (all endpoints) and your api configuration allowing you to update your api-spec (using open-api yaml) without adjusting the configuration (ie. policies).
API Management concepts
In the first version of this blog I took knowledge about API Management for granted and left my reviewers puzzled. Let’s take one step back and explain some concepts before diving into details.
Operations, APIs, Products and subscriptions
- Operations define actions on a resource (like HTTP POST /user to create a user)
- APIs group operations that belong together
- Product can be linked to one or more API’s
- Subscription (the right to consume an API) are applied to a product
As Microsoft states here, policies are a collection of statement that change the API behavior through configuration. Popular statements include:
- Format conversion from XML to JSON
- Rate limiting to restrict the number of incoming calls
- Add an http-header (like an API key) on the request to the backend
- Forward authentication data (like bearer tokens) to the backend
- Remove sensitive information from the response
Policies can be configured globally (on all APIs) or at the scope of a product, api or operation. The superset of policies are applied to sequentially to a request or response. API Management allows for deterministic ordering of the combined policy statements via the
<policies> <inbound> <rate-limit-by-key calls="150" renewal-period="60" counter-key="@(context.Subscription?.Key ?? "anonymous")" /> <base /> <set-header name="X-Forwarded-Uri" exists-action="override"> <value>@(context.Request.OriginalUrl.ToString())</value> </set-header> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
When you do not require any policy at operation-level, you are ready to configure your API by importing OpenApi-specs.
Let’s take a look at the code
For readability and of course to learn something new, I switched to bicep for defining my deployment. I’ll skip the obvious parts like creating the Api Management-instance and configuring logging, but you can find those in the complete bicep-file linked at the end of this post. I will configure API Management with the commonly used Petshop sample that can be found at github.
Define the API
In the code below, I define the Petshop-api and it’s policies. These policies are defined as xml and will generate a simple default inheriting policy with some comments on top.
This will result in the following api definition in API Management.
Define the products
Next I define the products. In the sample I define a basic product with rate-limiting and an advanced product without limitations. Both products are linked to the petshop api.
This will create the following policies on the basic product.
Deploy the template
That’s all that’s required for configuring api management. To deploy this template, run the following command:
az deployment group create --name demoapim --resource-group <rgname> --template apimanagement.deploy.bicep
Import the api definition
With this basic configuration, it’s now time to import the operations. As mentioned, I am using the `openapi.yaml` downloaded from github.
az apim api import --resource-group <rgname> --service-name thijdemoapis --api-id petshop --path petshop --display-name 'Petshop API' --specification-format OpenApi --specification-path openapi.yml
The parameter api-id should be the same as the name defined here in the bicep file: Microsoft.ApiManagement/service/apis@2020-12-01. Otherwise you will add a new api to API Management and miss the configuration you made.
The path and display-name parameters overwrite the settings configured in the api. So make sure they are the same as in the bicep file.
When running the command, you’ll see the disclaimer ‘Command group ‘apim’ is experimental and under development.’ After a few seconds, the operations and definitions are available in API Management.
Automating deployment in your release pipeline
One final consideration: when you decorate the functions in your function-app with OpenApi-attributes, you can generate the OpenApi-spec after the release of your function app and import it into API Management using the az apim command.
Although the az apim command is still under development, I would certainly choose it to configure api’s in API Management. It forces you to think about your policies by removing the possibility to create them at operation level, making it easier to maintain it. And for bicep? Well, I am loving it! The 0.4-release I used is production ready and intellisense support in VSCode helps creating correct templates quickly.