Conditions in ARM Templates (the Right Way)
Hot off Microsoft Build, Azure Resource Manager templates just got an overhaul, complete with new support for conditional logic.
Join the DZone community and get the full member experience.
Join For FreeAt this month's Build conference, there were lot’s of new Azure announcements and, in particular, lots of new features for Azure Resource Manager (ARM) templates. Ryan Jones, PM on ARM templates, did a breakout session talking about all the new functionality, which is available now on Channel 9. I want to focus on one of the big improvements, at least from my perspective. We now have proper conditional logic in ARM templates! I blogged a few weeks ago about a workaround that would let you have this sort of logic using nested templates, but this was very much a temporary solution. With this new functionality, conditions are now fully implemented into the language!
Condition
The ARM template language now includes the “condition” key, which can be applied to a resource to determine whether or not that resource is executed. Let's take the example from the previous post again, where we wish to deploy a network card and we want to determine whether this NIC has a public IP or not via the means of a parameter. In this template, we supply a parameter called “NetworkInterfaceType” that can be “Public” or “Private”. In this template, we always deploy a vNet, and then the next step is to create a public IP resource. We only want this Public IP to exist if the “NetworkInterfaceType” is “Public”, so we add a condition to the Public IP resource:
{
"apiVersion": "2017-04-01",
"condition": "[equals(parameters('NetworkInterfaceType'),'Public')]",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[Concat(variables('NICName'),'-pip')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "[Concat(variables('NICName'),'-pip')]"
},
"properties": {
"publicIPAllocationMethod": "[parameters('IPAllocationMethod')]",
"dnsSettings": {
"domainNameLabel": "[Concat(variables('NICName'),'-pip')]"
}
}
},
Very simple, we use the “condition” key, and supply a value that can be evaluated to true or false. In this case whether the parameter “NetworkInterface” equals the word “Public”. If it does we create this resource, if it does not we don’t. One thing to note is that if your condition evaluates to false then the system simply skips, or no-ops the section, it is still in the template, it still gets compiled (or whatever ARM templates do behind the scenes). This is important if you are doing something like new or existing, where subsequent resources will have a dependency on your conditional resource, they do still exist in the template so this won’t error.
Ok, so we now are only creating the Public IP when the parameter “NetworkInterfaceType” is set to “Public”, but we also need to control whether or not we assign the public IP to a NIC based on the parameter. This is where it is a little bit more complicated (although not much).
As I mentioned, you apply the condition at the resource level — you can’t apply it to sections inside the resource. So what we can’t do is have a single network card resource and choose which child sections are run. What we instead have to do is create two network card resources and set the condition to control which one actually gets created. One thing to note is that you can’t have the “name” property be the same for both NICs. Even though you will only create one or the other, the template still needs to compile with both in, and it will throw an error if they are named the same. To get around this, I have used the conditional parameter value in the name. This means later when you come to assign the NIC to a VM you can simply do something like [concat(variables(‘NICName’),’-‘,parameters(‘NetworkInterfaceType’))]
{
"apiVersion": "2017-04-01",
"condition": "[equals(parameters('NetworkInterfaceType'),'Public')]",
"type": "Microsoft.Network/networkInterfaces",
"name": "[concat(variables('NICName'),'-public')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "parameters('NICName')"
},
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('NICName'),'-pip')]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "[parameters('IPAllocationMethod')]",
"subnet": {
"id": "[concat(resourceId('Microsoft.Network/virtualNetworks', variables('NetworkName')), '/subnets/',variables('Subnet1Name'))]"
},
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',Concat(variables('NICName'),'-pip'))]"
}
}
}
]
}
},
{
"apiVersion": "2017-04-01",
"condition": "[equals(parameters('NetworkInterfaceType'),'Private')]",
"type": "Microsoft.Network/networkInterfaces",
"name": "[concat(variables('NICName'),'-private')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "parameters('NICName')"
},
"dependsOn": [],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "[parameters('IPAllocationMethod')]",
"subnet": {
"id": "[concat(resourceId('Microsoft.Network/virtualNetworks', variables('NetworkName')), '/subnets/',variables('Subnet1Name'))]"
}
}
}
]
}
}
Again, if NetworkInterfaceType is set to “Public”, we create the first NIC which has a public IP, and if it is set to “Private” we create the second.
Functions
To go along with conditions, we also have a load of new functions that help with creating our conditional statements:
These are fairly self-explanatory and can be used either on their own or in combination with other functions. For example:
"[greater(length(parameters('NetworkInterfaceType')),6)]"
That would be true if the length of the string “NetworkInterfaceType” is greater than 6 characters.
Ryan also announced a number of set-based functions that could also be useful for conditions:
- contains()
- empty()
- intersection()
- union()
- first()
- last()
- indexOf()
- lastIndexOf
- startsWith()
- endsWith()
- max()
- min()
- range()
Summary
This is a significant improvement in the ARM language and adds a lot more flexibility to templates. It removes the need for nested templates purely for conditions (although they are still a good idea for modularity) and provides many additional types of conditions than can be achieved with this method.
I would encourage you to view the presentation from Build with all the new changes to the ARM language, which also include serial copies, cross resource group deployments, and conversions, as well as new managed applications. I will be covering many of these over the next few weeks.
Published at DZone with permission of Sam Cogan, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments