Menu Home

Service Bus ARM Templates

I love Azure resource manager and especially ARM templates for deploying a full resource group. Unfortunately the documentation on how to author the templates still have a long way to come.

Recently I have been doing some IoT work, where we are using Service Bus and Event Hub. How to provision that with ARM templates is not well-documented (is it documented at all, I don’t know, haven’t found anything on it). Luckily, I’m part of the Azure advisory yammer network, where I asked if somebody knew how to deploy service bus resources with ARM Templates, and some nice Microsoft employee gave me a few hints, I think he told me the API is still beta, but it looks and works just as good as all the other ARM APIs. But now you are warned if it breaks some time down the road.

Enough chit chat, lets look at a template, for deploying an Azure Service Bus namespace, with a topic inside it, and a custom Shared Access Signature. I will try to post some more examples on other service bus resource types in the coming days. But lets start with something fairly simple.

Here’s the complete template, you can also get it from github

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "sbNamespace": {
      "type": "string",
      "metadata": {
        "description": "The service bus namespace"
      }
    }
  },
  "variables": {
    "location": "[resourceGroup().location]",
    "sbVersion": "2014-09-01",
    "topicName": "testTopic",
    "defaultSASKeyName": "RootManageSharedAccessKey",
    "authRuleResourceId": "[resourceId('Microsoft.ServiceBus/namespaces/authorizationRules', parameters('sbNamespace'), variables('defaultSASKeyName'))]",
    "sendAuthRuleResourceId": "[resourceId('Microsoft.ServiceBus/namespaces/authorizationRules', parameters('sbNamespace'), 'SendOnlyKey')]",
    "topicSubscriptionName": "testSubscription",
    "keyGeneratorTemplateUri": "https://raw.githubusercontent.com/sjkp/Azure.ARM.ServiceBus/master/Azure.ARM.ServiceBus/Templates/keygenerator.json"
  },
  "resources": [
    {
      "apiVersion": "2015-01-01",
      "name": "primaryKey",
      "type": "Microsoft.Resources/deployments",
      "properties": {
        "mode": "incremental",
        "templateLink": {
          "uri": "[variables('keyGeneratorTemplateUri')]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "seed": { "value": "1234a5" }
        }
      }
    },
    {
      "apiVersion": "2015-01-01",
      "name": "secondaryKey",
      "type": "Microsoft.Resources/deployments",
      "properties": {
        "mode": "incremental",
        "templateLink": {
          "uri": "[variables('keyGeneratorTemplateUri')]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "seed": { "value": "ac34a5" }
        }
      }
    },
    {
      "apiVersion": "[variables('sbVersion')]",
      "name": "[parameters('sbNamespace')]",
      "type": "Microsoft.ServiceBus/namespaces",
      "location": "[variables('location')]",
      "properties": {
        "messagingSku": 2
      },
      "resources": [
        {
          "apiVersion": "[variables('sbVersion')]",
          "name": "[variables('topicName')]",
          "type": "Topics",
          "dependsOn": [
            "[concat('Microsoft.ServiceBus/namespaces/', parameters('sbNamespace'))]"
          ],
          "properties": {
            "path": "[variables('topicName')]"
          },
          "resources": [
            {
              "apiVersion": "[variables('sbVersion')]",
              "name": "[variables('topicSubscriptionName')]",
              "type": "Subscriptions",
              "dependsOn": [
                "[variables('topicName')]"
              ],
              "properties": {
              }
            }
          ]
        },
        {
          "apiVersion": "[variables('sbVersion')]",
          "name": "[concat(parameters('sbNamespace'),'/SendOnlyKey')]",
          "type": "Microsoft.ServiceBus/namespaces/authorizationRules",
          "dependsOn": [
            "[concat('Microsoft.ServiceBus/namespaces/', parameters('sbNamespace'))]",
            "[concat('Microsoft.Resources/deployments/', 'primaryKey')]",
            "[concat('Microsoft.Resources/deployments/', 'secondaryKey')]"
          ],
          "location": "[variables('location')]",
          "properties": {
            "KeyName": "SendOnlyKey",
            "ClaimType": "SendSharedAccessKey",
            "ClaimValue": "None",
            "PrimaryKey": "[reference('primaryKey').outputs.key.value]",
            "SecondaryKey": "[reference('secondaryKey').outputs.key.value]",
            "Rights": [ "Send" ],
            "Revision": -1
          }
        }
      ]
    }
  ],
  "outputs": {
    "NamespaceDefaultConnectionString": {
      "type": "string",
      "value": "[listkeys(variables('authRuleResourceId'), variables('sbVersion')).primaryConnectionString]"
    },
    "SendOnlyConnectionString": {
      "type": "string",
      "value": "[listkeys(variables('sendAuthRuleResourceId'), variables('sbVersion')).primaryConnectionString]"
    }
  }
}

If you are familiar with ARM templates, it shouldn’t be too hard to follow what is going on. But I will give a few hints.

I only take one parameter the namespace name. So the topic name and subscription name, you will find in the variable section.

The variable section also contains a few other interesting things.

This was how I originally did it, now MS added a new version and of course it breaks things, so I hardcoded the API verison to 2014-09-01
I get the API version for the Microsoft.ServiceBus resource type by using
"sbVersion": "[providers('Microsoft.ServiceBus', 'namespaces').apiVersions[0]]",, it is fairly common to get the API version that way. It is potentially also dangerous, since your template might break if they create a new API version, with schema changes. For right now there is only one version that supports Microsoft.ServiceBus, so it should be fine.

Creating a service bus namespace can are done with the following

{
      "apiVersion": "[variables('sbVersion')]",
      "name": "[parameters('sbNamespace')]",
      "type": "Microsoft.ServiceBus/namespaces",
      "location": "[variables('location')]",
      "properties": {
        "messagingSku": 2
      },
      "resources": []
}

The messagingSku here defines if you want a Basic (1), Standard (2) or Premium (3) namespace, since we are going to use topics we must at least be on Standard.

The resources array are where we can create our Topics, Queues and Shared Access Signature policies.

To create a topic with a single subscription inside you can use the following

 {
          "apiVersion": "[variables('sbVersion')]",
          "name": "[variables('topicName')]",
          "type": "Topics",
          "dependsOn": [
            "[concat('Microsoft.ServiceBus/namespaces/', parameters('sbNamespace'))]"
          ],
          "properties": {
            "path": "[variables('topicName')]"
          },
          "resources": [
            {
              "apiVersion": "[variables('sbVersion')]",
              "name": "[variables('topicSubscriptionName')]",
              "type": "Subscriptions",
              "dependsOn": [
                "[variables('topicName')]"
              ],
              "properties": {
              }
            }
          ]
        }

Finally to create a Shared Access Signature policy, we add another resource to the service bus namespace resource array.

{
          "apiVersion": "[variables('sbVersion')]",
          "name": "[concat(parameters('sbNamespace'),'/SendOnlyKey')]",
          "type": "Microsoft.ServiceBus/namespaces/authorizationRules",
          "dependsOn": [
            "[concat('Microsoft.ServiceBus/namespaces/', parameters('sbNamespace'))]",
            "[concat('Microsoft.Resources/deployments/', 'primaryKey')]",
            "[concat('Microsoft.Resources/deployments/', 'secondaryKey')]"
          ],
          "location": "[variables('location')]",
          "properties": {
            "KeyName": "SendOnlyKey",
            "ClaimType": "SendSharedAccessKey",
            "ClaimValue": "None",
            "PrimaryKey": "[reference('primaryKey').outputs.key.value]",
            "SecondaryKey": "[reference('secondaryKey').outputs.key.value]",
            "Rights": [ "Send" ],
            "Revision": -1
          }
        }

This is where things get a little interesting. A Shared Access Signature consist of a primary and a secondary key, which should be 32 random bytes base64 encoded, as well as a permission level, the Rights array. Since ARM templates have no method of generating random byte arrays, I have created a small ARM template that use the uniqueString function to make two somewhat random keys we can use. I call the sub template as you can call any other ARM template that is publicly available over http/https from within a template with.

{
      "apiVersion": "2015-01-01",
      "name": "primaryKey",
      "type": "Microsoft.Resources/deployments",
      "properties": {
        "mode": "incremental",
        "templateLink": {
          "uri": "[variables('keyGeneratorTemplateUri')]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "seed": { "value": "1234a5" }
        }
      }
    }

Because uniqueString is just a hash value and not random data, my key generator template must take a seed to ensure that the two keys are unique, you can use any string with a length of six. The key generator template returns the generated key, as an output parameter, which we can reference with [reference('primaryKey').outputs.key.value].

I like the little sub template for generating keys, but you can of course also just provide the keys as parameters, but this way you have a self contained template, that can generate a service bus namespace with a single topic and a Shared Access Signature policy that grants send permissions only.

When the deployment are done, the output section will ensure that you get the connection string for the RootManageSharedAccessKey and the SendOnly.
servicebus-arm

Categories: Windows Azure

Tagged as:

Simon J.K. Pedersen

15 replies

  1. Hi Simon

    This is fantastic, many thanks!

    I have modified it to create a Service Bus and three queues which works fine but do you have any idea how to add a Shared Access Policy at the queue level instead of at the Service Bus level itself? I need to create separate send and listen policies for each of the three queues.

    Thanks again, Robert

  2. Hi Simon,
    Thanks for sharing this article. I’m trying to do something similar with service bus queue. I also tried running you sample code and it creates the topic alright, but the shared access policy is not being created (no errors whatsoever). I also tried capturing requests with Fiddler so I get an idea what and where to include in my template 🙂 . There is not a lot of information at the moment and your sample is the only one I could find. Do you have any thoughts on creating shared access policies for service bus queues via arm templates? Thanks in advance.

  3. Actually the key is created using your example (my mistake). Is there a way to create it for the topic / queue itself rather than for the entire namespace?

  4. The Shared Access policy is created on the namespace. Not for the queue/topic. I have yet to figure out how to do it on that level.

  5. Hi Simon, very helpful article, thanks.

    One question though, do you know if it is possible to set up a Rule/Action on a Topic Subscription using ARM?

  6. Simon,

    Very helpful info. I am wondering how we can use the template approach to also set the max size for the topic (as it is default to 1GB) and also DEFAULT MESSAGE TIME TO LIVE field, as this template would make it 10675199.1167301 days. Do you know if this even possible?

    Thanks

  7. The object for a queue looks like this on one of my queues

    {
    "id": "/subscriptions/688bf064-900b-4e8f-9598-2d9be0718
    s/namespaces/tiimosbybegptwr4ah6s/queues/submittokens",
    "name": "submittokens",
    "type": "Microsoft.ServiceBus/Queues",
    "location": "West Europe",
    "tags": null,
    "properties": {
    "lockDuration": "00:01:00",
    "requiresDuplicateDetection": true,
    "requiresSession": false,
    "defaultMessageTimeToLive": "365.00:00:00",
    "deadLetteringOnMessageExpiration": false,
    "duplicateDetectionHistoryTimeWindow": "1.00:00:00",
    "maxDeliveryCount": 10,
    "enableBatchedOperations": true,
    "sizeInBytes": 66072,
    "messageCount": 224,
    "isAnonymousAccessible": false,
    "status": "Active",
    "createdAt": "2016-02-02T09:31:37.7508307Z",
    "updatedAt": "2016-03-15T14:53:11.5018253Z",
    "accessedAt": "2016-03-30T15:19:10.254902Z",
    "supportOrdering": true,
    "countDetails": {
    "activeMessageCount": 0,
    "deadLetterMessageCount": 224,
    "scheduledMessageCount": 0,
    "transferMessageCount": 0,
    "transferDeadLetterMessageCount": 0
    },
    "autoDeleteOnIdle": "365.00:00:00",
    "enablePartitioning": false,
    "entityAvailabilityStatus": "Available",
    "enableExpress": false
    }
    }

    For a topic it looks like this

    {
    "id": "/subscriptions/688bf064-900b-4e8f-9598-2d9be0718133/res
    s/namespaces/tiimosbybegptwr4ah6s/topics/test",
    "name": "test",
    "type": "Microsoft.ServiceBus/Topic",
    "location": "West Europe",
    "tags": null,
    "properties": {
    "defaultMessageTimeToLive": "14.00:00:00",
    "maxSizeInMegabytes": 16384,
    "requiresDuplicateDetection": false,
    "duplicateDetectionHistoryTimeWindow": "00:10:00",
    "enableBatchedOperations": true,
    "sizeInBytes": 0,
    "filteringMessagesBeforePublishing": false,
    "isAnonymousAccessible": false,
    "status": "Active",
    "createdAt": "2016-03-30T15:24:15.867Z",
    "updatedAt": "2016-03-30T15:24:17.1528758Z",
    "accessedAt": "0001-01-01T00:00:00Z",
    "supportOrdering": false,
    "countDetails": {
    "activeMessageCount": 0,
    "deadLetterMessageCount": 0,
    "scheduledMessageCount": 0,
    "transferMessageCount": 0,
    "transferDeadLetterMessageCount": 0
    },
    "subscriptionCount": 0,
    "autoDeleteOnIdle": "10675199.02:48:05.4775807",
    "enablePartitioning": true,
    "isExpress": false,
    "entityAvailabilityStatus": "Available",
    "enableSubscriptionPartitioning": false,
    "enableExpress": false
    }
    }

    I assume that you can use most of the properties from the template, but haven’t tested so myself

  8. For anybody else coming to this, these work for a topic and subscription

    Topic properties:

    “requiresDuplicateDetection”: true,
    “duplicateDetectionHistoryTimeWindow”: “00:10:00”,
    “defaultMessageTimeToLive”: “14.00:00:00”,
    “filteringMessagesBeforePublishing”: false,
    “enablePartitioning”: true

    The enablePartitioning seems to force the max size to be 16GB

    For a subscription, these also work as properties:

    “deadLetteringOnMessageExpiration”: true,
    “lockDuration”: “00:02:00”,
    “defaultMessageTimeToLive”: “14.00:00:00”,
    “maxDeliveryCount”: 20

  9. Excellent stuff, Simon!

    An ARM novice question: If you already have the ServiceBus and want to create a new topic or subscription, how would the JSON template look?

Leave a Reply

Your email address will not be published. Required fields are marked *