ARM Templates and user-defined functions

Azure Resource Manager (ARM) templates are starting to become the privileged method for the deployment of resources in the Microsoft Azure Cloud. This results in a stronger community and therefor it’s continuously being improved by our friends from Redmond.

Before going into the technical details I’ll try to explain what brought me to blog about the topic of governance and naming Azure resources.

About a month ago I started working in a Microsoft Azure project team for one of the biggest banks in the Netherlands. The team exists of a couple of folks from Microsoft and some contractors like myself.

One of the coding principles that is being followed is “code as documentation” which means that all code delivered should be clean and self explainable as much as possible.

Because we’re deploying resources in a large environment a good governance plan is a must, which also accounts for the naming standards used for the resources. This can be challenging especially when deploying multi instances of a resource like virtual machines.

Deploying multiple VM’s

When deploying multi VM’s from an ARM template the complexity begins. Because all resources of the same type within a resource group need to have a unique name, a concatenation of various properties has to be used for the name.



In this example the value of the parameter virtualMachineName will be concatenated with the number of the copy index resulting in a name like web-server1, web-server2, etc.

When you’re deploying only a couple of VM’s this won’t be an issue although it doesn’t look very nice. An alternative to this can be to add an extra ‘0’ in front of the index value. This can either be done by adding the ‘0’ in the virtual machine parameter which is a bit clumsy, or add it to the concat. But this will only work when rolling out less that 10 machines.



As a result the name will look something like web-server01, ….web-server010

An alternative is to create a more complex concat with some if statements that will validate the length of the copyindex value to add an extra ‘0’ To do this we also have to add some more magic by using if statements and integer options like ‘less’ and ‘if’



What the hack are you doing here, you might ask to yourself. Well let me break that down.

  • First of all we take the name from the parameter virtualMachineName.
  • than the If statement checks if the value of the copyindex is less than 10.
    if(less(copyindex(1), 10)
  • When true an extra ‘0’ is added to the concat
    concat(‘0’, copyindex(1)))
    When false no extra character is added
Although this looks like a good solution it only solves a part of the problem because if we are deploying more than 100 machines we run into the same problem again. This is not something that a lot of folks will run into, but still we can better solve it now.
We can solve this either by adding another if statement in the previous concat that will validate if the copyindex value is greater than 100,  but as discussed before we live by the standard of “code as documentation” and this will only add more complexity and is bad for the readability of the code.
Another trick that we can pull from the bag is to use the string function padLeft in our ARM template. The will help us to get rid of all the complex if statements that we are using now.

p.s. All supported template expressions and functions can be found (here)

The padLeft can be used to add extra characters to a sting value up to a specified length.

padLeft(valueToPad, totalLength, paddingCharacter)

If we have a string value like “1234” we can use padLeft to add characters to the left side of the string resulting in “xxxx1234”

The following example shows how we can use the padLeft to overcome our challange for naming our virtual machines.



The above sample will result in an output value of web-server001 Pretty nice ay!

Now that we have solved the long expressions, we are still looking at another challenge although it is a minor one. Because a virtual machine exists out of multiple components like an os-disk, data disk(s), network interface(s), and the VM itself we need to do the concatenation for every resource that is related to the same VM. Although this is not a technical requirement, it helps you to keep an overview of all the resources that belong together.

As I mention before I don’t like to do the same thing more than once, so how can we solve this?

User-defined Functions

Since the start of may 2018 it is possible to to create your own functions within the ARM templates. This is great because you can now move the complicated expressions, that you don’t want to repeat for every resource to a separate section called functions. You can use all expressions and functions that are supported in template to create your own. user-defined functions

Because these user-defined functions are pretty new, there’s currently not a lot of documentation available. The current schema doesn’t recognize the parameters defined in the functions although they happily work as expected. This will probably be fixed soon by the good folks at Microsoft.

To add the functions to your template a separate section has to be declared as shown in the following example:



Now that we have functions section available we can start building our user-defined function for naming our resources.

Microsoft did a very good job at explaining how to create these functions, so I won’t do that again and just stick to the implementation of it.
First we have to create the function that is going to handle all the magic. As a namespace I will use myFunctions with a member generateVMName
Now that we have created the function we can use it to generate the name of our resource, in this case the VM.

What happens here is that when calling the function myFunctions.vmName two parameters needs to be passed to the user-defined function. In this case we are using “virtualMachineName” and “copyindex(1)” as an input value for our function. these two  input parameters will be used in the concat at the output section of our user-defined function to generate a new name.

The output of the above example will be something like: “web-server001”

In this example we have kept it pretty easy, but you can build awesome expressions to generate a new function that fits your needs.

A full version of the json template for a virtual machine can be found on my (GitHub)

The result of this deployment is shown below.



Some improvements in my opinion would be a to create nested functions. Right now if we want to create a separate function for the naming of our network interfaces we still have to redo the same concatenation as before, which results in doing the same multiple times again, something I’m trying to prevent.

What you can do is create a concat with a function in the name property of a resource, but still this is not ideal to my opinion.

Another great feature would be an option to call functions from other nested templates. Especially when creating very complex expressions. This could help to create one template with only functions that could be used by other templates.

Again you don’t want to copy/paste the same code over-and-over again into new templates that your creating.


User-defined functions are a great way to handle complex scenario’s in your ARM templates although some functionality is still missing. I do recommend using this new capability to make your templates more readable and keep all the complexity in a separate section of your template, instead of in every name property of the resource that you’re deploying.

Have fun coding!



One thought on “ARM Templates and user-defined functions

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.