Using custom ARM templates with the Deploy to Azure button

Deploy to Azure ButtonA few days ago, I made a blog post introducing the “Deploy to Azure Button” for Azure Websites, which demoed the default deployment behavior of creating a site and deploying your code to it.  If you want to do something more advanced and customize this behavior, then you can add an ARM template called “azuredeploy.json” at the root of your repository which will cause users to be presented with different inputs and configure your services as specified.  Since ARM templates are still a relatively new concept, I’m going to walk you through a workflow that I use to test them before checking them in to my repo, as well as describe some of the special behaviors that the “Deploy to Azure” site does to improve the experience for users.

An Example Template

Unfortunately I’m not aware of any tooling at the moment which makes it easy to write custom templates from scratch, so I usually just reference existing ones.  For a “simple” example of an ARM template, take a look at the azuredeploy.json file below which is from my HelloWorldMVCAppSettings project. This template is essentially the default ARM template which creates a site, with an additional app settings parameter.

{
  $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"
    },
    someAppSetting: {
      type: "string",
      defaultValue: "Test App Setting Value"
    },
    repoUrl: {
      type: "string"
    },
    branch: {
      type: "string"
    }
  },
  resources: [
    {
      apiVersion: "2014-06-01",
      name: "[parameters('hostingPlanName')]",
      type: "Microsoft.Web/serverFarms",
      location: "[parameters('siteLocation')]",
      properties: {
        name: "[parameters('hostingPlanName')]",
        sku: "[parameters('sku')]",
        workerSize: "[parameters('workerSize')]",
        numberOfWorkers: 1
      }
    },
    {
      apiVersion: "2014-06-01",
      name: "[parameters('siteName')]",
      type: "Microsoft.Web/Sites",
      location: "[parameters('siteLocation')]",
      dependsOn: [
        "[concat('Microsoft.Web/serverFarms/', parameters('hostingPlanName'))]"
      ],
      tags: {
        [
          concat('hidden-related:',
          resourceGroup().id,
          '/providers/Microsoft.Web/serverfarms/',
          parameters('hostingPlanName'))
        ]: "empty"
      },
      properties: {
        name: "[parameters('siteName')]",
        serverFarm: "[parameters('hostingPlanName')]"
      },
      resources: [
        {
          apiVersion: "2014-04-01",
          type: "config",
          name: "web",
          dependsOn: [
            "[concat('Microsoft.Web/Sites/', parameters('siteName'))]"
          ],
          properties: {
            appSettings: [
              {
                name: "someAppSetting",
                value: "[parameters('someAppSetting')]"
              }
            ]
          }
        },
        {
          apiVersion: "2014-04-01",
          name: "web",
          type: "sourcecontrols",
          dependsOn: [
            "[resourceId('Microsoft.Web/Sites', parameters('siteName'))]",
            "[concat('Microsoft.Web/Sites/', parameters('siteName'), '/config/web')]"
          ],
          properties: {
            RepoUrl: "[parameters('repoUrl')]",
            branch: "[parameters('branch')]",
            IsManualIntegration: true
          }
        }
      ]
    }
  ]
}

Everything under the root “parameters” property will be inputs into your template, which also shows up in the UI.  Then these parameter values feed into the resources defined later in the template with the “[parameters(‘paramName’)]” syntax.  In this example, you see 2 primary resource providers being specified:

  1. Microsoft.Web/serverFarms
  2. Microsoft.Web/sites

And under Microsoft.Web/sites, there are 2 sub-resources:

  1. Microsoft.Web/sites/<sitename>/config/web – Used to set app settings and connection strings.
  2. Microsoft.Web/sites/<sitename>/sourcecontrols/web – Used to wire up your Git repo

Each resource has its own set of dependencies, as well as a list of input parameters that can be populated with hard-coded strings, references to input parameters, or by even concatenating variables/strings.

Note: In the example that I’m referencing, the API version is 2014-04-01 for the site’s sub-resources, while it’s 2014-06-01 for everything else.  That’s because there’s actually some issues with the 2014-06-01 API for configuring app settings and connection strings through ARM.  It should be fixed soon, but for now using the 2014-04-01 works just fine.

Getting more examples and testing them

So that’s great and everything, but what if you want to try something even more complex? Well if you use the Azure Resource Manager PowerShell tools, you can get access to a whole gallery of ARM templates, as well as test them out from the command line.  To install it, just use Web Platform Installer and search for “Microsoft Azure PowerShell” which should give you everything you need.

Once you have the ARM PowerShell tools setup, open up a PowerShell window and run the commands below.  In summary, these commands will allow you to login to Azure with your account, download existing templates from the gallery to reference, and then deploy your template:

# You need this to use some of the cmdlets below.
PS C:\> Switch-AzureMode -Name AzureResourceManager

# Login with your Microsoft Credentials
PS C:\> Add-AzureAccount

# Lists all of your subscriptions.  The "IsCurrent" property
# tells you which subscription you'll use during a deployment.
PS C:\> Get-AzureSubscription

SubscriptionId            : 70d385d8-3616-45b5-948f-7b13864bc7ee
SubscriptionName          : Subscription-1
Environment               : AzureCloud
SupportedModes            : AzureServiceManagement,AzureResourceManager
DefaultAccount            : ellhamai@microsoft.com
IsDefault                 : True
IsCurrent                 : True
CurrentStorageAccountName :

SubscriptionId            : 42dd9e47-0678-4332-b469-370f5640d0cc
SubscriptionName          : Internal Consumption
Environment               : AzureCloud
SupportedModes            : AzureServiceManagement,AzureResourceManager
DefaultAccount            : ellhamai@microsoft.com
IsDefault                 : False
IsCurrent                 : False
CurrentStorageAccountName :

# If the wrong subscription is selected, use this command to
# change to the one you want to use
PS C:\> Select-AzureSubscription `
              -SubscriptionName "Internal Consumption"

# Setup a site variable name for quick testing iterations
PS C:\> $site = "mysite"

# Use these cmdlets to list and save templates from the Gallery
# Get-AzureResourceGroupGalleryTemplate
# Save-AzureResourceGroupGalleryTemplate

# This does the deployment.  It looks scary but once you reference
# the TemplateFile parameter, you get automatic tab-completion for
# every parameter in your template.  Notice that for simplicity,
# we used $site for many different fields, which actually mimics
# what the Deploy to Azure site is doing to reduce the number of
# fields that a user needs to fill out.
PS C:\> New-AzureResourceGroup `
           -Name $site `
           -Location "East US" `
           -DeploymentName $site `
           -TemplateFile c:\azuredeploy.json `
           -siteName $site `
           -hostingPlanName $site `
           -siteLocation "East US" `
           -sku "Free" `
           -workerSize 0 `
           -repoUrl https://github.com/Tuesdaysgreen/HelloWorlddemo `
           -branch master `
           -Verbose

Once you have a working template, you just need to push it to your public repo and you should be ready to go.

Special Parameter Names

In order to provide a better experience to users deploying your code, we added special logic to specific parameter names in the Deploy to Azure site.  If your template uses any of the following parameter names, you’ll automatically take advantage of these behaviors:

  • siteName – Does a check for existing site names
  • siteLocation – Returns possible site locations
  • sqlServerLocation – Returns possible SQL Server locations
  • sqlServerName – Does validation on the name syntax
  • sqlAdministratorLogin – Fills in the value by using the user’s name
  • repoUrl – Shows as a readonly label on the top
  • branch – Shows as a readonly label on the top
  • hostingPlanName – Hidden in the browser.  For simplicity, we set this to be equal to the siteName
  • sku – We don’t show “workerSize” unless sku is Basic or Standard
  • workerSize – We change 0,1,2 to “Small”, “Medium”, “Large”.

As we get more feedback from users, we can expand on this list to optimize more scenario’s that people think may be important.