Creating a Go Site Extension and Resource Template for Azure

The New Year is a good time to set goals for yourself. I’ve challenged myself this year to explore and ship some apps using the Go language, which is likely no surprise given my recent posts on setting up Go on Windows and running Go on Azure Websites.

As part of my initial explorations, I used Azure Websites to host my Go web apps. The last post I wrote showed my first Go prototype on Azure Websites. While useful as a prototype, it’s arguably not the most straightforward way to go about building and deploying your Go web app. The principal issue is that the Go runtime is not available by default on Azure Websites; we have to configure the website to use downloaded Go binaries. While my first prototype works, Azure has two capabilities you’ve likely never heard of which can help simplify and automate: Site Extensions and the Azure Resource Manager.

So, what did I do? I created a site extension for Go and wrote a resource template and PowerShell script for deploying an Azure Website that installs the site extention. You can find everything in my azure-go-lang-site-extension respository on Github.

Let’s see how it works.

Create the Site Extension

Site Extensions are exactly what the name suggests and provide ways to extend your Azure Website. You create a NuGet package with your code and config, package it up, deploy it to https://www.siteextensions.net/ (actually, any NuGet feed), and later you can apply these packages to your Azure Website. For additional details, please see the Azure Web Site Extensions blog post and the Azure Site Extensions wiki.

I used two important capabilities to get Go setup in the Azure Website: install/uninstall scripts and XML Document Transformation (XDT).

IF NOT EXIST "%WEBROOT_PATH%server.go" (
	cp server.go %WEBROOT_PATH%
)

IF NOT EXIST "%WEBROOT_PATH%go1.4.windows-386.zip" (
	cd /D %WEBROOT_PATH%
	curl -O https://storage.googleapis.com/golang/go1.4.windows-386.zip
	start unzip -o go1.4.windows-386.zip -d .
)

First, I wrote an install script called install.cmd which performs the following tasks:

  1. Copies the server.go file into the webroot. This is the file that serves the HTTP responses and the same one I used in my prototype.

  2. Downloads the Go SDK using curl.

  3. Unzips the Go SDK.

Unzipping the Go SDK can take several minutes. Today, this causes issues when trying to install the site extension from the Azure Portal and the Azure Resource Manager, as it will timeout, throw an error, and continue to retry until it ultimately fails. Consequently, I use the command start to run the unzip operation asynchronously, allowing the install.cmd execution to return and not causing a timeout error. While this solves the problem it is not ideal for if the instance is restarted during the process of unzipping the SDK it could leave us in a faulted state. This is likely a short term issue and I’ll update this post when it is resolved.

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> 
  <location path="%XDT_SITENAME%" xdt:Locator="Match(path)">
    <system.webServer>
      <handlers xdt:Transform="InsertIfMissing">
        <add name="httpplatformhandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" xdt:Transform="InsertIfMissing" />
      </handlers>
      <httpPlatform processPath="d:\home\site\wwwroot\go\bin\go.exe"
                    arguments="run d:\home\site\wwwroot\server.go"
                    startupTimeLimit="60" xdt:Transform="InsertIfMissing">
        <environmentVariables>
          <environmentVariable name="GOROOT" value="d:\home\site\wwwroot\go" xdt:Transform="InsertIfMissing"/>
        </environmentVariables>
      </httpPlatform>
    </system.webServer>
  </location>
</configuration>

Next, I wrote an XML document transform for the applicationHost.Config file to tell IIS to use our Go binaries for responding to web requests. After the site extension is installed and the first web request is processed, the applicationHost.config file is updated to include the additional configuration details. It’s important to point out the <location> ... <location> element, as this ensures our changes only apply to the correct website; otherwise, anything hosted on this machine is affected.

If you have questions on this configuration information, see my earlier post on setting up Go on Azure websites.

Packaging all of this into a site extension is pretty easy as it’s based on NuGet. I first created a Nuspec file to describe the package and then a build script which uses NuGet to create the package. The result is a NuPkg which I then upload to https://www.siteextensions.net/.

At this point you can log into the Azure Portal, create an Azure Website (or use an existing one), and install the Site Extension. Load your website blade and scroll down until you see the Extensions tile.

zeroextensions

Through the extensions blade you can browse available Site Extensions. You’ll find Go Lang for Azure Websites. Install it, and you’ll see it listed as one of your site extensions.

installedextension

Wait a few minutes and click the Browse link; you now have a Go web app running in Azure Websites. Not too bad! Of course, we can make the last step of creating the Azure Website and installing the Site Extension easier with the Azure Resource Manager.

Create the Resource Template

The Azure Resource Manager is a relatively new technology that provides a container for managing the lifecycle of Azure resources (called a resource group), a way to describe and execute the deployment and configuration of a set of Azure resources, and a management layer for these resources. As the resource manager is still in preview, documentation is hard to find; particularly for resource templates. To learn more about the resource manager, I recommend you watch Introduction to Azure Resource Manager from Build 2014 and read the REST API Reference and Template Language guides.

The resource template is nothing more than a JSON file that describes the Azure resources we want to deploy; in this case, it’s an Azure Website along with our Go site extension. Let’s look at the template.

{
  "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "siteName": {
      "type": "string"
    },
    "hostingPlanName": {
      "type": "string"
    },
    "siteLocation": {
      "type": "string"
    },
    "sku": {
      "type": "string",
      "allowedValues": [
        "Free",
        "Shared",
        "Basic",
        "Standard"
      ],
      "defaultValue": "Free"
    },
    "workerSize": {
      "type": "string",
      "allowedValues": [
        "0",
        "1",
        "2"
      ],
      "defaultValue": "0"
    }
  },
  "variables": {
    "goSite": "[parameters('siteName')]"
  },
  "resources": [
    {
      "apiVersion": "2014-04-01",
      "name": "[parameters('hostingPlanName')]",
      "type": "Microsoft.Web/serverfarms",
      "location": "[parameters('siteLocation')]",
      "properties": {
        "name": "[parameters('hostingPlanName')]",
        "sku": "[parameters('sku')]",
        "workerSize": "0",
        "numberOfWorkers": 1
      }
    },
    {
      "apiVersion": "2014-04-01",
      "name": "[variables('goSite')]",
      "type": "Microsoft.Web/sites",
      "location": "[parameters('siteLocation')]",
      "dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]",
      ],
      "properties": {
        "name": "[variables('goSite')]",
        "webHostingPlan": "[parameters('hostingPlanName')]"
      },
      "resources": [
        {
          "apiVersion": "2014-04-01",
          "name": "web",
          "type": "config",
          "dependsOn": [
            "[resourceId('Microsoft.Web/Sites', variables('goSite'))]"
          ],
          "properties": {
            "appSettings": [
              { "Name": "SCM_SITEEXTENSIONS_FEED_URL", "Value": "http://www.siteextensions.net/api/v2/" },
            ]
          }
        },
        {
          "apiVersion": "2014-04-01",
          "name": "GoLang",
          "type": "siteextensions",
          "dependsOn": [
            "[resourceId('Microsoft.Web/Sites', variables('goSite'))]",
            "[resourceId('Microsoft.Web/Sites/config', variables('goSite'), 'web')]"
            ],
          "properties": { }
        },
      ]
    }
  ]
}

I’m not going to attempt to explain this entire template as that’s well beyond the scope of this post. Take a look at this guide on the template language if you want to learn more.

For our purposes, I want to highlight the last section:

...

{
  "apiVersion": "2014-04-01",
  "name": "GoLang",
  "type": "siteextensions",
  "dependsOn": [
    "[resourceId('Microsoft.Web/Sites', variables('goSite'))]",
    "[resourceId('Microsoft.Web/Sites/config', variables('goSite'), 'web')]"
    ],
  "properties": { }
},

...

Here we tell the resource manager that we want the GoLang site extension installed into our Azure Website. Simple but effective.

To execute this template, use PowerShell. I wrote a script which makes this pretty simple.

Switch-AzureMode -Name AzureResourceManager

$subName = "YOURSUBSCRIPTIONNAME"
$userName = "YOURORGIDUSERNAME"
$securePassword = ConvertTo-SecureString -String "YOURPASSWORD" -AsPlainText -Force
$name = "YOURRESOURCEGROUPNAME"
$location = "West US"
$templateFile = "Template.json"
$siteName = $name + "Site"
$hostingPlanName = $name

$cred = New-Object System.Management.Automation.PSCredential($userName, $securePassword)
Add-AzureAccount -Credential $cred 

Select-AzureSubscription -SubscriptionName $subName
New-AzureResourceGroup -Name $name -Location $location -TemplateFile $templateFile -siteName $siteName -hostingPlanName $hostingPlanName -siteLocation $location -Force
 
Switch-AzureMode -Name AzureServiceManagement

When you run this command it will create a resource group and run the resource template, resulting in a new Azure Website with our site extension installed.

psoutput

The parameters siteName, hostingPlanName, and siteLocation are all defined within the resource template. For documentation on the New-AzureResourceGroup syntax you can review the MSDN documentation.

And that’s it, my friends. The end result is an Azure Website you can use to start playing around with Go.

There’s a lot of stuff to think about in this blog post. While my goal here was to make it easy to setup Go inside Azure Websites, I hope you see this as an example of how you can take advantage of Site Extensions and the Azure Resource Manager to do some amazing things in Azure.

Special thanks to David Ebbo for his help when creating the site extension!

I hope this helps!