# Challenge 7: ARM - Azure Resource Manager (ARM) Templates

# Here is what you will learn ๐ŸŽฏ

  • get to know the Azure Resource Manager and Resource Providers
  • learn about how ARM templates are structured and what you can do with them
  • create a basic template and deploy them via the Azure Portal
  • work with parameters and deploy using Azure CLI

# Just to be on the same page

Before we can get into Azure Resource Manager and the related templates, we need to clarify some terms:

  • Resource โ€“ an element manageable via Azure. For example: a virtual machine, a database, a web app etc.
  • Resource Group โ€“ a container for resources. A resource can not exist in Azure without a ResourceGroup (RG). Deployments of resources are always executed on an RG. Typically, resources with the same lifecycle are grouped into one resource group.
  • Resource Provider โ€“ an Azure service for creating a resource through the Azure Resource Manager. For example, โ€œMicrosoft.Webโ€ to create a web app, โ€œMicrosoft.Storageโ€ to create a storage account etc.
  • Azure Resource Manager (ARM) Templates โ€“ a JSON file that describes one or more resources that are deployed into a Resource Group. The template can be used to consistently and repeatedly provision the resources.

One great advantage when using ARM templates, is the traceability of changes to your infrastructure. Templates can be stored together with the source code of your application in the code repository (Infratructure as Code).

If you have established Continuous Integration / Deployment in your development process (which we will do on Day 4), you can execute the deployment of the infrastructure from Jenkins, TeamCity or Azure DevOps. No one has to worry about an update of the environment โ€“ web apps, databases, caches etc. will be created and configured automatically โ€“ no manual steps are necessary (which can be error-prone, as we all know).

# Table of Contents

  1. Azure Resource Manager and Resource Providers
  2. ARM Templates
  3. ARM with Parameter File
  4. Create your first ARM Template
  5. Cleanup

# Azure Resource Manager and Resource Providers

The Azure Resource Manager is the deployment and management service for Azure. It provides a management layer that enables you to create, update, and delete resources in your Azure subscription.

You can access the resource manager through several ways:

  • Azure Portal
  • Azure Powershell
  • Azure CLI
  • plain REST Calls
  • SDKs

arm

As you can see in the picture above, the Resource Manager is made of several Resource Providers (RP) that are ultimately responsible for provisioning the requested service/resource. Resource providers can be independently enabled or disabled. To see, what RPs are active in your subscription, you can either check the Portal (Subscription --> Resource Providers) or use Azure CLI (e.g. in the Cloud Shell (opens new window)) to query the resource manager:

$ az provider list -o table

Namespace                               RegistrationPolicy    RegistrationState
--------------------------------------  --------------------  -------------------
Microsoft.ChangeAnalysis                RegistrationRequired  Registered
Microsoft.EventGrid                     RegistrationRequired  Registered
Microsoft.Logic                         RegistrationRequired  Registered
Microsoft.Security                      RegistrationRequired  Registered
Microsoft.PolicyInsights                RegistrationRequired  Registered
Microsoft.AlertsManagement              RegistrationRequired  Registered
Microsoft.Advisor                       RegistrationRequired  Registered
Microsoft.Web                           RegistrationRequired  Registered
microsoft.insights                      RegistrationRequired  Registered
Microsoft.KeyVault                      RegistrationRequired  Registered
Microsoft.Storage                       RegistrationRequired  Registered
Microsoft.Portal                        RegistrationFree      Registered
Microsoft.DocumentDB                    RegistrationRequired  Registered
Microsoft.Search                        RegistrationRequired  Registered
Microsoft.Cdn                           RegistrationRequired  Registered
Microsoft.Cache                         RegistrationRequired  Registered
Microsoft.ResourceHealth                RegistrationRequired  Registered
Microsoft.Sql                           RegistrationRequired  Registered
...
...
...

providers

TIP

๐Ÿ“ Resource providers talk REST (what a surprise ๐Ÿ˜‰)
Here is an example of a REST call that creates a storage account:

PUT
https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/mystorageaccount?api-version=2016-01-01
REQUEST BODY
{
  "location": "westus",
  "sku": {
    "name": "Standard_LRS"
  },
  "kind": "Storage",
  "properties": {}
}

As you can see, deployments in Azure will always be executed against a resource group, as mentioned before! In this sample, the template will be ultimately picked-up by the Microsoft.Storage resource provider, which then will be responsible for creating a Storage Account for you.

But of course - nobody wants to construct native REST calls. Instead we focus on the json payload and let Azure CLI or powershell do the REST communication/transmission part.
The json document we are about to create is an ARM Template

# ARM Templates

A full ARM template is more than just a REST json payload. It usually consists of several parts:

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {},
  "variables": {},
  "resources": [],
  "outputs": {}
}
  • parameters โ€“ Parameters (e.g. a virtual machine's name) that are passed from the outside to the template. Typically, from the command line or your deployment tool (e.g. Terraform, Azure DevOps, Jenkins...)
  • variables โ€“ variables for internal use. Typically, parameters are โ€œeditedโ€, e.g. names are concatenated strings and stored in variables for later use.
  • resources โ€“ the actual resources to be created
  • outputs โ€“ Output parameters that are returned to the caller after the template has been successfully applied. With outputs you can achieve multi-stage deployments by passing outputs to the next ARM template in your deployment chain.

# A Basic Template

Sticking to our sample from above (Storage Account), let's create a basic template that will create a Storage Account and makes use of a very helpful feature (Template Functions). Here it is:

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "type": "string",
      "metadata": {
        "description": "The name of the storage account to be created."
      }
    },
    "storageAccountType": {
      "type": "string",
      "defaultValue": "Standard_LRS",
      "allowedValues": [
        "Standard_LRS",
        "Standard_GRS",
        "Standard_ZRS",
        "Premium_LRS"
      ],
      "metadata": {
        "description": "Storage Account type"
      }
    }
  },
  "variables": {},
  "resources": [
    {
      "name": "[parameters('storageAccountName')]",
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2015-06-15",
      "location": "[resourceGroup().location]",
      "tags": {
        "displayName": "[parameters('storageAccountName')]"
      },
      "properties": {
        "accountType": "[parameters('storageAccountType')]"
      }
    }
  ],
  "outputs": {
    "storageAccountConnectionString": {
      "type": "string",
      "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value]"
    }
  }
}

TIP

๐Ÿ“ As you can see, we are able to use functions to do dynamic stuff within a template, e.g. reading keys (listKeys()) or using parameter (parameters()). There are, of course, other functions e.g. for string manipulation (concatenate, padLeft, split...), numeric functions, comparison functions, conditionals etc. You can find all available template functions and their documentation here: ARM template functions (opens new window). Please make yourself familiar with the list!

# Let's deploy the template

There are several ways to kick off an ARM template deployment:

  • Azure cli
  • PowerShell
  • Azure Portal -> '+' -> 'Template Deployment'
  • ...

We will try each of these to learn its haptic.

  1. We use the Azure Portal. You may find it strange to use a web portal that offers wizards and use it to process our template. However, when testing/developing templates errors will occur. In this case the portal offers immediate feedback e.g. for validation errors. So do:
[Azure Portal] 
-> '+' 
-> 'Template deployment' 
-> Create 
-> 'Build your own template in the editor'
  1. Copy and paste the above ARM template into the portal editor window

Azure Portal Template Deployment

  1. Hit save. This will bring you to the next page.

TIP

๐Ÿ“ This page contains:
- fixed elements: The deployment scope - where should your resources be deployed (subscription & RG) - dynamic elements: The parameters - defined in the template will define the 'look' and hence what the user needs to provide.

Template Deployment Parameters

  1. Enter a unique lowercase value for the storage account and kick off the deployment.

  2. When finished take a look at the outputs section:

Template Deployment Output

TIP

๐Ÿ“ Think of it that way: You can define the outputs of a deployment in the template for later use e.g. if you want to upload something in the next step of your deployment and need the access key for this.

# ARM with Parameter File

You have noticed that you have been asked to enter a value for the parameter storageAccountName. In a fully automated deployment, this is not really acceptable.
Using e.g. the Azure CLI (opens new window) you can specify an answer parameter file that goes along with the deployment file.

az deployment group create -g basicarm-rg -n firsttemplate --template-file=./azuredeploy.json --parameters=@azuredeploy.params.json

Doing so you can set up parameter files for different environments, e.g.:

  • azuredeploy.params.DEV.json
  • azuredeploy.params.TEST.json
  • azuredeploy.params.PROD.json
  • ...

TIP

๐Ÿ“ One template + multiple parameter files. That's how most people do it.

Example for our storage account parameter:

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "storageAccountName": {
            "type": "string",
            "metadata": {
                "description": "The name of the storage account to be created."
            }
        },
.
.
.

The corresponding parameter file would look like this.

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "storageAccountName": {
            "value": "adcmyarmsa"
        }
    }
}

# Create your first ARM Template

Now I want you to create your first ARM Template. A speed course - based on the Tutorial: Create and deploy your first ARM template (opens new window)

You will:

  • Open Visual Studio Code and create an empty ARM template file.
  • Add a resource to it.
  • Use an ARM function
  • Create a parameter
  • Generate a parameter file
  • Deploy it using Azure CLI
  1. Open Visual Studio Code and create an empty ARM template file.Use the following code snippet.
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "resources": [] 
}
  1. Add a resource to it. Put the json code snippet that defines a public IP address into the resources section of the template:
{
  "name": "string",
  "type": "Microsoft.Network/publicIPAddresses",
  "apiVersion": "2020-06-01",
  "location": "string",
  "sku": {
    "name": "usefulstring"
  },
  "properties": {
    "publicIPAllocationMethod": "usefulstring",
    "publicIPAddressVersion": "usefulstring"
  }
}

You need to replace the "usefulstring" with meaningful data. See the following links as reference:

  1. Use an ARM functio. Concentrate on:
  "location": "string",

Azure resources go into a resource group. A resource group is located in an azure region. So you may want to use the same region for your public IP Address as it's hosting resource group.
Lucky us. There is a function available - that we can use. Replace "string" with "[resourceGroup().location]"

As example you can compare your ARM Template with the following code snippet:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
      "IPAddress": {
          "type": "string",
          "metadata": {
              "description": "description"
           }
      }
   },
  "resources": [
      {
        "name": "myFirstIpAddressviaARM",
        "type": "Microsoft.Network/publicIPAddresses",
        "apiVersion": "2020-06-01",
        "location": "[resourceGroup().location]",
        "sku": {
            "name": "Basic",
            "tier": "Regional"
        },
        "properties": {
            "publicIPAllocationMethod": "Static",
            "publicIPAddressVersion": "IPv4"
        }
    }
  ]
}
  1. Create a parameter. Add the parameter section to the template - use out Basic Template as reference.

  2. Create a parameter file. Use VS Code to generate a parameter file for you and enter a value:

VS Code can generate parameter file

As example you can compare your parameters file with the following code snippet:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "IPAddress": {
            "value": "40.85.113.39"
        }
    }
}
  1. Kick off the deployment using Azure CLI. To Do this you can upload the template and parameter file to your Azure Cloud Shell:

Upload to Cloud Shell

Create a resource group first by executing e.g.:

az group create -l northeurope -n rg-MyFirstARM

Kick off the deployment by executing e.g.:

az deployment group create -g rg-MyFirstARM --template-file pip.json --parameters '@pip.parameters.json'

Result:

My First ARM Template result

WARNING

There exist two modes t deploy an ARM template:

  • Complete โ€“ resources that are not present in the template, but do exist in the resource group, are deleted.
  • Incremental (default)โ€“ resources that are not present in the template, but exist in the resource group, remain unchanged.

# Cleanup

Execute the following commands:

az group delete -n basicarm-rg --no-wait --yes
az group delete -n rg-MyFirstARM

โ—€ Previous challenge | ๐Ÿ”ผ Day 1 | Next challenge โ–ถ