Improve this page

Each Azure Resource and its template must be compliant with the following standards.

Azure Resource Guidelines

External information on ARM template Best Practices:

ARM Template Description A markdown file is created per component which contains a description of the template itself.

Nested templates Avoid nested templates to minimize complexity. Make use of loosely coupled components and define the relation within the release definition. If necessary, the nested template must be stored in the sub folder nested within the component folder.

Secrets & keys Keys and secrets are stored in a Key Vault which must be part of the application or landing zone Resource Group.

Tagging The following limitations apply to tags:

Because of the maximum of 15 tags for a resource group, use as few tags as possible when creating an ARM template.

The following tags must be defined per resource within the Resources section of each ARM template.

  "parameters": {
     "tagBillingIdentifier": {
      "type": "string",
      "metadata": {
        "description": "BillingIdentifier tag."
      }
      "tags": {
        "TemplateVersion": "tagTemplateVersion",
        "BillingIdentifier": "[parameters('tagBillingIdentifier')]"
      },

The tag for the template version tagTemplateVersion will be set during the build with a small PowerShell script.

##-----------------------------------------------------------------------
## <copyright file="SetARMTagsDuringBuild.ps1">(c) Sogeti.</copyright>
##-----------------------------------------------------------------------
# Look for the build number and add it as a tag to the ARM template resource group.

[CmdletBinding()]

$fileName = "azuredeploy.json"

Write "BUILD_SOURCESDIRECTORY: $Env:BUILD_SOURCESDIRECTORY"
Write "BUILD_BUILDNUMBER: $Env:BUILD_BUILDNUMBER"
Write "ARM FOLDER: $($args[0])"

$TemplateFile = "$Env:BUILD_SOURCESDIRECTORY\$($args[0])\$fileName"
Write "ARM FILE: $TemplateFile"

$NewVersion = $Env:BUILD_BUILDNUMBER
Write "VERSION: $NewVersion"

$content = Get-Content("$TemplateFile")
$content = $content.replace("tagTemplateVersion", $NewVersion)
$content | out-file $TemplateFile

Security Security aspects of a resource are unique for each of the ARM templates. A common security principle which applies for ARM templates: Encryption of data in transit and at rest. The deployed Azure solution must be compliant with the Azure Security Center recommendations

Monitoring Azure Diagnostics must be enabled on all individual resources and point to the foundation OMS Workspace tenant. Application Insights is enabled for all application services.

Backup Backup is used for logical and technical failure. Backup functionality should be enabled by using the ARM template if possible. Disaster Recovery and Archiving requires a separate process, setup or template.

ARM Template guidelines

Parameter section

Parameter, variable and output names should follow lowerCamelCasing, for example:


"webIndexOffSet":

Use the name of the resource property in the parameter, variable and output names and use the abbreviation of the resource type as a prefix. Use only characters and don’t use hyphens. For example in the template for the Key Vault, we use the following parameter:

"kvtSkuName":

the following variable:

"kvtName":

and the following output:

"kvtResourceId":

They all start with the abbreviation of the resource type, in this case “kvt” for “Key Vault”, followed by the the name of the resource property.

The parameter section specifies which values can be entered into the template. The available options for the parameter section can be found here.

Different type of parameters are used within ARM templates:

Mandatory functional parameters

"billingIdentifier ": {
  "type": "string",
  "metadata": {
    "description": "Cost center"
  }
},

For every parameter in the template add a “metadata” property with a description. Make the description comprehensible for the people that are going to use the templates, for example:

  "metadata": {
    "description": "The name of the Application Service Plan."
  }

Variables section

The variables section contains the logic to construct resource names based on the naming convention defined. Different functions are used to construct these values.

The various functions for constructing these values can be found here.

The available options for the variables section can be found here.

The following example defines some default variables:

Multi value

"environment": {
  "westeurope": "W",
  "northeurope": "N",
  "global": "G"
},

Construct name

"redisCacheName": "[toLower(concat(resourceGroup().name, '-RCH'))]",

Resource ID reference

"redisCacheStorageID": "[concat(resourceGroup().id, '/providers/Microsoft.Storage/storageAccounts/', variables('rediscacheStorageName'))]",

Condition based variables

"vmOsDiskManagedDiskStorageType": "[if(equals(parameters('diskType'),'SSD'),'Premium_LRS','Standard_LRS')]",

If a variable is only used once in the template, it is better not to make a separate variable for it. On the other hand, if it promotes readability, it is wise to create an extra variable.

Resources section

The resources section define the Azure resources and its properties.

The available options for the resources section can be found here.

Below a snippet of the Azure VM ARM template is shown. Please note the order of the properties used.

"resources": [
"name": "[toLower(concat(variables('vmPrefix'),palest(copyindex(parameters('vmIndexOffset')),3,'0')))]",
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "[variables('vmApiVersion')]",
"location": "[resourceGroup().location]",
"tags": "[]",
"dependsOn": [
  "copyNic",
  "[variables('vmAvailabilitySetName')]"
],
"copy": {
  "name": "copyVm",
  "count": "[parameters('vmCount')]"
},
"properties": {
  "hardwareProfile": {
    "vmSize": "[parameters('vmVmSize')]"
  },
  ...
],

Output section

The output section of the ARM Template passes the information of the created resource as the output of the completed deployment. This information can be used on the command line or can be passed to a next task in the release. If a template generates something secret that nobody may see, use a Key Vault to store it, otherwise use the output section.

The output section should always contain:

"outputs": {
  "keyVaultName": {
    "type": "string",
    "value": "[variables('keyVaultName')]"
  },
  "keyVaultResourceId": {
    "type": "string",
    "value": "[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]"
  },
}

Optionally extra output resources can be added:

"keyVaultUri": {
  "type": "string",
  "value": "[reference(variables('keyVaultName')).VaultUri]"
}