Service Bus ARM Templates

13/12/2015

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

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
[js]
{
"apiVersion": "[variables(‘sbVersion’)]",
"name": "[parameters(‘sbNamespace’)]",
"type": "Microsoft.ServiceBus/namespaces",
"location": "[variables(‘location’)]",
"properties": {
"messagingSku": 2
},
"resources": []
}
[/js]

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
[js]
{
"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": {
}
}
]
}
[/js]

Finally to create a Shared Access Signature policy, we add another resource to the service bus namespace resource array.
[js]
{
"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
}
}
[/js]
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.
[js]
{
"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" }
}
}
}
[/js]
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