Visual Studio Team Services (and Team Foundation Server) supports build badges out of the box giving you an URL for an image that can be embedded in a web page (eg: a wiki or a GitHub page) to visualize the (always updated) status of a build.
Unfortunately Release Management doesn’t offer the same support for releases, fortunately it easy to implement a release badge generator with a few lines of code and using VSTS built-in extensibility mechanisms.
This is what I’m going to show you on this post, how I built a VSTS release badge generator using VSTS web hooks, an Azure Function to generate the badge and Azure Blog storage to store them practically for free (it will cost you at most some cents per month for a huge number of badges/accesses).
We want a system that:
- Is Fast
- Free or very very cheap to run
- Has very few moving parts
- Doesn’t require access to VSTS data
- Doesn’t require maintenance or management
- Is stateless, don’t want to manage data besides the badge itself.
The architecture consists of a system that statically generates the badge every time a release is deployed, only if there is a new release the badge is regenerated.
This means when a users sees a badge, it’s only accessing a static file (no computation is needed, so no extra costs are incurred. Computation costs are much higher than storage costs) , the badge is not generated in real and there is no need to have access to VSTS, meaning not only there is no need to manage credentials to access VSTS, the code is simpler and specially there is no attack surface against VSTS via the generator.
Since the files are statically generated and accessed via HTTP/HTTPS they can be cached (in proxies and in browsers) improving access speed to badges and saving bandwidth (and storage costs), by default the cache is configure for a low value but it can be configured to be adjusted to different release (expected) frequencies.
This means the we can generate badges while being decoupled from VSTS, generate badges from many team projects (and even different accounts) without having access to them, the only thing we need is to provide the endpoint of the generator and configure a web hook.
It is an event drive architecture, with all the advantages and disadvantages.
This design has some drawbacks:
- We only have badges after the the generator is configured and after a release is deployed, it doesn’t work with past deploys. If you can’t wait for the next release you can force a redeploy just to generate the badge.
- The badges are totally decoupled from the release definition and environments. If release definition or a environment is deleted the badges are not deleted.
- We could have a process to clean up orphaned badges, but for simplicity we just leave them alone (storage is cheap)
An Azure account to host the code and at least one VSTS account to generate the badges for.
VSTS Service Hooks allows integrating VSTS with other applications, every time an certain event happens in VSTS (a work item has changed, a build has started, pull request created, and all other sort of events) the application is notified in near real time.
Service hooks can be used to integrate out of the box with services like AppVeyor, Azure App Service, Campfire, Jenkins, Microsoft Teams , Slack, Office 365 or ZenDesk just to name a few. In our case we are interested in a generic integration, so we are going to use a Web Hook to receive an event every time a release deployment is finished.
We can create one web hook  with Release deployment completed  event per release definition (if we wish to generate release badges only for some releases or environments) or a web hook for any release definition  (this will generate a badge for all releases in any environment).
The event has a JSON payload, the schema of the event varies with the type of event we are receiving, the Release deployment completed event contains all the information we need to generate a badge, it contains among other things
- Release definition id/name
- The release number
- The environment
With a (near) real time event every time a release is deployed we just need to generate the badge based on the event payload.
Generate the badge using Azure Function
We need to generate the badge every time an event is received, an Azure Function is very suitable for this task. Some of its advantages (among others)
- Its very cheap to run (see hosting plan comparisons)
- If you already have an azure app services you can host it there (since you are already paying for it) so no extra costs
- Pay per use, pay only what you use with a generous free monthly cap (which means you can run it for free unless you have big number of deployments occurring)
- No need to manage infrastructure or care about scalability.
- First class with Azure components, we wan use other Azure resources with only a few lines of code or declaratively.
We need to implemented a function, that upon receiving a JSON payload via HTTP/HTTPS, generates a badge based on the event data and store it somewhere where is publically accessible via HTTP/HTTPS.
I’m not going into the details of creating an Azure Function, there is plenty of information available online:
- Create your first function in the Azure portal
- Azure Functions Tools for Visual Studio
- Azure Functions Documentation
We need to implement an Azure function that is triggered by an HTTP request and when the event is received we need to generate the badge based on the data (we will generate a badge with name of the release definition and the release number).
Generating the badge
We can either manipulate images files to generate the badge image, use some external library or use an external service to do the heavy lifting for us.
I’ve chosen the latter and used Shields IO service to generate badges. Shields IO is a service that can generate badges in several image formats (jpeg, gif, svg,…) for dozens of services (eg: Travis, AppVeyor, CircleCI,etc) but it can also generate generic badges and that what we will use to generate our badges with a single HTTP call.
Storing the generated badges
After the badge has been generated, we need to store it somewhere so people in general can access it. Azure Storage is a natural choice:
- It’s dirty cheap
- Badges can be accessed via HTTP/HTTPS anywhere in the world
- It’s fast (files are pre generated) and you can control time to live on browser caches to save bandwidth costs.
- It has built in replication mechanisms to guarantee durability and high availability.
So we will use Azure Storage Blobs, all generated badges will be stored on a container.
Writing a file from an Azure function is only a few lines of code, we could be using Azure Functions declaratively capabilities to write the file into azure storage with no code at all, but we want to control no only the storage container and file path, we also want to have a more fine grained browser cache directives. Even so writing the file to storage is just a file lines of code
The container where the badges are stored only one to fill one requirement, anonymous access must be enabled.
By default the functions are set to use function level security (if you use the Get Function URL from the portal, the default code is automatically added to the URL)
If you wish to have subscriptions from multiple accounts or team projects, you may want to have different keys so you can revoke them if needed without disrupting all other subscriptions. It is advisable you set up multiple keys.
Learn how to work with function or host keys
Be aware that anyone that you share the function URL with, can flood the function with fake events, not only generating fake data but also make you (potentially) incur into extra costs (compute and storage)
Show me the code
You must be wondering, so much talk and practically no details at all how this was implemented, I haven’t gone into details since the code is quite simple and it has plenty of comments.
The solution is not future proof, for example it doesn’t deal with failures or unavailability of Shields IO service (transient failures will not prevent badge generation, since Service Hooks built-in retry mechanism will take care of that).
The code works and can be used in production, but it has been written as a learning experimenting since I wanted to to try some things with Azure Function and at the same time produce something useful.
All the code is available as Open Source with a MIT license on GitHub, the repo contains not only the source code but also a PowerShell script (which uses an ARM template) to automatically provision and configure the Azure Function. Setting up Web Hooks subscriptions and deploying the code are manual tasks
The readme file on the repo has all instructions (way longer that this post) how to provision, deploy code and configure Web Hooks as well as parameterize the generator (different cache settings, badge styles, etc.).
Provisioning the function and deploying the generator is very easy to automate using VSTS release management. It can automated with only two tasks