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.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 with the extensions Azure Resource Manager Tools and Azure Resource Manager Snippets is particularly efficient.
{
"$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 Service Plan
"sku": {
"name": "Y1",
"tier": "Dynamic"
}
Of course, you can use the other SKU if you prefer.
The Function App
"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.
{
"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.
"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
Azure Unzip Everything
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.
"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
Deploy with a script
In Azure CLI
# 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
# create resource group
New-AzResourceGroup -Name AzUnzipEverything -Location eastus
# deploy it
New-AzResourceGroupDeployment -ResourceGroupName AzUnzipEverything -TemplateFile deployment\deployAzure.json
Deploy to Azure Button
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 who showed me this trick during one of my live streams).
[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.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.