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:
- Each resource or resource group can have a maximum of 15 tag name/value pairs. This limitation applies only to tags directly applied to the resource group or resource. A resource group can contain many resources that each have 15 tag name/value pairs. If you have more than 15 values that you need to associate with a resource, use a JSON string for the tag value. The JSON string can contain many values that are applied to a single tag name. This article shows an example of assigning a JSON string to the tag.
- The tag name is limited to 512 characters, and the tag value is limited to 256 characters. For storage accounts, the tag name is limited to 128 characters, and the tag value is limited to 256 characters.
- Tags applied to the resource group are not inherited by the resources in that resource group.
- Tags can’t be applied to classic resources such as Cloud Services.
- Tag names can’t contain these characters: <, >, %, &, \, ?, /
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.
- Template version
- Billing identifier
"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:
- Functional parameters; These type of parameters need to be filled by the Business Unit.
- Technical parameters; These type of parameters have a technical affect upon the end state of the component. The parameters are not mandatory but enable an optional switch for the end user to shape the solution.
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]"
}