If you are building an IoT solution and need reliable and secure communications between your IoT devices and your cloud-hosted solution backend then you are probably looking at the following Azure product: Azure IoT Hub.
Today we will focus on creating an Azure Resource Manager template to easily setup and deploy an IoT Hub to a resource group. Our ARM template will be created in a new Azure Resource Group deployment project in Visual Studio.
Creation
Let's declare the parameters of the ARM template:
{ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "iotHubName": { "type": "string", "defaultValue": "[concat('myIoTHub', uniqueString(resourceGroup().id))]" }, "iotHubSkuName": { "type": "string", "defaultValue": "F1", "allowedValues": [ "F1", "B1", "B2", "B3", "S1", "S2", "S3" ] }, "iotHubSkuCapacity": { "type": "int", "defaultValue": 1, "minValue": 1 }, "iotHubMessageRetentionInDays": { "type": "int", "defaultValue": 1, "allowedValues": [ 1, 2, 3, 4, 5, 6, 7 ] }, "iotHubPartitionCount": { "type": "int", "defaultValue": 2, "minValue": 2 } } ... }
Now we will declare the resources for the IoT Hub:
{ ... "resources": [ { "apiVersion": "2018-04-01", "name": "[parameters('iotHubName')]", "type": "Microsoft.Devices/IotHubs", "location": "[resourceGroup().location]", "sku": { "name": "[parameters('iotHubSkuName')]", "capacity": "[parameters('iotHubSkuCapacity')]" }, "properties": { "eventHubEndpoints": { "events": { "retentionTimeInDays": "[parameters('iotHubMessageRetentionInDays')]", "partitionCount": "[parameters('iotHubPartitionCount')]" }, "operationsMonitoringEvents": { "retentionTimeInDays": "[parameters('iotHubMessageRetentionInDays')]", "partitionCount": "[parameters('iotHubPartitionCount')]" } } } } ] ... }
We can pay attention to several things here:
And to finish, we will output the connection string and the Event Hub compatible connection string of the IoT hub:
{ ... "outputs": { "IoTHubConnectionString": { "type": "string", "value": "[concat('HostName=', reference(resourceId('Microsoft.Devices/IoTHubs', parameters('iotHubName')), providers('Microsoft.Devices', 'IoTHubs').apiVersions[0]).hostName, ';SharedAccessKeyName=iothubowner;SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/IotHubs', parameters('iotHubName')), providers('Microsoft.Devices', 'IoTHubs').apiVersions[0]).value[0].primaryKey)]" }, "IoTHubEventHubCompatibleConnectionString": { "type": "string", "value": "[concat('Endpoint=', reference(resourceId('Microsoft.Devices/IoTHubs', parameters('iotHubName')), providers('Microsoft.Devices', 'IoTHubs').apiVersions[0]).eventHubEndpoints.events.endpoint, ';SharedAccessKeyName=iothubowner;SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/IotHubs', parameters('iotHubName')), providers('Microsoft.Devices', 'IoTHubs').apiVersions[0]).value[0].primaryKey, ';EntityPath=', reference(resourceId('Microsoft.Devices/IoTHubs', parameters('iotHubName')), providers('Microsoft.Devices', 'IoTHubs').apiVersions[0]).eventHubEndpoints.events.path)]" } } }
As you can notice, we take advantage of the ARM template function listKeys and the ARM template function providers.
The function providers is useful to get the latest API version for a specific namespace, whereas listkeys is the function that will allow us to get the properties for a specific key name.
By default when a new IoT hub is created, five access policies are created: iothubowner, service, device, registryRead and registryReadWrite. In our template we retrieve the primary connection string for the first one.
Example of use
The ARM template is now ready, let's open a Windows PowerShell and try it:
.\Deploy-AzureResourceGroup.ps1 -ResourceGroupName 'MyResourceGroupName' -ResourceGroupLocation 'canadaeast' -TemplateFile '.\azuredeploy.json'
...
OutputsString :
Name Type Value
=============== ========================= ==========
ioTHubConnectionString String HostName=myIoTHubfkr54oehqy5pa.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=Vs38UdjTt5Wj1cV3Wji8e5bOx6nSsYJ/JA6ZrO6WOiA=
ioTHubEventHubCompatibleConnectionString String Endpoint=sb://ihsuprodblres036dednamespace.servicebus.windows.net/;SharedAccessKeyName=iothubowner;SharedAccessKey=Vs38UdjTt5Wj1cV3Wji8e5bOx6nSsYJ/JA6ZrO6WOiA=;EntityPath=iothub-ehub-myiothubfk-1163803-9a6e8b6b6c
If everything goes well, you should see the same kind of output as above.
To go further
In the template we are outputting the connection string as an example. But in a more advance scenario with Azure Functions you could directly set the Function App application setting that require the connection string like the following:
{ ... "resources": [ { "apiVersion": "2016-08-01", "name": "[parameters('functionAppName']", "type": "Microsoft.Web/sites", "location": "[resourceGroup().location]", "kind": "functionapp", "dependsOn": [ ... "[concat('Microsoft.Devices/IoTHubs', parameters('iotHubName'))]" ], "properties": { "siteConfig": { "appSettings": [ { "name": "IoTHubConnectionString", "value": "[concat('Endpoint=', reference(resourceId('Microsoft.Devices/IoTHubs', parameters('iotHubName')), providers('Microsoft.Devices', 'IoTHubs').apiVersions[0]).eventHubEndpoints.events.endpoint, ';SharedAccessKeyName=iothubowner;SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/IotHubs', parameters('iotHubName')), providers('Microsoft.Devices', 'IoTHubs').apiVersions[0]).value[0].primaryKey, ';EntityPath=', reference(resourceId('Microsoft.Devices/IoTHubs', parameters('iotHubName')), providers('Microsoft.Devices', 'IoTHubs').apiVersions[0]).eventHubEndpoints.events.path)]" } ] } } } ] ... }
Summary
We have seen how to create an ARM template that will deploy an Azure IoT Hub and output the connection string and the Event Hub compatible connection string.
You can get the project source code here:
Browse the GitHub repository
Please feel free to comment or contact me if you have any question about this article.
Thanks for the example! I will use it. But hope more modern API version allows a nicer way to get the connection strings.
P.S.
Even the secrets don't work anymore, you may want to obfuscate them.