IoT Edge Docker Image Cleanup

If anyone has done any development on, or run in production for a while, an IoT Edge box, inevitably they have a lot of unused docker images ‘hanging’ around on their IoT Edge boxes, just being bums and eating up disk space.  This can happen particularly if you’ve developed your own custom modules and released new versions over time, with new docker image tags over time, as shown below for the ‘echomodule’ module  (click on it for clearer view)

iotedge-images

A frequent question we get from customers is “will IoT Edge clean up these unused images?”.  The answer is, well…  “no”.   There are suggestions we’ve seen around maybe using cron jobs and such to schedule a ‘docker image prune’ run to remove them, but I wanted to see if I could do this as an IoT Edge module itself, so you don’t have to fool with OS-level config and can run/configure it remotely.

The short version is:  yep!  (you probably guessed that since I wrote this post, right?  you’re pretty clever)

The longer version can be found at our Azure IoT GBB (my team)’s github site here –>  IoT Edge Image Cleanup

Enjoy and as always, let me know what you think…

–Steve

Install IoT Edge on Red Hat Enterprise Linux (RHEL) – 7.x

This post demonstrates how to get Azure IoT Edge to work on Red Hat Enterprise Linux (RHEL)

Hi all.   Sorry for the lack of content lately.  Between some (minor) personal stuff and the coronavirus stuff with both myself and my customers, it’s been a bit of a goat-rodeo around Busby Manor lately. 

Smile

Recently I needed to help a customer get IoT Edge installed on a box running Red Hat Enterprise Linux (RHEL).  In this case, it was version 7.5, but this should work for other 7.x based versions too..   I think .  I’m about as far away from a RHEL expert as you can get.

NOTE:  credit for most of this info goes to Justin Dyer, a peer of mine on the Azure IoT pre-sales team!

First off, if you look at the “platform support” documentation for IoT Edge, you’ll notice that RHEL is a “Tier 2” supported platform.  That’s a fancy way of saying that either MSFT or someone we know has gotten it working on that platform and it generally works.  However, it also means that it is not a “gating” platform for us, meaning it’s not a platform that we test extensively every release on before we release it.  In other words, not working on RHEL will not block or gate a release.  That’s not because we don’t like it, or don’t want to “tier 1” support it, but rather it’s just one that we haven’t gotten around (yet) to doing all the necessary work to get it fully integrated into our extensive testing platform.  We love all Linux!  We’ve just prioritized based on how often we run into various platforms in the field with our customers. 

Now, with all the caveating out of the way, IoT Edge on RHEL DOES work, and seems to work fine, and we DO provide RPM packages for it whenever we do a release.

Pre-requisites

Ok..  enough pre-amble, let’s jump in.   For RHEL, we provide RPM packages that you can install with YUM…  The actual IoT Edge install is reasonably straightforward, once you get through the big pre-req, which is container-selinux.

The big issue is that the moby engine (i.e. open source docker) underneath IoT Edge needs a newer version of container-selinux than was installed on RHEL 7.5.   We need version 2:2.95 or greater.  If you have it already, great – proceed.

Smile

If you don’t, you can manually download from here and update.  Updating that package will be left as an exercise to the reader  (remember:  I’m not a RHEL expert, but hopefully you are )

If you are running your own RHEL install, you can skip this next section and jump down to the “Install IoT Edge” section

A note about RHEL on Azure VMs

Most of the testing I did here was on RHEL running in an Azure VM built with our ready-made RHEL images.  If you are running it on your own, you can skip this section.

container-selinux is found in the “rhel-7-server-extras-rpms” repo, which our Azure RHEL VMs do not have access to.  There are instructions on how to “remove the version lock and install the non-eus repos” in order to get access to it.

But, if you don’t want to read all that, these are the net instructions that you need to run:


sudo rm /etc/yum/vars/releasever
sudo yum --disablerepo='*' remove 'rhui-azure-rhel7-eus'
sudo yum --config='https://rhelimage.blob.core.windows.net/repositories/rhui-microsoft-azure-rhel7.config' install 'rhui-azure-rhel7'
sudo yum install container-selinux

Once those are complete, you can proceed with the “install IoT Edge” section below

Install IoT Edge

Finding the right packages

Before we install IoT Edge, a short note about how to release IOT Edge.  For all of the “non-docker-based” parts of the runtime (i.e. ignoring edgeAgent and edgeHub for the moment), there are really four major components of the runtime:

  • the moby engine:  the open-source version of docker, basically
  • the moby CLI:  gives you the ‘docker’ commands
  • libiothsm:  MSFT provided library that implements the security abstraction layer that let’s the edge runtime talk to various security hardware (like TPMS)
  • iotedged:  the IoT Edge “Security Manager”, which is the daemon based part of IoT Edge and really the component that ‘bootstraps’ all the rest of IoT Edge

When we do a ‘release’ (in the github sense of ‘release’) of IoT Edge, we only provide new packages for those components that changed with that release.  So, for example, in the 1.0.8 release, we had changes in all four components and you’ll see (under “assets”) new *.deb and *.rpm packages for all of them.

But in 1.0.9, only libiothsm and iotedged changed, so you only see new packages for those two components.

Unfortunately, that complicates the edge install for us, just a little bit.  So, for a given IoT Edge release, you need to spelunk a little to get the latest versions.  For the moby engine and CLI, you can usually find the latest version on the packages.microsoft.com site.  That’s the easier one.   For the iot edge components, unfortunately that requires a little more digging.  For the release you want to install, say 1.0.9, you have to work backwards through the releases to find the latest one in which we updated the libiothsm and iotedge components, in this case 1.0.8.  So, you need to go find those links, under ‘assets’ of each release, and capture the latest URL’s to the libiothsm and iotedge packages.

Sorry about that.  The good news is, that’s the hard part.

finally, install iotedge

Ok, finally, we can install IoT Edge.

The first step is to download the packages.  Make a folder on your device to hold them, CD into that folder, and then run


wget https://packages.microsoft.com/centos/7/prod/moby-cli-3.0.10%2Bazure-0.x86_64.rpm
wget https://packages.microsoft.com/centos/7/prod/moby-engine-3.0.10%2Bazure-0.x86_64.rpm
wget https://github.com/Azure/azure-iotedge/releases/download/1.0.9/libiothsm-std_1.0.9-1.el7.x86_64.rpm
wget https://github.com/Azure/azure-iotedge/releases/download/1.0.9/iotedge-1.0.9-1.el7.x86_64.rpm

Those URL’s are valid as the ‘latest’ releases of each component as of the 1.0.9 version of IoT Edge.  As future versions ship, you’ll need to see if the various components of them have updated, and replace the URI’s appropriately.

Next, we just install IoT Edge components with the following commands (run them one at a time, as they ask a y/n question in the middle):


sudo yum install moby-cli-3.0.10+azure-0.x86_64.rpm
sudo yum install moby-engine-3.0.10+azure-0.x86_64.rpm
sudo rpm -Uhv libiothsm-std_1.0.9-1.el7.x86_64.rpm
sudo rpm -Uhv iotedge-1.0.9-1.el7.x86_64.rpm

Obviously if you had to download newer package names, replace them.

Once those packages finish installing, all you need to do is open config.yaml and add in your connection string or DPS information and restart iotedge with:


sudo systemctl restart iotedge

There you go.  Enjoy.  As always, if you have issues, feel free to hit me up in the comments!

Connect MXChip DevKit to Azure IoT Edge

A customer of mine who is working on an IoT POC to show their management wanted to connect the MXChip Devkit to IoT Hub via IoT Edge. This turned out to be trickier than it should be, as the connection between the MXChip and the IoT Edge box is, like all IoT Hub/Edge connections, TLS-encrypted. So you have to get the MXChip to trust the “tls server” certificate that IoT Edge returns when a client tries to connect. Thanks to some great ground-laying work by Arthur Ma, I wrote a hands-on lab walking you through this scenario. The lab can be found on my team’s github site here

Enjoy, and let me know if you have any problems.

Monitor IoT Edge connectivity with Event Grid

Hi!  Long time, no see.  It’s been a few months since I posted here.  I won’t bore you with the many “I’ve been busy” excuses (but I have!).  Anyway, enough assuaging my guilt over not having shared with you guys in a while.   Let’s get to why you came here (since google.com probably sent you)

One of the frequent questions we get related to any IoT device, but especially IoT Edge is “how do I know if my IoT device/edge is up and running and talking to IoT Hub?”  I was recently researching this topic for a customer and ran across some cool stuff.   I even did a quick little sample/demo I’ll share at the bottom involving IoT Edge and Microsoft Teams. 

IoT Edge/Hub and Event Grid

As you may know, IoT Hub integrates with Azure Event Grid.  Azure Event Grid is a fully managed event routing service that uses a publish-subscribe model.  Event Grid integrates with a bunch of Azure Services (a pic is on that link above), including IoT Hub.    As shown from the list below, stolen from that same link, IoT Hub publishes several event types to Event Grid.

  • Microsoft.Devices.DeviceCreated – Published when a device is registered to an IoT hub.
  • Microsoft.Devices.DeviceDeleted – Published when a device is deleted from an IoT hub.
  • Microsoft.Devices.DeviceConnected – Published when a device is connected to an IoT hub.
  • Microsoft.Devices.DeviceDisconnected – Published when a device is disconnected from an IoT hub.
  • Microsoft.Devices.DeviceTelemetry – Published when a device telemetry message is sent to an IoT hub

Note that Device Connected and Device Disconnected events will be raised when the devices connect or disconnect.  That can be an explicit connection and disconnection, or a disconnect after a certain number of missed ‘heartbeats’, meaning the device or network may have committed seppuku (not to be confused with Sudoku) .  You can set Event Grid subscriptions to these events and then respond however you want to (Logic Apps, Azure Functions, Text, Email, etc)

So, what about IoT Edge?

Ok, Steve, but google sent me here because I searched for information about monitoring IoT *Edge*connectivity.  If it pleases the court, are you going to talk about that any time soon?

Yes!  The reason this is relevant to IoT Edge is that the connection/disconnection events not only work for IoT end-devices, but it also works for IoT Edge modules!   So, leveraging that, you can tell if any of your IoT Edge modules disconnect and don’t reconnect (did they crash?) or also if the IoT Edge runtime itself loses connectivity (crash?  network issue?) by looking for those events for the $edgeHub and $edgeAgent modules. 

Ok skippy, prove it!

Fine!  I will…   The IoT Hub/Event Grid teams have a nice little example of using the integration between IoT Hub and Event Grid to persistently track the status of your device connectivity in a CosmosDB database, via a LogicApps.  It will have a record for all of your devices, and their last connect/disconnect time.   It’s a pretty nice little sample and could be the beginning of a connectivity monitoring solution  You can build a UI, have it pull the status from CosmosDB, etc.

Check it out if you have the time or inclination.

However, I was too lazy busy to create a CosmosDB, stored procedure, UI, etc.   So I took the quicker way out.  I created a Channel in Microsoft Teams and, each time a device/module connected or disconnected, I just post a message to the channel.

I basically followed that article and all the pieces of it, except everywhere it referenced the stored procedure and CosmosDB, I just instead leveraged the built-in LogicApps connector for Microsoft Teams, which is super-easy to use.

The result is shown below.  I have a raspberry pi running IoT Edge sitting on my desk.  It has a SenseHat accessory in top of it and was running a module that I wrote that talks to it (uncreatively named “sensehat” – they don’t pay me enough for naming creativity).   At about 10:55 local time, I reached over the yanked the power plug out of the raspberry pi.  2-3 minutes later (after some missed heartbeats), viola, I got a post to my Teams channel telling me that both my module (sensehat) as well as the Edge runtime ($edgeHub and $edgeAgent) had disconnected.   (rpihat2 is the device name for my IoT Edge device)

IoT_Hub_Event_Grid_Teams

(full disclosure – I made ZERO effort to make the message look pretty)

I could just as easily, via LogicApps again,  have leveraged SendGrid, Twilio, etc to send me email alerts, TXTs, or whatever for these events.  If you do use Microsoft Teams, you can set alerts on the Channel when new events are posted too.  If you use the mobile app, you even get push notifications.

I didn’t write step-by-step instructions for doing this, as I hope it’s not too hard to follow the Azure IoT team’s cool instructions above and just substitute Teams/SendGrid/Twilio, etc for the output..   but feel free to let me know if I need to do step-by-step.

Leave a comment and let me know if this, or any of my other content, is useful (or if you need/want step-by-step instructions)

Enjoy,

–Steve

Connect Node-Red to Azure IoT Edge

NOTE:  updated on 2/14/2019 to add the note about adding the content type and content encoding to the MQTT topic if you want to route messages based on the message body in IoT Edge

Recently on behalf of a customer, I was asked if it was possible to connect Node-Red to Azure IoT Edge.  Node-Red, an IBM sponsored graphical process design tool, is increasingly popular in IoT scenarios.

My first thought was “well sure, there is an IoT Hub ‘node’ for Node-Red, surely it can be used for connecting to IoT Edge”.  Well, looks like it can’t.   For two reasons, 1) it’s not designed to do that (no way to specify the edge box/GatewayHostName) and 2) it’s not very good  Smile.  There are a couple of other options out there, too, but none of the IoT Hub specific ‘nodes’ will work for this use case.

Secondly, and this is really cool, my Azure IoT Global Black Belt peers in Europe have developed an IoT Edge module that hosts Node-Red inside Edge itself, where you can orchestrate your IoT process, and have it drop messages on edgeHub for further edge processing and/or uploading of messages to IoT Hub.   *If* you are interested in hosting and managing Node-Red from inside IoT Edge itself, you should definitely check it out.  It’s really nice.

With that said, a lot of customers (including the one that prompted this post) either already have a Node-Red environment outside of IoT Edge, or just for a host of reasons want to keep them separate.  If so, this post is for you.   Since the IoT Hub ‘node’ won’t seem to work for this use case, we are going to use the MQTT node.  One thing to note – this only addressed device-to-cloud telemetry.  I haven’t (yet) addressed cloud-to-device management/messages/configuration because my customer’s use case didn’t (yet) need it.  Baby steps.

First of all, let’s get some pre-requisites out of the way.

Pre-requisites

For this post, I assume a number of things:

  • You’ve already got a Node-Red environment up and going.  If not, follow the directions to do so from the Node-Red site.    I also assume you have some familiarity with Node-Red.  If you don’t, they have some nice tutorials on the site.  Make sure Node-Red is running   (node-red-start from the command prompt)
  • You already have Azure IoT Edge setup as a transparent gateway and have confirmed that you can connect to it over the 8883 MQTT using the handy openssl command included in the docs (this is necessary because Node-Red will be connecting to IoT Edge as a “leaf” or “downstream” device).  NOTE:  make sure that whatever name you use in the hostname parameter in your config.yaml file is the same resolvable name that you will use from Node-Red to connect to the IoT Edge box.  If you have to change it in config.yaml to match, restart iot edge before proceeding.
  • From your IoT Edge box, copy/download/ftp/whatever the root CA cert that was used to set up the IoT Edge box.  If you used a ‘real’ cert (i.e. from DigiCert, Baltimore, etc), then you’ll need to get the public key cert from them in the form of a pem or crt file.  If you used the convenience scripts from the transparent gateway instructions above, it’s the cert labeled azure-iot-test-only.root.ca.cert.pem in the ‘certs’ folder underneath whatever folder you generated your certs in.   Just grab that file and get it to your local desktop, we’ll need it later.
  • Your IoT Edge box is reachable, network-wise, from your Node-Red box.  Can you ping it?  Can you resolve it’s DNS name?  Can you telnet to 8883?

In my case, I ran everything (Node-Red and IoT Edge both) from a single Raspberry Pi that I had laying around.  But beyond the requirements for Node-Red and IoT Edge themselves, there is no requirement for specific OS’es or co-location, as long as they can see each other on the network, and IoT Edge can get to IoT Hub.

Setup

Ok, enough caveating and pre-req’ing, let’s do some setup.

One of the things you will need is an IoT Hub device identity in the hub to represent our Node-Red flow.  If you haven’t already, create an IoT Device to represent our Node-Red “device” (the flow).  Go ahead and capture the “device id”, and URI of the Hub (something.azure-devices.net).

You will also need a Shared Access Signature, a token that authenticates the device to IoT Hub.  The easiest way to generate one is to open up a cloud shell instance in the azure portal.   To do that, click on the Cloud Shell button in the top nav part of the portal.  It looks like this:

cloud-cli

You may have to go through some preliminary setup (select a blob storage account, etc) if it’s the first time you’ve done it.  You can use either Powershell or Bash.  I prefer Bash myself, but both work.

Once the cloud shell is created and you have a command prompt, the first thing we have to to do install the azure iot hub extension for the azure cli interface.   You can do this by running this command

az extension add –name azure-cli-iot-ext

You only have to do it the first time, it ‘sticks’ for subsequent times you use the cloud shell.  Once this is done, you can generate your SAS token with this command:

az iot hub generate-sas-token –d [device id]-n [hub name] –du [duration]

where [device id] is the device id from the device you created earlier, [hub name] is the short name of your IoT Hub (with the .azure-devices.net) and [duration] is the amount of time, in seconds, you want your toke to be valid.  Shorter values are more secure, but longer values are easier to manage (you have to re-create and re-configure the token in Node-Red when it expires).  It’s up to you to figure out your best balance between security and convenience Smile.  I will remain silent on the topic here.

Once you generate your token, you need to copy it somewhere, as we’ll need it later.  The process will look something like this (click on the picture to enlarge):

sas-token

You need the part that starts with “Shared Access Signature”, without the quotes.  Highlighted in yellow in the picture.     NOTE in the picture that ‘sdbiothub1’ is the name of my IoT Hub used in this demo.  Also note that I used a duration of 60 seconds, so you can’t monkey with my IoT Hub.  Not that you would ever do anything like that.

Ok, we have all the information we need to get started.

IoT Edge and IoT Hub debug prep

There’s really nothing else we need to do in order to get IoT Edge and IoT Hub ready, but we’ll do a couple of things to have them ready in the background to both debug our Node-Red setup and see the messages flow through if when we are successful.

On the IoT Edge box, run

iotedge logs –f edgeHub

This brings the edgeHub logs up and ‘follows’ them so we’ll be able to see our Node-Red box connect.

In the cloud shell that you have open in Azure, run this command

az iot hub monitor-events –n [hub name] –timeout 0

where [hub name] is your IoT Hub name as before.  This will monitor messages that make it to IoT Hub so we can see our messages flow in from Node-Red.

Just leave those things up and running in the background and we’ll come back to them later (you can CTRL-C to get out of them later when ready)

Let’s set up our Node-Red instance.

Node-Red configuration

On your Node-Red box, bring up the Node-Red configuration page in your browser (typically http://nameofyournoderedbox:1880)

You’ll have a blank canvas.  You can do something more sophisticated later, but for now we’ll just do a VERY simple flow to show messages flowing to IoT Edge.   We are using a very simple flow for two reasons — to show just the main concept of connecting to IoT Edge and because my Node-Red skills are every bit as awesome as my blog writing skills.

From the node palette on the left hand side, grab the MQTT node from the *output* (not input) section of the palette and drop it on the design canvas.  Also grab an Inject node from the input section and drop it on the canvas.    Connect the output of the Inject node into the input of the MQTT node, as shown  below (your MQTT node won’t say ‘connected’ yet).

node-red-canvas

Double click on the MQTT node to configure it.

  • In the ‘name’ field at the bottom, just give it a name you like.  Doesn’t matter what it is
  • For QOS, choose either 0 or 1 .  IoT Hub/Edge do not support QOS 2.
  • For the TOPIC, enter devices/[device id]/messages/events   (where [device id] is the device id you registered earlier in IoT Hub)
  • NOTE:  *if* your payload is JSON, and *if* you think you might want to do routing of the messages based on the *body* of the message, you need to send a content type of ‘application/json’ and a content encoding of ‘utf-8’  (or 16, etc).  to do that, we need to append that information (url-encoded) to the MQTT topic, which gets passed in as a system property.  So, in that case, the ‘topic’ call would look like this (note the ‘/$.ct=application%2Fjson&$.ce=utf-8’ appended to the MQTT topic)
    • devices/[device id]/messages/events/$.ct=application%2Fjson&$.ce=utf-8

Below is a screen shot of mine.  Ignore the “Server” field in the screenshot, as we haven’t configured that yet.  For my ‘device id’, I called my device nodeRedTest for this and subsequent screenshots

mqtt-node-config

I have Retain set to false, but I really have no idea if that makes any difference or not.  I’m not a Node-Red or MQTT expert, although I do play one on conference calls with customers Smile.

Ok, next we will configure our ‘server’.  Click on the little edit pencil next to the Server box

Give your ‘server’ a name (doesn’t matter what it is – I called mine iothub-mqtt in the screenshots).  On the Connection Tab,

  • for the Server  box, enter the hostname of your IoT Edge box (exactly as it’s specified in config.yaml and we verified we could resolve earlier)
  • Change the port from 1883 to 8883  (the MQTT over TLS port)
  • CHECK the Enable Secure (SSL/TLS) connection box.  We will come back and configure that in a moment.
  • For the “Client ID” box, enter your IoT device ID from earlier
  • UNCHECK the “use legacy MQTT 3.1 support” box.  IoT Edge does not support legacy MQTT connections

Example values based on my setup are shown below.  ‘rpihat1’ is the hostname of my IoT Edge box.

mqtt-node-connection

Click on the ‘Security’ Tab

  • For the username, enter  “[hub name].azure-devices.net/[device id]/api-version=2016-11-14”  (filling in your values for hub name and device id)
  • For the password, paste in the SAS token, in it’s entirety that you copied earlier

A sample screenshot of mine is below

mqtt-node-security

You don’t need to set or change anything on the Messages tab

Back on the Connections Tab, next to the “TLS Configuration” box, click on the edit/pencil button

Next to “CA Certificate”  (the fourth button down), click “Upload”.  Upload the root CA certificate that you downloaded earlier.

For the name at the bottom, just make up a meaningful name for this config and click Save

Click Save or Update on the main node configuration box to save all our values and close it as well

We are now ready to save our flow.  Click “Deploy” in the upper right to save and deploy your flow.

Checking progress

At this point your flow is running and you should see your MQTT node status as “Connecting” with a yellow dot next to it.  Flip over to your IoT Edge box and look at the edgeHub logs.  You should see something like this….  (it may take a minute or more to look like this)

edgehub-logs

For some unexplained reason, the TLS Handshake between the Node-Red box and my IoT Edge box fails exactly seven times before it successfully connects.  I have no idea why and haven’t (and probably won’t) put in the effort to troubleshoot.  I suspect it’s related to a a 10-second delay between tries and the 60-second refresh value in the node config..  but who knows?

Eventually you should see the successful connection in the logs as shown the bottom half of the picture above.

If you flip back over to your Node-Red canvas, you should have a green dot next to your MQTT node and it should say “connected”

We just left the Inject node configured with it’s default of “timestamp”…  it really doesn’t matter what we send our IoT Hub at this point, we just want to see messages go in, and an updating timestamp every time we send a message is handy.  NOTE that the Inject node sends ‘timestamps’ in Unix Epoch time (number of seconds since Jan 1, 1970) so it just looks like a big number.

click on the little button (circled in red below) on the Inject node in the Node-Red canvas

inject-button

This will cause a message to be created and sent to the MQTT node for sending to IoT Edge.  That will, in turn, send the message up to IoT Hub.

Flip over to your Azure Portal browser window and you should see your message having made it to IoT Hub.  click on the Inject button a few more times to send a few more messages (because it’s fun)

The output should look something like this

iothub-debug

Your Node-Red installation is now connected and sending messages through IoT Edge up to IoT Hub.  You can now make your Node-Red flow more sophisticated (reading from sensors, etc) and do any and all the goodness that comes with IoT Edge (stream processing, custom modules, machine learning, etc).

If you have any questions or feedback, please leave them in the ‘comments’ section below.

Enjoy!

Mosquitto MQTT broker to IoT Hub/IoT Edge

 

EDIT:  edited on 8/30 to change tls version to tls 1.2.  Seems that TLS 1.0 doesn’t work any more.  Thanks to Asish Sinha for the heads up.  Also updated the api-version to the latest

 

Earlier, I had a post on connecting an MQTT client to IoT Edge.

It seems like lately my team and I have had a lot of customers with brownfield equipment that can speak MQTT, but are either too old or too low powered (the devices, not the customers Smile)  to do MQTT over TLS.  It is also often the case that you have no real control over the MQTT topic(s) that the device sends events/messages over.  Additionally, many devices even imply “intelligence” or “data” into the topic structure, meaning the topic hierarchy itself conveys information vs. only having important information in the message payload.

Both a TLS connection, and sending data on a very specific topic, are current requirements to talk to either IoT Edge or IoT Hub itself over MQTT.   So, how do we overcome this impedance mismatch between what IoT Hub/Edge requires, and the equipment can do?   One way is to use a middle layer to do the translations.  A popular choice is the open source MQTT broker mosquitto from the Eclipse Foundation.   Mosquitto has a built-in option to set up an MQTT “bridge”, in which the broker will accept incoming messages over MQTT and then forward them as an MQTT client to another MQTT server.  The good news is, Mosquitto can listen to the unencrypted MQTT traffic (port 1883 by default), and then forward it along over a TLS-protected MQTTS connection (port 8883) via this bridge. 

That takes care of our MQTT vs. MQTTs issue.  But what about the any topic vs. a specific topic problem.  Unfortunately, IoT Hub and IoT Edge both only accept telemetry/event data on a specific MQTT topic:  devices/[device-id]/messages/events where [device-id] is the ID of the connected device.  That one is a little trickier, and will be addressed later in this post after we cover the basics of setting up the bridge.

A couple of notes/caveats before we get started:

  • I am NOT a mosquitto expert.  I’ve learned just enough to get this working Smile
  • This is certainly not the only way to solve this problem.  But is one way that seems to work pretty well.
Mosquitto Bridge Setup for IoT Hub/Edge

Before we can configure our Mosquitto MQTT bridge, there are a few pre-requisites to take care of 

  • If you don’t already have one, create an IoT Hub and create a device (only follow that one section) that will represent our Mosquitto broker. The messages in IoT Hub/Edge will appear as if they come from the broker as the IoT device.
  • If you are talking directly to IoT Hub, you can skip this step.  If you are wanting to route your messages through IoT Edge, you need to setup an IoT Edge device as a gateway.
  • Gather the TLS server-side root certificate.  In order for mosquitto to establish a TLS connection to either IoT Hub or IoT Edge, it needs to trust the server-side TLS certificate that will be presented to the broker when it tries to open the connection to IoT Hub/Edge.  Gathering the CA cert from which the TLS server-side cert was generated, the process differs slightly based on whether you are connecting to IoT Hub or IoT Edge.  Either way, save the cert to a file on the mosquitto server, we’ll use it later.

For IoT Hub, the TLS certificate chains up to the public DigiCert Baltimore Root certificate. You can create this file by copying the certificate information from certs.c in the Azure IoT SDK for C. Include the lines —–BEGIN CERTIFICATE—– and —–END CERTIFICATE—–, remove the ” marks at the beginning and end of every line, and remove the \r\n characters at the end of every line.  Name the file with a .pem extension.

For IoT Edge, use whatever root certificate you used to create the IoT Edge Device CA Certificate.  If you used our convenience scripts to set up IoT Edge, that will be the azure-iot-test-only.root.ca.cert.pem found in the ‘certs’ folder where you ran the scripts

Now that we have our pre-req’s finished, we can do our Mosquitto  bridge setup.  This is done via the Mosquitto configuration file.   There may be other things in that file, however, below is an example configuration entry.


# Bridge configuration
connection azureiot-bridge
log_type debug
address [edge or hub fqdn]:8883
remote_username [iothub-shortname].azure-devices.net/[device-id]/api-version=2019-03-31
remote_password [sas-token]
remote_clientid [device-id]
bridge_cafile [iot hub or edge root ca cert]
try_private false
cleansession true
start_type automatic
bridge_insecure false
bridge_protocol_version mqttv311
bridge_tls_version tlsv1.2
notifications false
notification_topic events/

topic devices/[device-id]/messages/events/# out 1

The parts in bold need to be replaced with your values, where

  • [iot hub or edge FQDN] is the DNS name of either your IoT Hub (including the .azure-devices.net) or your IoT Edge device  (i.e. whatever name was used as the ‘hostname’ in config.yaml on IoT Edge)
  • [iothub-shortname] is the name of your IoT Hub  (e.g. ‘myiothub’) without the .azure-devices.net
  • [device-id] is the name of the IoT device created in IoT Hub to represent this broker
  • [sas-token] is a SAS token generated for that device-id in that hub
  • [iot hub or edge root ca cert] is the full path to the root certificate file you created earlier
  • All values are case sensitive.

The very last line (that starts with the word ‘topic’) subscribes the bridge to all messages that are sent with the topic structure of ‘devices/[device-id]/messages/events/#’ (the # is a wildcard to include any sub-topics). When a message that fits that topic structure gets published, the bridge will get it and pass it along to the IoT Hub/Edge.

restart your Mosquitto broker using the updated configuration file.  You should see debug output indicating that it has connected the bridge (and if you are using IoT Edge, you should see debug output in the edgeHub logs showing the connection from the broker)

If you want to test the connection, you can send a test message using the mosquitto_pub command, using the following command (replacing [device-id] with your device id you created above):


mosquitto_pub -t devices/[device-id]/messages/events/ -m "hello world!"

The trailing slash is important and required. You should see the message above be forwarded by the MQTT bridge to either IoT Hub or IoT Edge.

If you are fortunate enough to have full control over your MQTT topic structure from your devices, and there is no intelligence in your topic structure, you’re done.  Congrat’s and have fun!  You can just point your MQTT clients at the broker address (making sure you update the MQTT topic to point to devices/[device-id]/messages/events/) and rock and roll.

However, for the use cases where you don’t have MQTT topic control, or there is intelligence in your topic hierarchy, keep reading.

MQTT Topic Translation

Unfortunately, this is where things get a little less “clean”.  The mosquitto MQTT bridge has no ability to “rewrite” or completely change the topic structure of the messages it receives.  One way to do it is to write a simple client that subscribes to all potential topics from which the MQTT devices might send data, and then resend the payload after translating the MQTT topic into the IoT Hub/Edge required topic structure. 

Below is a simple python script that I wrote to do the translation as an example.  This sample subscribes to all topics (#  – the wildcard) and, if the message doesn’t already use the IoT Hub/Edge topic structure, it simply resends the message payload using the hub/edge topic.


import paho.mqtt.client as mqtt
import time

#replace [device-id] with your device you created in IoT Hub.
iothubmqtttopic = "devices/[device-id]/messages/events/"

# this sample just resends the incoming message (on any topic) and just
# resends it on the iothub topic structure.  you could, of course, do any
# kind of sophisticated processing here you wanted...
def on_message(client, userdata, message):
     global iothubmqtttopic
     if(message.topic != iothubmqtttopic):
         messageStr = str(message.payload.decode("utf-8"))
         print("message received " ,messageStr)
         print("message topic=",message.topic)
         client.publish(iothubmqtttopic, messageStr)

# replace <broker address> with the FQDN or IP address of your MQTT broker
broker_address="[broker address]"

print("creating new instance")
client = mqtt.Client("iottopicxlate") #create new instance
client.on_message=on_message #attach function to callback
print("connecting to broker")
client.connect(broker_address) #connect to broker

print("Subscribing to all topics")
client.subscribe("#")

client.loop_forever() #stop the loop

Of course, this is one extremely simple example, that just passes along the same message payload and swaps out the message topic.  You can, of course, add any kind of sophisticated logic you need.  For example, you could parse the topic hierarchy, pull out any ‘intelligence’ in it, and add that to the message payload before sending.

if you want to test this, copy this python script to a file, edit it to add your device id and URI of your mosquitto broker, and run it.  You can then try….


mosquitto_pub –t /any/topic/structure/you/want –m "hello world"

You should see the python script receive the file, do the translation, and republish the message.  Then the mosquitto bridge will forward the new message along to IoT Hub/Edge.

Connect MQTT client to Azure IoT Edge

NOTE:  Edited on 9/13/2018 to fix bugs in code and make it send data in a loop, just to be more realistic test

NOTE:  Edited on 2/14/2019 (Happy Valentine’s Day!) to add the contentType and contentEncoding values to allow you to route on message bodies.

As you may know, MSFT provides some nice SDKs to connect devices to IoT Hub.  Those SDKs abstract away much of the complexity of connecting, protocol abstraction, device twins, direct methods, etc.

However, there are many reasons, in particular “brownfield” devices, where you might prefer to connect via an open source MQTT library, like the nice Paho MQTT library, directly to IoT Hub.  The IoT product group has put together a very good description and sample code for connecting your MQTT device directly to IoT Hub.

With the general availability of Azure IoT Edge, the natural next question is “Can I connect my MQTT devices to IoT Hub *through* IoT Edge?”.  This lets you take advantage of all the nice local processing and cloud services you can pull to the Edge, but make minimal changes to your MQTT-based IoT devices.

The short answer is YES!  For the (only slightly) longer answer, keep reading.

There are only a few steps needed to make this happen.

  • If you haven’t done it yet, you need to set up IoT Edge as a transparent gateway.   The instructions for that are here (linux) and here (windows)….(don’t read too much into the “transparent” part of that, as you can still add other modules such as custom code, Azure ML, Stream Analytics, etc)
  • The MQTT client will establish a TLS connection to the IoT Edge device.  As such, it needs to trust the server certificate that the edgeHub component of IoT Edge presents to it.  For the MQTT/TLS connection to work, depending on the MQTT client (I use the paho-mqtt library below, just like the IoT team did), you’ll likely need the “root ca” certificate that was used to generate the device ca certificate used in IoT Edge.  If you used our test scripts provided at the links in step 1 above, then the certificate you need is at $CERTDIR/azure-iot-test-only.root.ca.cert.pem  (where $CERTDIR is the same one from step 1).  If you used a “real” certificate authority, like Baltimore, DigiCert, etc, they each generally have a method to get their public signing certs.   (we happen to have a couple of them here).   You will need to have them in a file you can reference on the file system of the MQTT box.
  • Your MQTT client needs to be able to resolve the hostname of your IoT Edge box (i.e. you should be able to ping it by name).  If you used a “real” FQDN in DNS for your IoT Edge box, then you are good.  If you didn’t, you may have to add a “hosts” file entry (/etc/hosts on Linux or \windows\system32\drivers\etc\hosts on Windows) to resolve the name.   This name should match the “hostname” parameter from the IoT Edge’s config.yaml file.
  • Finally, we need to make a few minor modifications to the sample code provided by the IoT product group, as seen below…   The biggest change is that, while we still need to authenticate the device to IoT Hub with a valid SAS token for the IoT Hub (and thus the credentials stay the same as the direct case), the actual connection point will be edgeHub.

The code changes to the python script are shown below.  I’ve commented each change with a comment starting with #EDGE. Also, if you copy and paste the code, my blog editor doesn’t respect spaces, so you need to indent the lines under the on_xxxx function definitions, and the code under the while(True) statement

from paho.mqtt import client as mqtt
import ssl

import time

path_to_root_cert = '[path to root CA cert]' # e.g. './azure-iot-test-only.root.ca.cert.pem'
device_id = "[iot device id]" # e.g. "myIoTDevice"
sas_token = "[generated SAS token]" # e.g. SharedAccessSignature sr=exampleIotHub.azure-devices.net%2Fdevices%2FmyIoTDevice&sig=8%2Fo6sdsFE%2BplYLQJrxIo5Usx1iVV0gnySaVhkh7aNOk%3D&se=1563635795"
iot_hub_name = "[iothub short name]" #e.g. exampleIoTHub

#EDGE - the FQDN of the device.. for example: myedgedevice.local
edge_device_name = "[edge device name]"

def on_connect(client, userdata, flags, rc):
print ("Device connected with result code: " + str(rc))
def on_disconnect(client, userdata, rc):
print ("Device disconnected with result code: " + str(rc))
def on_publish(client, userdata, mid):
print ("Device sent message")

client = mqtt.Client(client_id=device_id, protocol=mqtt.MQTTv311)

client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_publish = on_publish

#EDGE - we need to add the + "/api-version=2016-11-14" (api version) to the end of the username. Technically, you can do this if you are
# talking directly to IoTHub as well, but it's optional. Edge seems to require it.
client.username_pw_set(username=iot_hub_name+".azure-devices.net/" + device_id + "/api-version=2016-11-14", password=sas_token)

client.tls_set(ca_certs=path_to_root_cert, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1, ciphers=None)
client.tls_insecure_set(False)

# start paho's background processing

client.loop_start()

#EDGE - although we are still authenticating to IoTHub with the IoTHub based SAS token, we are actually connecting
# to IoT Edge device's MQTT endpoint

client.connect(edge_device_name, port=8883)

i=0

while(True):

payload = "{"message_id": %d}" % (i)

client.publish("devices/" + device_id + "/messages/events/", payload, qos=1)

time.sleep(5)

i=i+1

NOTE:  *if* your payload is JSON, and *if* you think you might want to do routing of the messages based on the *body* of the message, you need to send a content type of ‘application/json’ and a content encoding of ‘utf-8’  (or 16, etc).  to do that, we need to append that information (url-encoded) to the MQTT topic, which gets passed in as a system property.  So, in that case, the ‘publish’ call would look like this (note the ‘/$.ct=application%2Fjson&$.ce=utf-8’ appended to the MQTT topic)

client.publish(“devices/” + device_id + “/messages/events/$.ct=application%2Fjson&$.ce=utf-8”, payload, qos=1)

That’s it. At this point you should be able to run your script and see the successful connection reflected in your edgeHub logs (docker logs -f edgeHub) and, assuming you have the default route set (“FROM /* INTO $upstream”), see the data flowing into IoTHub. (note that the sample app only sends one message and then stops… CTRL-C to exit and run again).

Enjoy!