Have you ever wanted to create a nested loop in an ARM template? Currently, it’s not possible to create resources within a nested loop. You can use the copy element to create multiple instances of a resource by looping over an array. But you can’t loop over an inner array to create sub-resources.
For example, you can’t create Service Bus topics and subscriptions using an outer and inner loop. The workaround is to move the sub-resource to the top-level and then reference the parent resource by name.
Here’s a sample template that creates the service bus namespace, topics, and subscriptions:
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus namespace"
}
},
"topics":{
"type": "array",
"metadata": {
"description": "List of topics"
}
},
"subscriptions":{
"type": "array",
"metadata": {
"description": "List of subscriptions"
}
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.ServiceBus/namespaces",
"sku": {
"name": "Standard"
},
"name": "[parameters('serviceBusNamespaceName')]",
"apiVersion": "2017-04-01",
"location": "[resourceGroup().location]",
"properties": {}
},
{
"type": "Microsoft.ServiceBus/namespaces/topics",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', parameters('topics')[copyIndex()])]",
"apiVersion": "2017-04-01",
"location": "[resourceGroup().location]",
"copy": {
"name": "topicLoop",
"count": "[length(parameters('topics'))]"
},
"properties": {},
"dependsOn": [
"[concat('Microsoft.ServiceBus/namespaces/', parameters('serviceBusNamespaceName'))]"
]
},
{
"type": "Microsoft.ServiceBus/namespaces/topics/subscriptions",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', parameters('subscriptions')[copyIndex()].topic, '/', parameters('subscriptions')[copyIndex()].subscription)]",
"apiVersion": "2017-04-01",
"location": "[resourceGroup().location]",
"copy": {
"name": "subscriptionLoop",
"count": "[length(parameters('subscriptions'))]"
},
"properties": {},
"dependsOn": [
"topicLoop"
]
}
]
}
Note, the subscription is a top-level resource now. The topic loop name has been added to the dependsOn array.
Here’s the parameters.json file:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"value": "rjtestsbnmspace"
},
"topics": {
"value": ["topic1", "topic2"]
},
"subscriptions": {
"value": [{
"topic": "topic1",
"subscription": "subscription1"
},
{
"topic": "topic1",
"subscription": "subscription2"
},
{
"topic": "topic2",
"subscription": "subscription3"
}
]
}
}
}
I’ve created an array of strings to hold the topics and another array of objects parameter to hold the subscription along with its topic. As a result of the copy element, multiple topics will be created, one for each item in the topics array. Then, subscriptions for each topic will be created based on the subscriptions array.
Great article, however I wanted to let you know that it is now possible to nest copy loops in ARM templates. I’ve just done one which has an outer loop for the whole VM and an inner loop for multiple data disks – here’s an extract of the code:
“name”: “[concat(parameters(‘vmName’), padLeft(copyIndex(1), 2, ‘0’))]”,
“type”: “Microsoft.Compute/virtualMachines”,
“copy”: {
“name”: “vmLoop”,
“count”: “[parameters(‘vmCount’)]”
},
“properties”: {
“osProfile”: {
“computerName”: “[concat(parameters(‘vmName’), padLeft(copyIndex(1), 2, ‘0’))]”
},
“hardwareProfile”: {
“vmSize”: “[parameters(‘vmSize’)]”
},
“storageProfile”: {
“osDisk”: {
“name”: “[concat(parameters(‘vmName’), padLeft(copyIndex(1), 2, ‘0’),’_OSDisk’)]”,
“createOption”: “FromImage”,
“managedDisk”: {
“storageAccountType”: “[parameters(‘vmDiskType’)]”
}
},
“copy”: [
{
“name”: “dataDisks”,
“count”: “[parameters(‘dataDiskCount’)]”,
“input”: {
“caching”: “[parameters(‘dataDiskCaching’)]”,
“name”: “[concat(parameters(‘vmName’), padLeft(copyIndex(‘vmLoop’, 1), 2, ‘0’), ‘-dataDisk’, padLeft(copyIndex(‘dataDisks’), 2, ‘0’))]”,
“lun”: “[copyIndex(‘dataDisks’)]”,
“createOption”: “Empty”,
“diskSizeGB”: “[parameters(‘dataDiskSize’)]”,
“managedDisk”: {
“storageAccountType”: “[parameters(‘vmDiskType’)]”
}
}
}
]
}
}
},
LikeLike