How to create your own ‘Deploy to Azure’ button

You probably have seen this button before already:

When you click it, it brings you to a page where you are able to deploy Azure resources in a (new) resource group of your choice.

Once the deployment is started and succeeds, new functionality is running in your Azure subscription.

Can we make use of this functionality our selves? How hard could it be?

Let’s see…

A little bit of introduction

The ‘magic’ behind the scenes is just an ARM template deployment.

An ARM (Azure Resource Management) template is a description, in JSON format, of a number of Azure resources and their relationship. Those resources can be deployed to an Azure account using that file.

Making use of ARM templates is the preferred way of deploying resources, also called Infrastructure as Code (IaC), because is reliable, secure and repeatable over creating resources by hand.

Still, ARM templates are not ‘sexy’. It’s actually hard to write and maintain ARM templates. If you want to know more about ARM, please check out this great learning path at MS Learn.

Entering BICEP

We have seen in the past some examples of new programming languages making something hard a bit easier. For example, we have seen how Less and Sass make writing CSS easier. And we have seen how TypeScript has made writing JavaScript much more reliable and easier.

Microsoft has recently introduced BICEP (pun intended) to make ARM templates easier.

Here is an example.

Comparison

This is an BICEP template creating an Azure storage account:

@minLength(3)
@maxLength(24)
@description('Provide a name for the storage account. Use only lower case letters and numbers. The name must be unique across Azure.')
param storageName string

output storagename string = exampleStorage.name
output storagekey  string = exampleStorage.listKeys().keys[0].value

resource exampleStorage 'Microsoft.Storage/storageAccounts@2021-02-01' = {
  name: storageName
  location: 'eastus'
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

If we build this BICEP file, an ARM file is generated:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.4.613.9944",
      "templateHash": "16566843126863072798"
    }
  },
  "parameters": {
    "storageName": {
      "type": "string",
      "metadata": {
        "description": "Provide a name for the storage account. Use only lower case letters and numbers. The name must be unique across Azure."
      },
      "maxLength": 24,
      "minLength": 3
    }
  },
  "functions": [],
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2021-02-01",
      "name": "[parameters('storageName')]",
      "location": "eastus",
      "sku": {
        "name": "Standard_LRS"
      },
      "kind": "StorageV2"
    }
  ],
  "outputs": {
    "storagename": {
      "type": "string",
      "value": "[parameters('storageName')]"
    },
    "storagekey": {
      "type": "string",
      "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2021-02-01').keys[0].value]"
    }
  }
}

If you are into curly brackets, please stick with ARM 🙂

This is just a very simple example but I think you find the BICEP version already more readable.

Building a BICEP / ARM template

Although BICEP is more readable, you still need to know this ‘ARM language’. You have to know how Azure resources are described and for each resource you use, you need to know the underlying properties and relationship with other resources.

BICEP just makes it easier, not easy 🙂

Luckily, we get some help from the BICEP extension in Visual Studio Code:

The code completion is so powerful and productive!

Once the BICEP file is written, you can generate an ARM template like this in PowerShell:

az bicep build --file main.bicep --outdir .

This will generate a new ARM file called ‘main.json’ in the same folder.

Notice you need to have installed both the AZ Azure CLI in Powershell and the BICEP package inside the AZ CLI:

/* in powershell */
az install bicep
az bicep version
az bicep --help

In a nutshell: you started with BICEP and ended with ARM.

Deploying using that button

That logic behind the button we use understands ARM templates only, for now BICEP is not supported…

First, let’s ‘render’ the button .

You can add this piece of HTML in a webpage or that piece of Markup in GitHub:

/* HTML */
<img src="https://aka.ms/deploytoazurebutton"/>

/* Markup */
![Deploy to Azure](https://aka.ms/deploytoazurebutton)

This is just the button being drawn.

This button has to point to somewhere, some logic that knows about deploying ARM templates.

Point it to this URL:

https://portal.azure.com/#create/Microsoft.Template/uri/[url-of-your-ARM-template]

Do not forget to fill in the location of your ARM template.

For example, The ARM template seen above, is available in a raw page at GitHub.

Just take that URL as an example, URL encode it and place that string in the URL seen above.

I have done this for you aready. Just try it out here:

The same experience can be seen in GitHub (all code seen here in the blog is available in this GitHub repo).

When you click on either one of the buttons, you are redirected to the Azure portal and you are asked to identify the Azure subscription you use. When that is do, the deployment wizard is shown:

Just select a (new) resource group and fill in the related region.

In the BICEP file, we defined one parameter named ‘Storage Name’. See how the attributes of that parameter are now part of the wizard functionality (min length, max length, description).

Deploy the resources after a positive validation:

See how the resource deployment succeeds:

See how the two output parameters, define in the BICEP template, are also made available:

Note: If you expose secrets (as I did using the ‘listKeys()’ function), these are not hidden! That is why I had to blur it in the screenshot.

Finally, the resources are created:

The location of the storage account was hardcoded. To have it in sync with the resource group location, adding an extra parameter is enough:

param location string = resourceGroup().location

Use it at the right place:

/* hardcoded */
location: 'eastus'

/* dynamic */
location: location

Conclusion

We have seen how easy it is to create an ARM template from a BICEP file.

If you are interested in Azure resources in a coded, controlled way, please check out this BICEP language. Check out these MS Learn modules too, it’s a full day of training.

We have also experienced how this ARM file can be used for deployments.

Once you have seen how the ‘Deploy to Azure’ button works, there is not much magic left anymore.

This button still needs a human to execute the deployment. Still, you are in control of what is deployed so it’s a great solution if you want to share Azure resource deployments to other (unknown) users in their subscription.

Keep in mind your ARM template is now publicly exposed and can be ‘reverse-engineered’.

Last but not least, everything we have done can be done by build pipelines too.

All source code behind this blog post is made available here on GitHub.

Appendix

This is the power of the community.

Niel Peterson, @nepeters, reacted to this blog post with a nice addition.

You can use Github Actions to run the bicep build process. This way the ARM template is never out-of-date so the button logic is always up-to-date.

Check out his GitHub Action to see this in action.