Azure API Management is a great product that we often use on customer solutions. It is an extremely effective way to provide a layer of abstraction between your callers and back-end APIs, and provides centralised governance across your API surface.
However, one of the most common questions from our customers is: "What is the best way to implement an effective CI/CD pipeline with Azure API Management?" In this regard, we've seen customers trying automation strategies like:
- Syncing Git repositories across environments
- Scripting the entire API surface in PowerShell
Whilst they do work, unfortunately these options are rather laborious, and really reduce the efficiency of developer and deployment processes.
Enter ARM Templates
This all changed in July 2017 when the APIM product team announced support for ARM Templates.
This brought the deployment model back in line with other Azure resources, and was a great step forward.
The only drawback was that ARM templates needed to be:
- Completely generated by hand, or;
- Manually exported from the API Management resource group and then cleaned up by hand (this implies doing all the Azure API Management configuration through the portal).
Both of these models lead to a lot of developer friction - surely there has to be a better way of doing this?
Contract First Development
At Mexia, we believe that a contract-first approach for API solutions leads to better outcomes for various reasons:
- Alignment across teams
- Enables teams to work in parallel during the development process
- Provides an agreed standard for testing and acceptance
With HTTP-based APIs, one of the most popular ways to define API contracts is Swagger (or as it has recently been renamed to, OpenAPI).
By defining your Swagger specifications for an API upfront, it opens up a number of possibilities for reducing developer friction during the API development process.
At Mexia, we always try to not only deliver the solution required, but also leave our customers with a set of best practices that will help them along the way on future projects.
As part of this, we have created a Visual Studio 2015/2017 plugin for this specific scenario of contract-first, called Swagger2ARM.
It will take a valid Swagger specification and generate an ARM template for the necessary resources contained within the specification. This enables a developer, with a single click, to immediately have deployment-ready ARM templates after designing the API surface!
All you need to do is right click the relevant Swagger file (in this case the petstore.json sample) and then select ‘Generate ARM from Swagger’.
Immediately, the generated ARM templates will appear in the current project:
And the results of the operation will appear in the Visual Studio status bar:
Versioning and Revisioning
As you may be aware, Azure API Management supports the concepts of versions and revisions. There is no need to go into the details of this in this article, as there is some good documentation available already. Put simply, versions and revisions are invaluable for managing the lifecycle of an API - versions for larger breaking changes, and revisions for smaller incremental changes.
The files generated by our Swagger2ARM extension have both version (v1) and revision (rev2) in the filename. That's because we use the version number in the source Swagger file to drive the APIM versions and revisions.
In this particular case the source Swagger file was version 1.1, which translates into version 1 and revision 2 in Azure APIM.
(Note the revision is always greater by one in APIM, because minor version numbers start at zero, and APIM revisions start at one - i.e. version 1.0 in Swagger is version 1, revision 1 in APIM)
When dealing with a change in your API, you should increase either the major version (for a breaking change, such as changing a response schema), or the minor version (for a non-breaking change, like adding a new operation). Our Swagger2ARM extension will take care of making the necessary changes in the generated ARM templates, including the version set, API version and API revision.
Dealing with Policies
One limitation of the contract-first approach is that API and operation policies do not exist in Swagger - they are essentially vendor specific. To assist with this, we developed a feature that synchronises policies from APIM, back to the local ARM template.
This gives us a basic API lifecycle that works like this:
- Develop the Swagger contract for your API
- Use Swagger2ARM to generate the local ARM templates
- Deploy to your Development APIM instance using the local ARM templates
- Configure the APIM policies for your API and operations in the APIM portal
- Synchronise the policies using Swagger2ARM back to your local ARM template
At this point you have all your ARM templates ready to deploy all the way to Production :-)
VSTS Release Pipelines
Once we have all the ARM templates ready, we can easily set up a release pipeline that looks like this:
The variables for the release look like this:
This gives us the ability to deploy a different revision from the version pipeline if necessary.
It's also important that each major version has its own release pipeline in VSTS, so that we can deploy different versions of an API in isolation to each other.
The result is a flexible deployment system that caters for versions and revisions, capable of deploying all the way to Production.