How to Deploy your Azure Function Automatically (4 Ways, with ARM template) 
Tags: serverless, azure, devops, cicd
Published: 2019-07-19 11:38:31

---
title: How to Deploy your Azure Function Automatically (4 Ways, with ARM template) 
published: true
tags: serverless,azure,devops,CI/CD
canonical_url: http://www.frankysnotes.com/2019/07/four-ways-to-deploy-your-azure-function.html
---

It's so nice to be able to add some serverless components in our solution to make them better in a snap. But how do we manage them? In this post, I will explain how to create an Azure resource manager (ARM) template to deploy any Azure Function and show how I used this structure to deploy an open-source project I've been working on these days.

## Part 1 - The ARM template

An ARM template is a JSON file that describes our architecture. To deploy an Azure Function we need at least three recourses: a functionApp, a service plan, and a storage account.  

!["Generic Simple ARM"](https://thepracticaldev.s3.amazonaws.com/i/6g36tkfiqe0ucwvmu6vt.png)

The FunctionApp is, of course, our function. The service plan could be set as dynamic or describe the type of resource that will be used by your function. The storage account is where is our code.  
  
![](https://thepracticaldev.s3.amazonaws.com/i/wnm6j3422te418bxa96u.png)
  
In the previous image, you can see how those components interact more with each other. Inside the Function, we will have a list of properties. One of those properties will be the **Runtime**, for example, in the AZUnzipEverything demo, it will be _dotnet_. Another property will be the connection string to our storage account that is also part of our ARM template. Since that resource doesn't exist yet, we will need to use the dynamic code.  
  
The Function node will contain a sub-resource of type `storageAccount`. This is where we will specify where is our code, so it cant be clone to Azure.  
  
### Building ARM for a Simple Function
  
Let's see a template for a simple Azure Function that doesn't require any dependency, and we will examine it after.  
  
> You can use any text editor to edit your ARM template. However, the bundle [VSCode](https://code.visualstudio.com/?WT.mc_id=devto-blog-frbouche) with the extensions [Azure Resource Manager Tools](https://marketplace.visualstudio.com/items?itemName=msazurermtools.azurerm-vscode-tools&WT.mc_id=devto-blog-frbouche) and [Azure Resource Manager Snippets](https://marketplace.visualstudio.com/items?itemName=samcogan.arm-snippets&WT.mc_id=devto-blog-frbouche) is particularly efficient.

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2018-07-01",
            "name": "storageFunc",
            "location": "[resourceGroup().location]",
            "tags": {
                "displayName": "storageFunc"
            },
            "sku": {
                "name": "Standard_LRS"
            },
            "kind": "StorageV2"
        },
        {
            "type": "Microsoft.Web/serverfarms",
            "apiVersion": "2018-02-01",
            "name": "servicePlan",
            "location": "[resourceGroup().location]",
            "sku": {
                "name": "Y1",
                "tier": "Dynamic"
            },
            "properties": {
                "name": "servicePlan",
                "computeMode": "Dynamic"
            },
            "tags": {
                "displayName": "servicePlan"
            }
        },
         {
              "apiVersion": "2015-08-01",
              "type": "Microsoft.Web/sites",
              "name": "functionApp",
              "location": "[resourceGroup().location]",
              "kind": "functionapp",
              "dependsOn": [
                "[resourceId('Microsoft.Web/serverfarms', 'servicePlan')]",
                "[resourceId('Microsoft.Storage/storageAccounts', 'storageFunc')]"
              ],
              "properties": {
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', 'servicePlan')]",
                "siteConfig": {
                  "appSettings": [
                    {
                      "name": "AzureWebJobsDashboard",
                      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', 'storageFunc', ';AccountKey=', listKeys('storageFunc','2015-05-01-preview').key1)]"
                    },
                    {
                      "name": "AzureWebJobsStorage",
                      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', 'storageFunc', ';AccountKey=', listKeys('storageFunc','2015-05-01-preview').key1)]"
                    },
                    {
                      "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
                      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', 'storageFunc', ';AccountKey=', listKeys('storageFunc','2015-05-01-preview').key1)]"
                    },
                    {
                      "name": "WEBSITE_CONTENTSHARE",
                      "value": "storageFunc"
                    },
                    {
                      "name": "FUNCTIONS_EXTENSION_VERSION",
                      "value": "~2"
                    },
                    {
                      "name": "FUNCTIONS_WORKER_RUNTIME",
                      "value": "dotnet"
                    }
                  ]
                }
              },
              "resources": [
                  {
                      "apiVersion": "2015-08-01",
                      "name": "web",
                      "type": "sourcecontrols",
                      "dependsOn": [
                        "[resourceId('Microsoft.Web/sites/', 'functionApp')]"
                      ],
                      "properties": {
                          "RepoUrl": "https://github.com/FBoucher/AzUnzipEverything.git",
                          "branch": "master",
                          "publishRunbook": true,
                          "IsManualIntegration": true
                      }
                 }
              ]
            }
        
    ],
    "outputs": {}
}
```

#### The Storage Account

The first resources listed in the template is the Account Storage. There nothing specific about it.  
  

#### The Service Plan

The service plan is the second resource in the list. It's important to notice that to be able to use the SKU **Dynamic** you will need at least the API version of **apiVersion** to be "2018-02-01". Then you specify the SKU.  
  
```json
    "sku": {
        "name": "Y1",
        "tier": "Dynamic"
    }
```
  
Of course, you can use the other SKU if you prefer.  
  
#### The Function App

Final resources added to the mixt, and this is where all the pieces are getting together. It's important to notice that the other in which the resources are listed are not considered by Azure while deploying (it's only for us ;) ). To let Azure knows you need to add dependencies.  

```json
"dependsOn": [
    "[resourceId('Microsoft.Web/serverfarms', 'servicePlan')]",
    "[resourceId('Microsoft.Storage/storageAccounts', 'storageFunc')]"
]
```
  
This way the Azure Function will be created after the service plan and the storage account are available. Then in the **properties** we will be able to build the ConnectionString to the blob storage using a reference.  
  
```json
{
    "name": "AzureWebJobsDashboard",
    "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', 'storageFunc', ';AccountKey=', listKeys('storageFunc','2015-05-01-preview').key1)]"
}
```
  
The last piece of the puzzle is the sub-resource **sourcecontrol** inside the FunctionApp. This will define where Azure should clone the code from and in which branch.  
  
```json
"resources": [
    {
        "apiVersion": "2015-08-01",
        "name": "web",
        "type": "sourcecontrols",
        "dependsOn": [
        "[resourceId('Microsoft.Web/sites/', 'functionApp')]"
        ],
        "properties": {
            "RepoUrl": "https://github.com/FBoucher/AzUnzipEverything.git",
            "branch": "master",
            "publishRunbook": true,
            "IsManualIntegration": true
        }
    }
]
```
  
To be sure that everything is fully automatic the properties **publishRunbook** and **IsManualIntegration** must be set as `true`. Otherwise, you will need to do a synchronization between your Git (in this case on GitHub), and the Git in Azure.  
  
> There is excellent documentation that explains many deferent scenarios to [Automate resource deployment for your function app in Azure Functions](https://docs.microsoft.com/en-us/azure/azure-functions/functions-infrastructure-as-code?WT.mc_id=devto-blog-frbouche)

### Real Sample: Azure Unzip Everything

To deploy the project [AzUnzipEverything available on GitHub](https://github.com/FBoucher/AzUnzipEverything), I needed one more Azure Storage with pre-define containers (folders).  
  
![](https://thepracticaldev.s3.amazonaws.com/i/3a9qxuhszz2vt7oxsqg7.png)
  
Of course, all the source code of both the Azure Function and the ARM template are available on GitHub, but let me highlight how the containers are defined from an ARM template.  

```json
"resources": [
    {
        "type": "blobServices/containers",
        "apiVersion": "2018-07-01",
        "name": "[concat('default/', 'input-files')]",
        "dependsOn": [
            "storageFiles"
        ],
        "properties": {
            "publicAccess": "Blob"
        }
    }
]
```
  
Just like with **sourcecontrol** , we will need to add a list of sub-resources to our storage account. The name MUST start by `'default/'`.  
  
## Part 2 - Four Deployment Options

Now that we have a template that describes our needs we just need to deploy it. There are multiple ways it could be done, but let's see four of them.  
  
### Deploy from the Azure Portal

Navigate to the Azure Portal (https://azure.portal.com), from your favorite browser and search for "deploy a custom template" directly in the search bar located at the top of the screen (in the middle). Or go at https://portal.azure.com/#create/Microsoft.Template. One in the Custom deployment page, click on the link **Build your own template in the editor**. From there, you can copy-paste or upload your ARM template. You need to save it to see the real deployment form.  
  
![](https://thepracticaldev.s3.amazonaws.com/i/77mtjip9h4ch4wubr732.png)
  
### Deploy with a script

Would it be in PowerShell or in Azure CLI you can easily deploy your template with these two commands.  
  
In Azure CLI  
  
``` bash
    # create resource group
    az group create -n AzUnzipEverything -l eastus

    # deploy it
    az group deployment create -n cloud5mins -g AzUnzipEverything --template-file "deployment\deployAzure.json" --parameters "deployment\deployAzure.parameters.json"  
```

In PowerShell  
  
```powershell
    # create resource group
    New-AzResourceGroup -Name AzUnzipEverything -Location eastus

    # deploy it
    New-AzResourceGroupDeployment -ResourceGroupName  AzUnzipEverything -TemplateFile deployment\deployAzure.json
```

### Deploy to Azure Button

One of the best way to help people to deploy your solution in their Azure subscription is the Deploy to Azure Button.  
  
[![](https://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FFBoucher%2FAzUnzipEverything%2Fmaster%2Fdeployment%2FdeployAzure.json?WT.mc_id=cloud5mins-github-frbouche)  
  
You need to create an image link (in HTML or Markdown) to this to a special destination build in two-part.  
  
The first one is a link to the Azure Portal:  
  
```
https://portal.azure.com/#create/Microsoft.Template/uri/
```
  
And the second one is the location of your ARM template:  
  
```
https%3A%2F%2Fraw.githubusercontent.com%2FFBoucher%2FAzUnzipEverything%2Fmaster%2Fdeployment%2FdeployAzure.json
```
  
However, this URL needs to be encoded. There is plenty of encoders online, but you can also do it from the terminal with the following command (A big thanks to [@BrettMiller_IT](https://twitter.com/BrettMiller_IT) who showed me this trick during one of my live streams).  

```powershell
[System.Web.HttpUtility]::UrlEncode("https://raw.githubusercontent.com/FBoucher/Not-a-Dog-Workshop/master/deployment/deployAzure.json")
```
  
Clicking the button will bring the user at the same page on the Azure Portal but in the user subscription.

### Azure DevOps Pipeline

From the Azure DevOps portal (https://dev.azure.com), select your project and create a new Release Pipeline. Click on the **+ Add an artifact** button to connect your Git repository.  
  
![](https://thepracticaldev.s3.amazonaws.com/i/c6rqf0c23kc0rag5trp0.png)

Once it's added, you need to add a task the current job. Click on the link **1 job, 0 task** (4). Now you just need to specify your Azure subscription, the name of the resource group and select the location of your ARM template inside your repository. To make the deployment automatic with each push in the repository, click that little lightning bolt and enable the _Continuous deployment trigger_.  
  
![](https://thepracticaldev.s3.amazonaws.com/i/vm5drd4d2ox4qixg6tse.png)

## Wrapping-up
Voila, you know have four different ways to deploy your Azure Function automatically. But don't take my word for it, try it yourself! If you need more details you can visit the [project on GitHub](https://github.com/FBoucher/AzUnzipEverything) or watch this video where I demo the content of this post.  
 
{% youtube b54Guh_nDjo%}