Azure Deployment templates – Variables vs Parameters

I was having a conversation today with a fellow CSA, and found myself explaining an all-too-common question.  Doing a quick web search using some Bing-Fu, I came across 0 articles that seemed to make it simple.  This being a go-to place for simple cloud, I thought it might be a good topic to blog about.

Azure Deployment templates -> variables vs parameters.  What’s the difference and when should I use one over the other.

Simply put:

Variables should be used when you have a value that you would like to use in the “resources” section of your template (I call it the meat and potatoes section), but this same value may be used in multiple areas of the template, AND (this is the important difference between variables and parameters) you do not want the template deployer changing those values at deployment time.  Sure, its nice because you can change the value in one place, but it should be few and far between when these values get changed.  A great use-case might be calculations, or functions such as trim(), split(), concat(), etc…

That being said:

Parameters are those things that you want to be dynamic at deploy time.  These are things a template deployer (whether that be a person, web form, or automated process) will supply at template deployment time.  IE… names, urls, etc…  -> so this way your template will be Idempotent.

 

BEWARE -> using “auto-generated” templates -> such as the “export template” functionality in the portal, or one that I found out today with the Azure Data Factory tool that publishes a template during Git integration…  These tools don’t always parameterize the appropriate values, settings, names, etc…; and you may find that you will need to modify some of these templates by hand in order to make them Idempotent.

 

Here is an example of a Resource Group that I deployed 2 VMs in an availability set, behind a load balancer.  If you go to the Resource Group, on the menu select “Automation script” and look at the generated template, you will get something akin to the following (To simplify, I only picked 1 resource out of the many that were deployed)

Here is the parameters section:

"parameters": {
"virtualMachines_Web1_name": {
"defaultValue": "Web1",
"type": "String"
},
"virtualMachines_Web2_name": {
"defaultValue": "Web2",
"type": "String"
},
"loadBalancers_ExWebLB_name": {
"defaultValue": "ExWebLB",
"type": "String"
},
"publicIPAddresses_ExLBIP_name": {
"defaultValue": "ExLBIP",
"type": "String"
},
"networkInterfaces_web1863_name": {
"defaultValue": "web1863",
"type": "String"
},
"networkInterfaces_web2366_name": {
"defaultValue": "web2366",
"type": "String"
},
"availabilitySets_ExampleAS_name": {
"defaultValue": "ExampleAS",
"type": "String"
},
"virtualNetworks_Example_vnet_name": {
"defaultValue": "Example-vnet",
"type": "String"
},
"networkSecurityGroups_Web1_nsg_name": {
"defaultValue": "Web1-nsg",
"type": "String"
},
"networkSecurityGroups_Web2_nsg_name": {
"defaultValue": "Web2-nsg",
"type": "String"
},
"schedules_shutdown_computevm_web1_name": {
"defaultValue": "shutdown-computevm-web1",
"type": "String"
},
"schedules_shutdown_computevm_web2_name": {
"defaultValue": "shutdown-computevm-web2",
"type": "String"
},
"subnets_default_name": {
"defaultValue": "default",
"type": "String"
},
"securityRules_default_allow_rdp_name": {
"defaultValue": "default-allow-rdp",
"type": "String"
},
"securityRules_default_allow_rdp_name_1": {
"defaultValue": "default-allow-rdp",
"type": "String"
},
"virtualMachines_Web1_id": {
"defaultValue": "/subscriptions//resourceGroups/Example/providers/Microsoft.Compute/disks/Web1_OsDisk_1_c434f498411044a1a7522438aceb22bc",
"type": "String"
},
"virtualMachines_Web2_id": {
"defaultValue": "/subscriptions//resourceGroups/Example/providers/Microsoft.Compute/disks/Web2_OsDisk_1_57bb1064202f4f57bfb4dc35947058a6",
"type": "String"
},
"loadBalancers_ExWebLB_id": {
"defaultValue": "/subscriptions//resourceGroups/Example/providers/Microsoft.Network/loadBalancers/ExWebLB/frontendIPConfigurations/LoadBalancerFrontEnd",
"type": "String"
},
"loadBalancers_ExWebLB_id_1": {
"defaultValue": "/subscriptions//resourceGroups/Example/providers/Microsoft.Network/loadBalancers/ExWebLB/backendAddressPools/webPool",
"type": "String"
},
"loadBalancers_ExWebLB_id_2": {
"defaultValue": "/subscriptions//resourceGroups/Example/providers/Microsoft.Network/loadBalancers/ExWebLB/probes/Probe80",
"type": "String"
}
}

Here is the variables section
"variables": {},
Notice anything missing? How about ANY variables?
And for the sake of forever scrolling, here is one VM object that I will point out of the “meat and potatoes” also known as resources section:


{
"comments": "Generalized from resource: '/subscriptions//resourceGroups/Example/providers/Microsoft.Compute/virtualMachines/Web1'.",
"type": "Microsoft.Compute/virtualMachines",
"name": "[parameters('virtualMachines_Web1_name')]",
"apiVersion": "2017-03-30",
"location": "southcentralus",
"scale": null,
"properties": {
"availabilitySet": {
"id": "[resourceId('Microsoft.Compute/availabilitySets', parameters('availabilitySets_ExampleAS_name'))]"
},
"hardwareProfile": {
"vmSize": "Standard_D1_v2"
},
"storageProfile": {
"imageReference": {
"publisher": "MicrosoftWindowsServer",
"offer": "WindowsServer",
"sku": "2016-Datacenter",
"version": "latest"
},
"osDisk": {
"osType": "Windows",
"name": "[concat(parameters('virtualMachines_Web1_name'),'_OsDisk_1_c434f498411044a1a7522438aceb22bc')]",
"createOption": "FromImage",
"caching": "ReadWrite",
"managedDisk": {
"storageAccountType": "Standard_LRS",
"id": "[parameters('virtualMachines_Web1_id')]"
},
"diskSizeGB": 127
},
"dataDisks": []
},
"osProfile": {
"computerName": "[parameters('virtualMachines_Web1_name')]",
"adminUsername": "mcadmin",
"windowsConfiguration": {
"provisionVMAgent": true,
"enableAutomaticUpdates": true
},
"secrets": []
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaces_web1863_name'))]"
}
]
},
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": true,
"storageUri": "https://.blob.core.windows.net/"
}
}
},
"dependsOn": [
"[resourceId('Microsoft.Compute/availabilitySets', parameters('availabilitySets_ExampleAS_name'))]",
"[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaces_web1863_name'))]"
]
}

As I was pointing out, if you look at the bolded VM size in the VM section above, this template assumes that the deployer will only ever be allowed to deploy a Standard_D1_V2 machine (unless they manually edit the template, of course).  It also assumes the Image to be used for each VM will not be changed, as this information (offer, publisher, sku) is hard coded in the resources. This template has 2 VMs by the way, but I only included one as reference.  The VM Image details would be a perfect thing to put in the variables section, since we are deploying multiple machines with the same image, but we may not want the deployer to change the image at deploy time.  If we decide later that we want to use a Linux image, or a different Windows image, having this in the variables section would allow us to only have to change those values in 1 place, and they would apply to multiple VMs in the template.

Even though the VM names have been parameterized, the parameter naming holds on to the fact that I originally deployed these as Web Servers.  That is not necessarily what I want to deploy going forward, and this could be confusing to the deployer, if this template is to be used for more than just deploying web servers.

So, to simply state what has been said (aka. TLDR):

  1. parameters should be used for things that can/should be changed at runtime
  2. variables should be used for values that apply in multiple resources, and may change in the future – but not as often as each deployment
  3. We should not trust tool-generated JSON to give us the Idempotency that we need from our templates.

Hopefully this helps simplify things for you!

About the Author

Matt Canty

Matt is a CSA at Microsoft on the US National Team. As a former PFE, Matt has been working in the Azure space since 2014. Hobbies include: music (multiple instruments), Xbox gaming, auto enthusiast, soccer. Note: Matt's political opinions are solely the opinions of Matt, and are not necessarily a reflection of the political opinions of the company he works for.