Azure DevOps CI / CD (i.e. Build / Release) mechanism is very valuable to me. It makes things reproduceable & robust. It allows me to change whatever I want since I have this constant safety net underneath.
For simple projects, having a build and release is fine. With micro-services I adopt the pattern of one service / one repo / one CI/CD.
That is great until we consider shared infrastructure. If many services share an infrastructure (e.g. AKS Cluster, App Service Plan, etc.), which one’s release should deploy the shared service? I considered having each of them doing so but finally adopted the pattern of having a separate repo / CI/CD for the shared infrastructure. I now need to cascade releases: shared infrastructure first, then the “real” services.
That’s not too bad to sequence it since it’s all automated. But… I’m cheap and I delete my “dev” environment all the time. So, when I need to reconstruct it, I need to release the shared infrastructure then each services. This is when it gets ugly to do it with Azure DevOps UI.
In this article I’m going to show how to use Azure DevOps REST API to do this. This is going to be solution-agnostic. You could implement it with a console-app running on your laptop, a container in Azure, etc. .
In the next article, I’m going to show how to implement it using Azure Logic Apps.
We are going to stay simple. I’m going to target the scenario of doing a release on a project, waiting for it to succeed and then releasing another project. There are many more scenarios with approvals and conditions, etc. , but here we’re going to look at the basics. You can use your imagination to land your own scenario.
Fundamental of APIs
Azure DevOps has a collection of REST APIs. There is an API per entity, e.g. Build, Release, Git, Work Item, etc. .
As the online documentation states, APIs requires use to know:
- Our organisation: that’s the highest namespace in DevOps. When we look at the main dashboard, we land on
- Our project: this is the name of the devops project. Again, when we look at the project’s dashboard, we land on
We also need to authenticate. But that’s important, so let’s do an entire sub section about that.
Typically, a user authenticates through an Azure AD application. The application then performs actions on behalf of the user.
In our case, it makes more sense to have actions performed by the system. Azure AD Service Principal aren’t supported in Azure DevOps. Instead, there is a concept of Personal Access Tokens (PAT).
A PAT is a token we can directly pass to requests. We do not need to authenticate using that token to receive a bearer token.
PAT can be given Full access (useful to start) or custom scope. We recommend using a custom scope here. As always, we apply the Principle of least privilege to limit the attack surface and the magnitude of the risk should the secret be leaked.
For our scenario, a token giving access to read / write / execute of releases is enough:
Unfortunately, at the time of this writing (end of November 2018), there isn’t a way to limit the scope per project.
We can set expiration dates to PATs and revoke them shall they be compromised.
It is important to note that a token can be readily used without any user authenticating. It is therefore important to handle the token with care. Ideally, Azure Key Vault should be used so the secret isn’t expose to users.
Unfortunately, at the time of this writing (end of November 2018), there is an API for revoking tokens but none to create one (to automatically rotate PATs).
Using a PAT to authenticate
Although using PATs is simple, there are caveats that can easily incur some wasted time.
PAT are using for basic HTTPS authentication. So, they are passed clear text over HTTPS connection.
Well… their base-64 representation is passed clear text.
Actually… An important detail is that a colon (i.e. :) needs to be prepended to the PAT before its base-64 representation be calculated.
This is because typically we use the pair
USER is empty, hence
To be clear, if the PAT value is
XYZ, we need to take the base-64 value of
We can then use the following HTTP host header:
Authorization: Basic BASE64PATSTRING
A C# example is given at the bottom of this article.
Creating a release
Now we can look at specific API.
First we’ll need to create a release:
The request body can contain a few specifics, such as an artefact list.
The real key value to pass in the body is the
release definition ID. This can be found by looking at the URL when looking at a release definition. It is present in the URL query string under
It typically is a small integer (e.g. 1, 2, 3, etc.).
This API returns a payload of information. The key value to keep is the
id. This is the
release-id. We’ll need it to inquire about the release status.
Getting a release status
In order to wait for the release to be over, we’ll need to probe the get-release API:
This is where the
releaseId we got from the previous API comes handy.
It is pretty straightforward to use.
The returned payload contains information about the release and each of its stage. The stages information can be found under the
environments JSON property. Each of those stage have a status. This is the value we need to monitor.
We hope this quick look at Azure DevOps API gives enough pointer to start using them.
In the next article, we’ll look at an example implemented using Azure Logic Apps.