Azure Resource Manager (ARM) Template Tips

What are ARM templates?

An Azure Resource Manager template is a declarative JSON file that defines the infrastructure and configuration of your project. In this agile world we live in and adopting the process of shifting left and automating our deployments we want to add our infrastructure to our Software Development Lifecycle as well, this is known as Infrastructure as Code. This is the practice that further reduces the gap between development and operations. Here we define and version our infrastructure in code just as we do our application.

Now that we have a basic understanding of the purpose of ARM templates, I want to share a couple of tips and best practices for using them.

Use Functions

ARM templates provide you with the ability to create your own functions within a template. This functionality allows you to create functions that would simplify the process of creating your resources in Azure. For example, say all the resources for a template belongs to the finance department, you probably want to prefix these resources with something like “fin” or 'financeDept'. With user-defined functions, you can do just that. See the sample below:

"functions": [
{
"namespace": "finance",
"members": {
"prefixDepartment": {
"parameters": [
{
"name": "resourceName",
"type": "string"
}
],
"output": {
"type": "string",
"value": "[concat('financeDep-', toLower(parameters('resourceName')))]"
}
}
}
}
],

The function defined above will prefix 'financeDept-' to the name of a resource. Say we are creating a storage account for the finance department and name it 'storage2020', the resource will be named 'financeDept-storage2020' when the template is deployed.

Nested Templates

Next up we have nested templates. When creating an ARM template, it tends to grow in size pretty quick and nobody wants to go through a JSON file that is thousands of lines long. This is where can split our infrastructure into different templates that can then be called from one single trigger template.

In sticking with the example of creating a template for the finance department above, let’s say we need to create storage accounts, VM’s, a Load balancer, configure the networking etc. Doing all of this in a single template can easily load your template. In this scenario, we can create a template for each individual resource and call them from the main template. Below is an example of nesting a template:

{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"StorageAccountName": {
"type": "String",
"defaultValue": "storage2020"
}
},
"variables": {
"templatelink": "C:/Projects/demoARM/templates/newStorageAccount.json"
},
"functions": [
{
"namespace": "finance",
"members": {
"prefixDepartment": {
"parameters": [
{
"name": "resourceName",
"type": "string"
}
],
"output": {
"type": "string",
"value": "[concat('financeDep-', toLower(parameters('resourceName')))]"
}
}
}
}
],
"resources": [
{
"apiVersion": "2015-01-01",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "incremental",
"templateLink": {
"uri": "[variables('templatelink')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"StorageAccountName": {
"value": "[finance.prefixDepartment(parameters('StorageAccountName'))]"
}
}
}
}
]
}
view raw nested-template.json hosted with ❤ by GitHub

The above template creates a new storage account. The definition for the storage is defined in a separate template. We create a deployment resource in our main template and specify the location of the template we want to deploy. The location is specified with the 'templateLink' variable.

Resource groups & subscriptions

You would typically create a template that deploys resources to a single resource group, but what if you need to deploy resources to multiple regions? Below are some things to keep in mind when creating such a template:

  • A single deployment is limited to 5 resources groups
  • If no resource group is set then the values for the parent template are used
  • The account doing the deployment needs to have permission to the subscription

Conditional deployments

Sometimes you might have to skip certain deployments. Suppose that those resources already exist, Use the condition element to specify whether the resource is deployed. True if you want the resource to be deployed and False if not.

{
"condition": "[equals(parameters('newOrExisting'),'new')]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2017-06-01",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[variables('storageAccountType')]"
},
"kind": "Storage",
"properties": {}
}

Above we are adding a condition to the deployment of a storage account using the **newOrExisting** parameter. If the parameter is set to 'New' a storage account be deployed and not if it set to anything else.

Deployment modes

There are two types of deployment modes for ARM templates, one is the default incremental mode which is pretty self-explanatory. The other is Complete mode.

The traits of incremental deployments are as follows:

  • All resource defined in the template will be created in the specified resource group
  • Update the configuration for a resource that differs from what is defined in the template
  • All resources in the resource group that are not part of the template will be ignored

The difference between an incremental deployment and complete deployment hinges mostly on the third trait specified above for incremental deployments. if you deploy in complete mode, your resource group will have exactly what is in your template, meaning any existent resource in your resource group that is not specified in your template will be deleted.

Conclusion

Above are a couple of tips that I find very helpful when working with ARM templates, especially when your project starts growing in complexity and you the need to minimize time and effort is of most importance.

Share: