Dynamic ARM Templates with Copy Variable


Brad Watts here to talk about how we can elevate our ARM Templates to the next level using the “copy” option within our variables. When you're starting your journey on building ARM Templates we make them more dynamic by adding Parameters to the templates. When we start building our library of templates to be used in our organization this works great for most resources but not all. For instance, creating a VM you can typically simply use Parameters and Variables. But what about an Application Gateway? You may have one Application with two backend pools and another with five backend pools. Simple Parameters and Variables aren't flexible enough to handle this. That is where we can introduce the “copy” command in our template. 

What is the Copy Command? 

The Copy command simply allows me to create a variable with multiple values in an array. More importantly it allows you to dynamically create this variable instead of having to know the length of your variable ahead of time. 

Define multiple instances of a variable – Azure Resource Manager | Microsoft Docs 

How to Use the Copy Command 

Before we jump into the gritty details I want to give you a high level view of how we can utilize the copy command to make our template more dynamic. In the scenario we will walk through we will have 3 key pieces. A Parameter, variable, and resource. 

  • Parameter: this will be an array and is how we pass in the data that is needed to build our JSON array in the Copy Command.
  • Variable: this is where we have the copy command and our variable will output a dynamic array based on the Parameter that is passed in
  • Resource: in the resource block of the arm template you will be using the Variable in place of a JSON array


This was really confusing to me when I first started looking at it so let's walk through a real example. An Application can have multiple Backend Address Pools and the format for this section is: 

“backendAddressPools”: [ 

“name”: “DisplayName”,
“properties”: { 
“backendAddresses”: [ 
“fqdn”:  “server1.fqdn.com

We don't know how many items will be in the backendAddressPool (JSON array) so our goal is to create a nested template that can be used to create any Application Gateway no matter how many backendAddressesPools it has.  


First we need a Parameter that allows you to pass in the backendAddresses. We can pass them in an Array, but we need two values (DisplayName and FQDN). So, each value in the array we'll use a delimitator. For instance, below I used “|” as the delimitator. 

**Note** For simplicity I kept the backendAddresses to a single fqdn 

Our nested template would have a Parameter named “backendPool”  

“backendAddresses”: { 
“type”: “array” 

If we look at this from the parent template, below is an example of pass in the values with 2 backend pools. Each element in the array is delaminated by the “|” with first item being the name and the second item being the fqdn: 

“backendAddresses”: { 
“value”: [ 


Now we need to dynamically create the JSON array using the Copy Command. Lets look at the JSON for this: 

“variables”: { 
“copy”: [ 
“name”: “backendAddressPools”,
“count”: “[length(parameters(‘backendAddresses'))]”,
“input”: { 
“name”: “[split(parameters(‘backendAddresses')[copyIndex(‘backendAddressPools')],'|')[0]]”,
“properties”: { 
“backendAddresses”: [ 
“fqdn”: “[split(parameters(‘backendAddresses')[copyIndex(‘backendAddressPools')],'|')[1]]” 

Now let's dissect this: 

  • name: This is the name of the variable that we can reference later in the template. 
  • count: This dynamically creates an array, so we need to know how many times to iterate though. In this example we are passing in an array with 2 values so that's how many times we need to iterate through. 
  • Input: This is a single element in the JSON array that will be created. You can see that this is formatted just like a single object in the backendAddressPool element in an Application Gateway. We have two dynamic properties in this section: 
  • name: We split the parameter by the delimiter and grab the first item. So in our example it'll be Pool1 the first time and Pool2 the second (“Pool1|″) 
  • fqdn: We split the parameter by the delimiter and grab the second item. So in our example it'll be the first time and the second (“Pool1|“) 

So, from our example the result for the backendAddressPools variable would be: 

“name”: “Pool1”, 
    “properties”: { 
    “backendAddresses”: [ 
        “fqdn”: “” 


“name”: “Pool2”, 
    “properties”: { 
    “backendAddresses”: [ 
        “fqdn”: “” 


We now have an array created using the copy command within our variables. We need to put this to use within the actual resource creation. The good news is this is the simple part. You simply use this variable in place of the JSON array in the resource. So, for our Application Gateway it would look like this: 

“name”: “[parameters(‘applicationGatewayName')]”, 
“type”: “Microsoft.Network/applicationGateways”, 
“apiVersion”: “2019-09-01”, 
“dependsOn”: [], 
“location”: “[resourceGroup().location]”, 
“zones”: “[parameters(‘zones')]”, 
“properties”: { 
“backendAddressPools”: “[variables(‘backendAddressPools')]”, 

Putting it All Together 

I always think it's good to see a complete example to hopefully tie everything together. For an example of a more dynamic way to create an Application Gateway take a look at this template: 

CSANestedTemplates/AppGWHTTPSListenerKV.json at main · microsoft/CSANestedTemplates (github.com) 

And then here is an example of a parent template that calls this as one of the nested templates: 

CSAAKSDeployments/2-Tier-AppGW-AKS.json at main · microsoft/CSAAKSDeployments (github.com) 


This first time I saw the Copy command used in a variable I had no idea what they were doing. However, after I understood use it, it opened a whole new way of creating dynamic ARM Templates. Hopefully, you see the power in this command in the example I gave with the Application Gateway. It's a great tool to have in your ARM Template toolbelt! 


This article was originally published by Microsoft's Entra (Azure AD) Blog. You can find the original article here.