Azure, Azure Resource Manager, Azure Stack
comments 7

Building Azure Resource Manager Templates – Using Copy Object

If you are following this series, by now you know how to use parameters and variables in ARM template language. We used that knowledge to create a template that creates storage account and virtual network. Let us revisit the scenario.


In this part, we will extend the template to add publicIP, load balancer, and RDP endpoints for the virtual machines. Before we do that, let us review what we need. To achieve what we want in this, we will use copy object in the template.

What is Copy Object?

In our template, we already have the storage account and virtual network configuration defined. What we now need is a load balancer with a public IP so that we can access this deployment from the Internet. Also, we need the inbound NAT rules for enabling RDP access to the VMs in this deployment. But before we create the RDP rules for each VM, we need to know how many VM instances we are deploying. As shown in the scenario diagram, we need to be able to deploy this template within development, test, or production environment. Depending on the selected environment, we will have one AD DC VM along with 1 (development) or 2 (test) or 3 (production) VMs that join the AD domain. Therefore, we need a way to capture the environment type as an input parameter. Also, based on the environment type selected by the user, we need to decide on the number of VM instances required for the deployment and then create only relevant number of inbound NAT rules within the load balancer.

There are multiple ways to achieve this.

  1. Add multiple resource definitions; one for each resource instance we need. This does not give the flexibility to dynamically add or remove instances based on the VM count we need. This method is not efficient.
  2. Use some kind of iterator, like other programming languages, within a resource definition and tell ARM how many resource instances we need. This can be achieved using copy object technique in ARM templates.

Before we go to the copy object discussion, let us first add more parameters and variables that are needed for this template. These additional parameters will also help us with defining the iteration count within the copy object.

Within the new parameters, we have the dnsName parameter that will be for external access. The environmentType parameter is used to capture whether the user is deploying this template for dev, test, or production. However, based on this selection, we need a way to find out the number of VMs we need in the domain. That is, we need know the instance count for each environment. Remember our discussion on free-form vs known configurations? We don’t want to leave such choices to the end user. So, the place where this decision can be made will be in the variables element.

Within the variables element, we defined new variables for making it easier to select the environment type. The deploymentSize variable defines a JSON object. Within this, we are associating instancesCount to every deployment type.

We use the selectedDeployment variable to retrieve the value of deploymentSize variable. This is done by associating the value of environmentType parameter and looking up for that in the deploymentSize variable.

This works because deploymentSize is a JSON object similar to a dictionary. We can index into it using one of the key names. So, in this case, the key names will match the value passed to the environmentType parameter and indexing into that will give us the right object within deploymentSize. Once we have the selectedDeployment variable populated, we can access the instancesCount value by dot-referencing the property name. For example,

This should not be alien to us. We use variables() function and pass the selectedDeployment variable name to it. The resulting object has the instancesCount property which can then be retrieved using dot-referencing. Now that we figured this out, let us go back to the discussion around multiple instance creation within a template.

Working with Copy Object

As stated earlier, the reason we need instancesCount value is because we need to tell ARM how many times it has to iterate for creating the resource for which we need multiple instances. This is done within the copy object syntax.

The name property within the copy element defines a name for the iterator which can later be used for defining dependencies. The count property defines the number of times the iterator has to run. In our case, we will set the value of count property to the instancesCount value we retrieve from the selectedDeployment.

We place this inside the resource definition. In our scenario, we will use the copy object within Microsoft.Network/loadBalancers/inboundNatRules resource type. Let us first see the new resources that we need to enable public IP and load balancer configuration. We will then review the resource definition to understand how we use the copy object.

In this updated resource collection, we have added three resource types. A public IP address to assign it to the load balancer, a load balancer that will act as a container for the VM endpoints, and finally, RDP endpoints based on the number of VM instances we need.

Observe the highlighted lines in the resource collection. The copy object that we used within the inboundNATRules resource type tells ARM how many instances we need to create based on the VM count we need.

Within an ARM template, for each resource type and its instances, the name property should carry a unique value. Without this, you will see an error during deployment that multiple instances cannot use the same name. So, if you look at line number 77, we are dynamically constructing the value for name.

As you see here, we are concatenating ‘/loadbalancer/’ with ‘VM-‘ and then using the copyIndex() function and finally add ‘-RDP’. The copyIndex() function gives us the iteration number. So, if we are creating three VM endpoints within this resource definition, we get iteration values 0, 1, and 2. The index always starts from 0. However, if we want to offset it to different value than zero, we can specify the value inside the copyIndex() function. In my example, I am using 1 so that the values the template receives will be 1, 2, and 3. Since we are using the iteration number within the name value, we always get an unique identifier.

Another function that we are using within this template update is the add() function. This function, as its name suggests, adds integers. I am using this function to dynamically derive the frontend port number for the VM RDP endpoint.

We are doing this since all VM RDP endpoints should be accessible through the load balancer. By using add function, we are adding the iteration value to the RDP port (3389) to derive a unique value.

Now that we have completed our discussion on the copy object and its usage, let us move on to see the ARM template update that creates storage account, virtual network, public IP, load balancer, and finally the VM RDP endpoints. We are not creating an RDP endpoint for the DC VM.

You can go ahead and deploy this update by clicking on the button below. 

arm-environmenttypeThis brings up the portal and prompts for the parameter values we need for the deployment. Within the parameters, you will see the environmentType dropdown with three possible values. I have selected Production as the input here and I expect to see three RDP endpoints created at the end of template deployment. Let us see if this worked.


As you see here, the endpoints with respective frontend port numbers are created. We will have to associate these endpoints to VM network interfaces and we will take that up in a later part. Stay tuned.

Filed under: Azure, Azure Resource Manager, Azure Stack


Ravikanth is a principal engineer and the lead architect for Microsoft and VMware virtualized and hybrid cloud solutions within the Infrastructure Solutions Group at Dell EMC. He is a multi-year recipient of Microsoft Most Valuable Professional (MVP) award in Windows PowerShell (CDM) and Microsoft Azure. Ravikanth is the author of Windows PowerShell Desired State Configuration Revealed (Apress) and leads Bangalore PowerShell and Bangalore IT Pro user groups. He can be seen speaking regularly at local user group events and conferences in India and abroad.

  • Brian Tower

    Hello, Ravi. Great series, but the content for Part 6 seems to be missing. There is nothing below the “This is part of a series…” banner.

  • Thanks Brian! Interestingly, if I remove that post from the series, I can see the contents. For now, I untagged the series on that part.

  • Brian Tower

    Thanks for the quick reply and work around. I am finding your “course” to be one of the most informative on the net. Thank you very much for sharing your knowledge and experience.

  • You are welcome, Brian. Glad you are liking this series. BTW, I figured out what was wrong with the other post. It was the length of the post that was freaking out WordPress. I removed the JSON content towards the end and all is well now.

  • Rohit Kumar

    Hi Ravikanth,

    I am trying to deploy my Azure ARM environment of three VM’s in three different subnets of same VNet using json file but couldn’t able to find any such Json file.

    Can you please help me out here.


  • Hi Ravikanth,

    Do you know if you can loop through load balancing rules? I need a load balancer with 50 rules but can’t do it using your example with NATs. I keep getting the following error:

    “code”: “OperationNotSupported”,
    “message”: “PUT operation on resource

  • Sandeep Anumula

    Hey Rohit, Even am trying same now. Have you done with this?