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.

Advertisements

2 thoughts on “Using custom ARM templates with the Deploy to Azure button

  1. Is https://github.com/projectkudu/slingshot an improvement to https://github.com/bradygaster/deploytoazure using ARM templates? What are other improvements?
    I tried running Brady’s code on my local machine and when I signed in on MS sign in page I was redirected to my page, but on Error page with no explanation on what is going on. How can I intercept the error which probably happens in OpenID Connect module. Probably I have an Error Codes like 400, 401, 403, or 405. I checked however with Fiddler and I can see a page with a hidden form sent from MS sign in page with auth code and token id filled out in hidden fields.
    Thanks
    Rad

    Like

  2. Slingshot is an improvement over Brady’s deploy to azure project. We actually worked with him initially to get started on the idea. There aren’t any improvements other than being able to use ARM templates, however that is pretty big in itself since it gives you the ability to provision many different Azure services.

    I’m actually not too familiar with how the sign-on process works myself, but you should make sure that you’re running with an organizational account within your directory (live id’s won’t work). You also need to make sure you’re using your directories AAD ID and secret. We have some setup steps on the slingshot readme which details this. If you’re still running into issues, then feel free to mention it in the GitHub project.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s