Search code examples
azureazureservicebusazure-resource-managerservicebusazure-servicebus-topics

Azure ARM template for Service bus with Topics with many Subscriptions


Hi I have an ARM template for create a ServiceBus with its topics and subscriptions. But I can only accomplish 1 topic - 1 subscription because I cannot make a nested loop to create many subscriptions per topic.

I wish I could execute a template like this:

params:

{
   "serviceBusName": "mybus",
   "topics": 
    [ 
      { 
         "topicName": "mytopic1",
         "subscriptions": [ "mysubscription1", "mysubscription2"]
      },
      { 
         "topicName": "mytopic2",
         "subscriptions": [ "mysubscription1"]
      }  
    ]
}

This is my actual template:

{
  "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "ServiceBusNamespaceName": {
      "type": "string"
    },
    "ServiceBusSku": {
      "type": "string",
      "allowedValues": [
        "Basic",
        "Standard"
      ],
      "defaultValue": "Standard"
    },
    "ServiceBusSmallSizeTopicInMb": {
      "type": "int",
      "defaultValue": 1024
    },
    "ServiceBusMaxSizeTopicInMb": {
      "type": "int",
      "defaultValue": 1024
    },
    "Topics": {
      "type": "array"
    }
  },
  "variables": {
    "DefaultSASKeyName": "RootManageSharedAccessKey",
    "DefaultAuthRuleResourceId": "[resourceId('Microsoft.ServiceBus/namespaces/authorizationRules', parameters('ServiceBusNamespaceName'), variables('DefaultSASKeyName'))]",
    "SbVersion": "2017-04-01"
  },
  "resources": [
    {
      "apiVersion": "2017-04-01",
      "name": "[parameters('ServiceBusNamespaceName')]",
      "type": "Microsoft.ServiceBus/namespaces",
      "location": "[resourceGroup().location]",
      "sku": {
        "name": "[parameters('ServiceBusSku')]"
      },
      "tags": {
        "displayName": "ServiceBus"
      }
    },
    {
      "copy": {
        "name": "topics",
        "count": "[length(parameters('Topics'))]"
      },
      "type": "Microsoft.ServiceBus/namespaces/topics",
      "name": "[concat(parameters('ServiceBusNamespaceName'), '/', parameters('Topics')[copyIndex()].topic)]",
      "apiVersion": "2017-04-01",
      "location": "[resourceGroup().location]",
      "scale": null,
      "properties": {
        "defaultMessageTimeToLive": "P1D",
        "maxSizeInMegabytes": "[parameters('ServiceBusMaxSizeTopicInMb')]",
        "requiresDuplicateDetection": false,
        "duplicateDetectionHistoryTimeWindow": "PT10M",
        "enableBatchedOperations": true,
        "status": "Active",
        "supportOrdering": true,
        "autoDeleteOnIdle": "P10675199DT2H48M5.4775807S",
        "enablePartitioning": false,
        "enableExpress": false
      },
      "dependsOn": [
        "[resourceId('Microsoft.ServiceBus/namespaces', parameters('ServiceBusNamespaceName'))]"
      ],
      "resources": [
        {
          "apiVersion": "[variables('sbVersion')]",
          "name": "[parameters('Topics')[copyIndex()].subscription]",
          "type": "Subscriptions",
          "dependsOn": [
            "[parameters('Topics')[copyIndex()].topic]"
          ],
          "properties": {
            "lockDuration": "PT1M",
            "requiresSession": "false",
            "defaultMessageTimeToLive": "P7D",
            "deadLetteringOnMessageExpiration": "false",
            "maxDeliveryCount": "2",
            "enableBatchedOperations": "true",
            "autoDeleteOnIdle": "P7D"
          }
        }
      ]
    }
  ],
  "outputs": {
    "NamespaceDefaultConnectionString": {
      "type": "string",
      "value": "[listkeys(variables('DefaultAuthRuleResourceId'), variables('SbVersion')).primaryConnectionString]"
    },
    "DefaultSharedAccessPolicyPrimaryKey": {
      "type": "string",
      "value": "[listkeys(variables('DefaultAuthRuleResourceId'), variables('SbVersion')).primaryKey]"
    }
  }
}

And an example of the params json for the actual template:

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "ServiceBusNamespaceName": {
            "value": "mybus"
        },
        "ServiceBusSku": {
            "value": "Standard"
        },
        "Topics ": {
            "value": [
                {
                    "topic": "mytopic1",
                    "subscription": "mysubscription1"
                },
                {
                    "topic": "mytopic2",
                    "subscription": "mysubscription1"
                }
            ]
        }
    }
}

Solution

  • In general, there are two ways to do something like this. You can restructure your code so that the subscriptions are top level resources. Or you use the named variant of copyIndex to achieve nested loops. Both variants can be seen in this blog post and the comment for it.

    Azure ARM Templates – Are nested loops possible?

    However, for your case, the only option is to make the subscriptions a top level resource. See aka.ms/arm-copy/#looping-on-a-nested-resource for more details.

    This is the full example taken from the blog post mentioned above.

    {
        "$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"
                ]
            }
        ]
    }
    
    {
        "$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"
              }
            ]
          }
        }
      }
    

    This example for VMs uses named copyIndex - it works when the nested loop is not for a resource itself.

    {
      "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')]"
                }
              }
            }
          ]
        }
      }
    }