In my last post, I went over JSON as it pertains to Azure ARM Templates. In this post, I’m going to dig deeper into the parts that make up an ARM Template. I will use a simple VNet deployment as an example. The goal is to define each part of the ARM Template and how the pieces fit together to make a deployment.
The full example file I use in this post can be found here.
ARM templates have five sections. They include:
I will start with functions as they are referenced in the sections that follow. There are two types of functions in ARM Templates, template functions, and user functions. Below I focus on template functions that are pre-defined and evaluated at the time of the deployment. The examples below don’t use user functions and I leave that out for brevity.
Template functions work by calling the function name followed by parameter in single parentheses. Enclosed the parameter in single quotes. Single quotes are not allowed in a JSON file as mentioned in my previous post. However, at run time the function is evaluated and replaced by the literal value referenced in the parameter or variable section. The dnsServer example starts with calling the function “parameters” and passing the function parameter “dnsServers”.
The function is enclosed in brackets to identifie it as a function.
Lastly, the function is enclosed in double quotes, making it a JSON string literal (otherwise it would be interpreted as an array).
At run time, the above function “[parameters(‘dnsServers’)]” is interpreted and replaced with “10.115.115.10”
The Parameters section contains user configurable variables. Parameters can be modified at the time of deployment with the use of a parameter file. Also, if deployed through the portal the user will get text boxes requesting input for the deployment.
Take a look at the “dnsServers” parameter below. This is an object belonging to parameters. The properties of this object are represented by another series of key-value pair objects. One of them, “allowedValues” value is an array, as indicated by the  brackets.
The schema of dnsServers looks like the image below.
Parameters are not required in an ARM deployment. Parameters aid the readability and the reusability of a template. With some modifications to the parameter section, this template could be deployed in many different environments.
The only required parameter values include:
paramterName – Name of the parameter.
type – The JSON value allowed for the parameter. The values include string, int, bool, securitstring, object, secureObject, and array.
Parameters can have many properties. The optional parameters used in the example above and in this template include:
defaultValue – Sets a default value that can be overridden at the time of deployment.
allowedValues – Array object with a list of values that can be used. The deployment will not run if the override value does not match an allowed value.
metadata > description – metadata contains the object description. This is the parameter description visible on a portal deployment.
Once established, parameters can be called using the parameters function. This is a template function used to return the literal value of a parameter. For the example of dnsServers, the parameter is referenced like shown below.
Variables are, well, variables. Like parameters, they are not required for a deployment. The difference between variables and parameters is that parameters are exposed and can be modified during deployment. Variables are embedded in the template and can only be modified in the template, prior to run time.
Say you are a consultant and you reuse templates for different clients. There could be a “Client prefix” added as a variable that appends a prefix to resource names. This would change only once per client and not during deployment.
In their simplest form, variables are a key-value pair with the key used to reference the value in a template function. Call the variable template function similarly to the parameters function. The expression “[variables(‘location’)]” is evaluated to the location of the deployments resource group.
You may have noticed the “resourceGroup().location” expression in the above example. This is a resource function that references the location property of the resource group used during deployment. More information on the many different template functions that can be used to simplify templates can be found here.
ARM Templates deploy resources in parallel. This is good for speed, but sometimes resources depend on other resources. The dependsOn statement forces an order to resource deployment. In another example of the value of variables, I have a dependsOn statement that references a complex expression below. You can see it has a long concatenate statement for a network security group.
Below is the same template, replacing the complex “dependsOn” expression with a variable. This may seem trivial in a small deployment, but can significantly increase readability and decrease complexity in large deployments. Also, notice that variables can reference parameters, adding to their flexibility of use.
Parameters vs. Variables
Best practices is to use variables whenever possible and to limit parameters to items that need to be defined at the time of deployment. It is not uncommon to set up the template so all parameters are referenced by a variable, and only variables are used in the resource section of the template.
The resources section defines what Azure resources will be deployed and is a mandatory section of a template. The “resources” template key has an array value, indicated by the brackets that encloses the section.
The Resources array contains a list of complex JSON objects. The first object, shown below, represents the Virtual Network. Within that object are other objects and arrays used to define required and optional settings for the deployment.
A Microsoft website with details on resources is located here. This template was created based on Microsoft’s example located under Reference, Network, Virtual Network. Every resource will have several required key-value settings as defined below.
apiVersion – API version used for the deployment
Type – Azure resource being deployed
Name – name of the resource
Properties – list of optional and mandatory settings to create the resource
Other settings are optional. For example, I did not have to add Address Spaces, DHCP servers or Subnets. Those are required for my environment however and it wouldn’t be complete without them. I left out user-defined routing and VNet peering, but I could have added that if needed for my environment. Notice the use of parameters and variables in the resource section. This adds to the readability and reusability of the template.
Always start with clear plan for the deployment prior to creating the template. When creating a VM for example, first outline requirements such as VM, network adapter and storage account. Then identify the properties of each. For the VM, there will be the VM size, OS type, number of data disks. The network adapter will need the IP allocation, static or dynamic. What will the name of the storage account be and they type, LRS, GRS? Each one of these resources is a member of the resource section “array”. The properties defined in each resource.
The assortment of resources and properties of each can be overwhelming. This is where understanding the structure of JSON can help. The resource section is an array, each section underneath is just a key-value pair that defines the resource.
A note on API versions. Some sample templates use an expression to reference the latest version instead of an explicit date like this example. I advise against dynamic API versions. It is possible that schema changes with an updated API will break templates.
The Output section returns information at the end of the deployment. This can be informational or used by applications if the deployment was submitted programmatically. One example could be a deployment with a public IP. The IP is unknown until it’s assigned during the deployment. The Output section could return the IP information.
Options for outputs are the name, they type and the value. In the example to follow, I output the variable ns1Id and the VNet name
Retrieve outputs with the Get-AzureRmResourceGroupDeployment command. The deployment below was run with the command:
New-AzureRmResourceGroupDeployment -Name TestDeploy -ResourceGroupName TestVNet -TemplateFile .\Template2.json
Retrieve deployment details with the following command:
Get-AzureRmResourceGroupDeployment -Name TestDeploy -ResourceGroupName TestVNet
Although formatting is shifted to display on the screen, the output is correct.
Test a template with the Test-AzureRmResourceGroupDeployment command prior to running the deployment. This command requires a Resource Group and a reference to the template file. There is no need for a deployment name.
Test-AzureRmResourceGroupDeployment -ResourceGroupName TestVNet -TemplateFile .\Template2.json
The command will run and return an error if it finds one. Otherwise, no output indicates there was no issue.
Congratulations on making it to the end! I just scratched the surface on resources in an ARM template. Stay tuned for a deeper dive into the resource section.