Resolving Branch Specific Azure Template Links in Github Repositories


In this blog post, I will demonstrate a solution to problems associated with fixed template links in Azure Resource Manager templates stored on GitHub. This is likely to be one of those solutions to a problem that you did not even know you had. But read on, you might still find it useful.

The Problem

The Azure Resource Manager is the primary tool or orchestrating deployments in Azure. Using Azure Resource Manager Templates it is possible to describe and manage complicated deployments. Templates are also used to share example deployments and reference architectures. The Azure Quickstart Templates is one of the largest collection of such templates. I will often share architectures and example deployments with colleagues and customers via GitHub repositories and one of the features that I use a lot is to add a deploy button directly in the README.md file in the repository. An example would be my Team Foundation Server (TFS) in Azure deployment template, which you can find on GitHub with an associated  blog post.  The relevant section of the README.md file renders as seen below on GitHub. People interested in using this deployment can simply click the button.

The Quickstart Templates use the same icons and links to enable easy deployment. An issue with those links (the buttons) is that the URL for referring the Azure portal to the template is fixed. In the specific example above, the link that produces the "Deploy to Azure" button is:

<a href="https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fhansenms%2Fiac%2Fmaster%2Fdevnet-tfs%2Fazuredeploy.json" target="_blank">
    <img src="http://azuredeploy.net/deploybutton.png"/>
</a>

Cosmetically this is pretty bad in itself. The lack of readability makes it hard to manage the links, but the real problems start when you are making edits to the repository and you push those edits into a different branch for testing. Since the link explicitly references the master branch, you then have to change the link, push it and when you merge back into master, you have to change it back. It is an awkward workflow, which is error prone. Another problem scenario is when somebody clones the repository to make changes. They then have to accommodate the new repository name (and/or branches) while editing and if they would like to submit a pull request to merge back, they then manually have to change links. This is a major hassle when working with the Azure Quickstart Templates repo and one reason why some edits never get merged back in.

The Solution - Transmogrify

To solve this problem, I have developed a small .NET core 2.0 Web App. It is tentatively called "transmogrify" and you can find the source code on GitHub. This web app is designed to be called from one of the README.md files in a GitHub repository. It will then determine which repository and branch it is being called from by analyzing the referring URI. It will then transform the requested template URL into a correct link for the Azure portal (Azure Commercial or Azure Government) and redirect to the portal. I have deployed this Web App to an Azure Web App at the address https://transmogrify.azurewebsites.net. By using this endpoint instead of the fixed URL, the link above is achieved with:

<a href="https://transmogrify.azurewebsites.net/devnet-tfs/azuredeploy.json" target="_blank">
    <img src="http://azuredeploy.net/deploybutton.png"/>
</a>

Or for Azure Government:

<a href="https://transmogrify.azurewebsites.net/devnet-tfs/azuredeploy.json?environment=gov" target="_blank">
<img src="https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.png"
</a>

Not only is this link much simpler to read and write (without the URL encoded link to the template), it also works from any clone of the repository and any branch within the repository.

The code for the Web App is so simple, we can repeat the entire thing here without overwhelming anybody:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace transmogrify
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.Run(async (context) =>
            {

                if (context.Request.Headers["Referer"].ToString().Length == 0)
                {
                    await context.Response.WriteAsync("Please call from referring URI at github.com");
                    return;
                }

                Uri rUri = new Uri(context.Request.Headers["Referer"].ToString());

                string org = rUri.Segments[1].Replace("/","");
                string project = rUri.Segments[2].Replace("/", "");
                string branch = "master";

                if (rUri.Segments.Length > 3)
                {
                    branch = rUri.Segments[4].Replace("/", "");
                }

                string portal = "https://portal.azure.com";
                if (context.Request.Query["environment"] == "gov")
                {
                    portal = "https://portal.azure.us";
                }

                string templateUri = "https://raw.githubusercontent.com/" + org + "/" + project + "/" + branch + context.Request.Path;
                templateUri = System.Web.HttpUtility.UrlEncode(templateUri);

                string redirectUri = portal + "/#create/Microsoft.Template/uri/" + templateUri;

                if (context.Request.Path != "/") {
                    context.Response.Redirect(redirectUri);
                } 

                await context.Response.WriteAsync("Please provide redirect path.");
            });
        }
    }
}

Related work

I have been made aware that there is a related project hosted at https://deploy.azure.com. I am not sure if it has been updated lately or if deployment to Azure Government is directly supported. A newer implementation of that project is the Slingshot project. Do check that one out too. In both cases, these projects aim to deploy without using the Azure portal which has matured considerably since the inception of those projects. The solution in this blog post leverages the Azure portal and simply redirects.

Conclusions

Fixed URLs with direct references to specific repositories and branches make it difficult to use good development practices when authoring Azure template libraries. In this blog, I have demonstrated that the problem can be solved by using a Web App to transform URLs based on repository and branch names and redirect to the appropriate Azure portal. The current version of the app only supports Azure Commercial and Azure US Government clouds, but that could easily be expanded. For long term reliability, the transmogrify app should probably be deployed and managed as a central service, but for now, feel free to use it in your templates.

Let me know if you have questions/comments.

Comments (0)

Skip to main content