In our introduction to Azure Resource Manager Templates we wrote a template from scratch. Whenever we used a name for a resource we had to use the full name. Imagine having to change this value. Sure you could find and replace all instance, but we all know how this could go wrong.
For a basic introduction also see our previous article on Azure Resource Manager Templates 101.
Variables
Of course ARM has a mechanisms to deal with this. Under the variables property you can define key value pairs. Later you reference the value by it’s key. That way when you want to change the value you only have to do it once.
"variables": { "aspName": "demo-asp1", "aspSku": "F1", "aspCapacity": "1", }
To reference the value use the variables function.
"resources": [{ "name": "[variables('aspName')]", "sku": { "name": "[variables('aspSku')]", "capacity": "[variables('aspCapacity')]" } }]
Also you can compose variables using other variables and functions.
"variables": { "prefix": "demo-", "asp-name": "[concat(variables('prefix'),'asp1')]", "location": "[resourceGroup().location]" }
Deployment parameters
Variables centralize the values you have to use throughout your template. But they still live in your deployment files. Imagine you want to deploy the same set of resources for a production, a staging and a development environment. Ideally working from one master deployment file.
You can achieve this by using ARM parameters. You can supply the values for these parameters from a file, via the command line or the user interface.
The parameters property accepts a set of parameter names and the type expected.
"parameters": { "name": { "type": "string", }, "db": { "type": "string", "minLength": 3, "maxLength": 50, "metadata": { "description": "Name of database to create" } } }
Type is the only required property for every type. To provide a description of the parameter you add the metadata property as shown. This available for any type.
Available types are:
- string (i.e. “alice”),
- int (a number 42),
- bool (i.e. true or false),
- object (a JSON notation, i.e. { firstName: “alice”, lastName: “doe” }) or
- array (a JavaScript array, i.e. [1,2,3] or [“a”,”b”,”c”]).
"parameters": { "instances": { "type": "int", "minValue": 1, "maxValue": 10, "metadata": { "description": "Number of instances to create" } }, "names": { "type": "array", "minLength": 1, "maxLength": 10, "metadata": { "description": "Names of instances" } }, "level": { "type": "string", "allowedValues": [ "Small", "Medium", "Large" ], "defaultValue": "Small" }, }
The minLength and maxLength properties are available for string and array. For the latter is defines how many values must be provided at least and at most.
"parameters": { "level": { "type": "string", "allowedValues": [ "Small", "Medium", "Large" ], "defaultValue": "Small" }, }
If you want to restrict the values a parameter may have, use the property allowedValues. You can specify a default using property defaultValue. This works for string and int.
Using parameters is analog to variables:
"count": "[parameters('level')]"
Deploying with parameters
You can specify each parameter in the PowerShell command.
New-AzureRmResourceGroupDeployment -Name Demo -ResourceGroupName Demo -TemplateFile demo.json -prefix demoprefix
You can also create a parameters file. The contents are a similar to a resource definition file. But they contain only the schema, contentVersion and parameters properties.
{ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "prefix": { "value": "demoprefix" } } }
Then you run the PowerShell command using the TemplateParameterFile parameter.
New-AzureRmResourceGroupDeployment -Name Demo -ResourceGroupName Demo -TemplateFile demo.json -TemplateParameterFile demo.params.json
To load the file from a remote URI instead, use the TemplateParameterUri
parameter.
See Microsoft Docs for more information on deploying Azure Resource Manager Templates.
Functions
You can use a number of functions when setting values in templates. Some of the most used ones are:
- int(“1”) – converts your string to an integer
- add(1,2), mul(1,2), sub(2,1), div(4,2) – simple arithmetic operations
- concat(“abc”,”def”) or concat([a,b,c],[d,e,f]) – puts together two strings or arrays
- replace(“a.b.c”, “.”, “-“) – replaces parts of strings, e.g. the result here is “a-b-c”.
- split(“a.b.c”, “.”) – splits a string into an array, e.g. the result here is [“a”,”b”,”c”]
- substring(“azure”,2,1) – returns a string with a specific length from another string, e.g. the result here is “u”.
- toLower,toUpper – makes strings upper or lower case
- uniqueString(“a”,”b”,”c”,”d”) – creates a hash of the input values, e.g. resource group, parent name or even date
Be aware that you cannot use functions (or any dynamic expression) in parameter files.
See Microsoft Docs for more information on Azure Resource Manager Template functions.
Loops
If you want to create a resource more than one time there are two options. You can copy paste the resource definition. But what happens if you want a parameter to decide how often to create the resource.
For this you can use the special “copy” property on the resource definition.
"resources": [{ "name": "[concat(variables('aspName'), copyIndex())]", "sku": { "name": "[variables('aspSku')]", "capacity": "[variables('aspCapacity')]" }, "copy": { "name": "aspcopy", "count": "[parameters('aspCount')]" } }]
Note the copyIndex function used as a suffix in the name. This add a unique number to the resource name. Otherwise the second copy would fail because names must be unique. This will number the site something0, something1, … . If you would rather start with 1 simply use copyIndex(1).
You cannot use copy on child resources but rather only on top-level resources.
Another helpful usecase is using copy together with an array of names. Assume for example you want to create 3 identical resources but with different names.
"parameters": { "country": { "type": "array", "defaultValue": [ "Vienna", "Berlin", "Barcelona" ] } }, "resources": [{ "name": "[concat(variables('aspName'), '-', parameters('country')[copyIndex()])]", "copy": { "name": "countrywebsite", "count": "[length(parameters('country'))]" } }]
You pass an array of countries in as a parameter. The copy count then uses the length function to determine how many items were passed. Then the suffix for the name is read from that same array using copyIndex.
See Microsoft Docs for more information on loops in Azure Resource Manager Templates.