Azure Resource Manager templates allow rapid and repeatable deployment of resources to Azure. It is a deployment template and thus can be interpreted through Powershell, CLI, the Azure portal, and even REST API. ARM templates are surprisingly flexible for JSON files- allowing for variables, conditionals, and loops. These programmatic behaviors are handled during deployment, so it is possible for a seemingly valid template fail during deployment. Ultimately, ARM templates saves us the trouble of deploying resources individually.
Not quite starting from scratch.
Users familiar with the Azure portal will know that you can export resources within a resource group as a template, this exports an ARM template for resource deployment as well as scripts to run these templates in a variety of programming languages. This is a valuable starting point. Of course, starting from scratch is also possible. While this requires more typing, it is a valuable exercise. Ultimately, one will need to have some kind of architecture in mind when writing and deploying the template. Thus, It is often worthwhile to manually create part this architecture for testing anyways.
The problem with these exported templates is that they are over constrained- it tries to exactly replicate the previous deployment including the IDs and names of each resource. That's well and good If we want to deploy an new resource group exactly like the old one, but what if we want to deploy a variable amount of VMs or change how resource names are generated? That is where the configurability of ARM templates shines through.
ARM Template Features
Before we go further, I would recommend keeping these two pages handy: Resource Manager on Azure Documentation as a gateway for all Microsoft provided ARM documentation; and Create Azure Resource Manager Template for a quick start for creating templates. Both will go into more detail than this guide.
Parameters, Variables, and Functions
ARM deployments allow for you to specify a parameter file saving users the trouble of directly editing the template each time. Parameters take the following form:
"type": ... ,
"defaultValue": ... ,
We can force parameters to take a certain type such as string or number. We can also specify default values for parameters if no input is provided. Lastly, Allowed values are helpful for settings with a limited amount of options- e.g. VM sizes. Usually, the name of the resource group and the deployment location are helpful parameters to include. Parameters can be referenced later in the template by the following form:
In general, values with variable output are surrounded by bracket in ARM templates. The double quotes (") are necessary even for numeric outputs.
Variables use a similar format to parameters and can be referenced using the same syntax, though they only contain a value.
The "variables" used in ARM templates can be considered constants that are initialized at the start of execution and referenced throughout the rest of the template. Variables are usually used to encapsulate long expressions such as the resource ID.
Lastly, ARM provides some inbuilt functions. For deployments some helpful housekeeping functions are...
- uniqueString() - Returns 13 character long hash based on inputs, useful for generating unique resource names.
- tolower(), substring() - Both function manipulate strings and are again useful for creating unique resource names.
- resourceId()- Returns the unique identifier of a resource, which is needed for declaring dependencies.
ARM also allows us to declare custom functions. Functions accept user defined inputs and returns an output JSON object. To avoid naming conflicts with template functions, you will have to specify a name space. As I have limited experience working with these custom functions, I would recommend checking out documentation for more information.
A really helpful object is the copy object, which allows for the deployment of a resource multiple times. For example, we want to deploy 3 VMs behind a load balancer. Without the copy object, we would have to declare the VM object three times within the template film. With the copy object, we could get away with writing only 1 base VM object.
By including the copy object, we get access to the copyIndex() function. This returns the loop iteration allowing the resources to have different names. Furthermore, this copy index can be offset to start from a nonzero number. For example, we can use the function to open a series of ports on a load balancer- 5000, 5001, 5002, and so forth. This is one method of accessing VMs with no public IP address behind a load balancer.
Over the course of this guide, I went over some interesting features of ARM templates. For more information about the actual resources- VMs, VNets, NICs, etc.- refer to the documentation. Each resource requires its own fields as well as multitude of optional parameters depending on your deployment. Lastly, Microsoft provides a large number of quick start templates for ARM templates. Don't be afraid to use these.