In this blogpost, I'll explain how the communication between VSTS and our devices works in our DevOps for IoT scenario. In two previous blogposts I described the end-to-end scenario and explained how the installation of the application on the target device works. I suggest you read at least the first part to get the big picture.
It's important to point out that the scenario described is technology-agnostic for the most part. It really doesn't matter if your device is running Windows IoT Core (as chosen in my implementation) or any kind or Linux system or something totally different. The only part where this is important is the automated UI tests which clearly demonstrate the power of the Universal Windows Platform.
For this blogpost, I'll concentrate on how our devices are being notified.
In contrast to standard DevOps scenarios (something like a CI/CD chain for Web Development) - there are several challenges. Let's stick with the "refrigerator control" scenario. If I want to program a software for a refrigerator and want to be able to update it after the refrigerator has been shipped, here's a number of things I have to deal with:
Access to Devices
I won't have physical access to the devices. So, I can't plug in a USB stick and install new software. I also won't be able to RDP into the devices.
I can't even tell if the devices are online all the time. So, when I want to send a message, I have to make sure the message really arrives, even if the device is currently unplugged. For an IoT device of course I expect it to be online again at some point.
I don't have "System Center" or something similar to manage my device. My device might be pretty low powered and might not be supported by classic device management solutions at all.
I have to expect to have not only ten or 100 devices. If this was the case, you might think it is okay to do something manually. But in my refrigerator scenario I want to be able to ship millions of refrigerators. So, I have to deal with millions of devices. I need to be able to communicate and orchestrate this massive number of "things".
Azure IoT Hub
The good news is, that for all of those topics there's a solution out there which has been built to exactly address these. It's called Azure IoT Hub. It's here to allow devices to register, to report their state and to communicate bidirectionally between the hub and the device.
So the implemented flow is:
Whenever a device starts an app deployed on the device contacts IoT Hub and registers itself with a unique identifier. In my implementation, this is done by a custom app which could be delivered with the device initially (e.g. it might be burned into the OS image). If the device is already registered, that's fine as well, the device will then just keep a connection open to IoT Hub. This guarantees that IoT Hub is able to talk to the device and vice versa. The protocols supported here are HTTP, AMQP and MQTP - and IoT Hub really doesn't care what the device looks like as long as it is capable to "speak" those protocols.
It's possible to report its state using Device Twins. This could be used to identify certain devices based on properties they are reporting. I didn't do this in my scenario, it's just something I want to point out because you probably would be using this to be able to cluster your devices later.
Whenever you want to send an information to the devices, you would use Azure IoT Hub as your communicator - basically this means we are telling Azure IoT Hub what message we want to transfer to the devices. We can decide what this message should look like content wise. All we need on the device is an app which is able to interpret this message. In my implementation, I already have an app on the device which was responsible for registering the device at IoT Hub. So, I just leverage this application to listen for incoming messages from IoT Hub as well. In my implementation, this is a background application which automatically starts on boot and always restarts itself in case it crashes. (See background applications for Windows 10 IoT Core) If you're working with another operation system you'd just pick something similar for your OS.
In my case the message sent triggers a download of the new application from a FTP server and triggers the installation after the download. I picked this solution because a) it was easy to implement and b) is kind of general purpose. You could of course change the action depending on your specific scenario.
What is worth pointing out is, that Azure IoT Hub really takes all the pain from me, when it comes to the challenges addressed earlier.
- I don't have to have physical access - my device registers itself with IoT Hub and I'm able to talk to it afterwards.
- I don't have to care about connectivity of my device - Azure IoT Hub makes sure messages arrive even if the device is offline while the message is being triggered (similar to sending an SMS via mobile phone).
- I don't have to set up any kind of homegrown device management - because this is exactly what Azure IoT Hub does for me.
- I really don't have to worry about scale, because Azure IoT Hub is built for scale - it supports up to 10.000.000 devices per Azure IoT Hub instance. This is amazing - imagine all of those devices are sending messages in short intervals - Azure IoT Hub is capable of receiving them because this is what it's been created for.
- The best thing is that you can get started with Azure IoT Hub in literally 5 minutes - you just spin up an instance in your Azure portal.
Talking to Azure IoT Hub from VSTS
Now that we know how to communicate with our devices it's really just a question of how to trigger the communication during our End-To-End CI/CD chain based on Visual Studio Team Services (VSTS). In this case I created a release definition which just triggers a node.js script which again is responsible to send a message to all devices.
The script is based on the sample script for cloud to device messaging and can be found here. (I'll go into details on how to setup everything in a future post where I will also point out all downloads again.)
What the script basically does is it queries all devices registered at my IoT Hub instance and then ask IoT Hub to send a message to all of these sequentially. (In my case the message just says "Cloud to device message from VSO" to keep things simple. Of course, I could add Ids, version numbers etc. and based on the information I could react differently on the device after receiving. For my end-to-end show case, I'm just triggering an installation, no matter what message is coming in.
Working with IoT Hub really took the pain from communicating to a wide range of devices. It's easy to get started with and scales for extensive use in the future. So, it's a very crucial component when automating communication to IoT devices. I don't even want to think about building something similar myself because I'd have to bring tons of budget and time to realize this. Azure IoT Hub also works when connecting devices behind firewalls because the connection is always established outgoing - from device to IoT Hub. It should be easily possible to set up the communication whatever the network infrastructure looks like.
Besides this I think it's pretty amazing that I'm able to run npm and node during deployments on a hosted (!) VSTS release agent. This makes leveraging existing scripts really easy and in my case saved a lot of work.
Let me know in the comments section if you have feedback for this scenario.