AWS IoT-Part 1
When I started looking at AWS IoT I just drowned into classic AWS documentation hell. The diagram-less documentation is hard to follow and that is one of the reasons I am writing this article. While the technology design is sound and once you understand works quite well, it is hard to grasp it when you start afresh.
Very Important :-
- AWS IoT endpoint is a MQTT broker.
- The authentication mechanism between Thing or Device and IoT endpoint uses mutual certificate based auth. There are other options such as Cognito etc but we will discuss those later.
- The control i.e. who can publish and subscribe to what topics is defined by IoT policies attached to certificates which are attached to device/s.
We will get to some details in a minute but I want you to first understand the most important thing why we are doing all this. i.e. to send a message from the device to AWS and vice versa.
Message Flow
So once you configure the IoT core you will get an AWS endpoint which is essentially a MQTT broker. How you use this broker is entirely down to how you set things up via policies etc which we will discuss in some detail.
Important thing to remember is that devices can publish and subscribe to whatever topics they want to based on what you authorise them to do.
Ideally you would keep the devices isolated from each other to prevent crosstalk. So the example above I have separated devices and direction of comms using the patterns
dev2iot/<device id>
iot2dev/<device id>
So let’s get to some details about the IoT components and concepts. In the UI you will find some very confusing groupings and hierarchies.
- Thing Type — This is a type of thing. So for example maybe you want to monitor a single channel temperature sensor. So this will be a Temperature Sensor Type.
- Thing — This is an instance of Thing Type. So each of your temperature sensors will have a record as a “thing”
- Thing Group — This allows additional level of grouping. For example you may want to group all sensors by say year when it was manufactured.
- Dynamic Thing Group — This one is interesting because you can define a query and on that basis devices will get added or removed from a group. For example, you may want to upgrade a version of firmware of a device or a config and see that the numbers in a dynamic group change to monitor progress of updates.
- Billing Group — Let’s say you have multiple clients using the same thing type you could group them using this construct for billing purposes.
- Certificate — Each device should be attached to its own certificate. Although AWS do allow same cert across devices, you should use one certificate per device
- Certificate Policies — These policies are attached to the certificate. This was an interesting association. A certificate policy can be attached to multiple certificates.
- Thing Shadow — In most IoT applications you would want to know the current state of the Thing and its inputs or outputs. Thing Shadow is like a record which represents the current most up-to-date state information of the Thing.
Just to summarise the relationship between different object types here is a quick visual.
In words…
- Thing can be of Thing Type.
- Thing can belong to zero or more Thing Groups.
- Certificate can be attached to zero or more Things. Ideally attach unique certificate for each device.
- Policy can be attached to zero or more Certificates.
Steps to add your first device :-
First create a device-policy
Add the following policy json document and create a policy. Remember to replace correct region and account ids in the json below.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Publish"
],
"Resource": [
"arn:aws:iot:eu-west-2:123456789012:topic/dev2iot/${iot:Connection.Thing.ThingName}"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Receive"
],
"Resource": [
"arn:aws:iot:eu-west-2:123456789012:topic/iot2dev/${iot:Connection.Thing.ThingName}"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": [
"arn:aws:iot:eu-west-2:123456789012:topicfilter/iot2dev/${iot:Connection.Thing.ThingName}"
]
},
{
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": [
"arn:aws:iot:eu-west-2:123456789012:client/${iot:Connection.Thing.ThingName}"
]
}
]
}
Note: The default policy that you get when you use the wizard is not useful. There are some nuances which are not documented and all the nice looking example policies that should be used to comply with best practices do not work unless you get some things right which we will come to later.
Add a Thing using a wizard.
Click the Onboard on the main AWS IoT view.
Click Get Started
Select Node to go through steps in this blog. You can choose either platform.
Set the name as 1001.
AWS generates SDK for you. Download the SDK.
Follow steps below. The first time you run start.sh it will install sdk and download the example node script to run. We will edit it in following steps.
Now your device is completely registered.
Next go to Manage tab on main AWS IoT view. You should see device 1001 in the list. Click 1001.
Click on Security tab under thing view.
Once you click the certificate above you will see the attached policy. We need to detach this policy and attach the one we created i.e. device-policy.
Detach 1001 default policy added by AWS.
Attach the policy we created.
Now you can see device-policy properly attached to our thing 1001. Now we are ready to send messages and receive messages from AWS IoT.
Run the sdk sample client
Unzip the connection kit you downloaded earlier.
chmod +x start.sh
./start.sh
Once it installs you can kill the process.
Change the code in node_modules/aws-iot-device-sdk/examples/device-example.js
if (args.testMode === 1) {
device.subscribe('iot2dev/1001');
} else {
device.subscribe('iot2dev/1001');
}
if ((Math.max(args.delay, minimumDelay)) !== args.delay) {
console.log('substituting ' + minimumDelay + 'ms delay for ' + args.delay + 'ms...');
}
timeout = setInterval(function() {
count++;if (args.testMode === 1) {
device.publish('dev2iot/1001', JSON.stringify({
mode1Process: count
}));
} else {
device.publish('dev2iot/1001', JSON.stringify({
mode2Process: count
}));
}
}, Math.max(args.delay, minimumDelay)); // clip to minimum
Modify last line in start.sh to match client-id to thing name.
node node_modules/aws-iot-device-sdk/examples/device-example.js — host-name=xxxxxxxxxxxx-ats.iot.eu-west-2.amazonaws.com — private-key=1001.private.key — client-certificate=1001.cert.pem — ca-certificate=root-CA.crt — client-id=1001
./start.sh
Finally to test if this has all worked …
Enable IoT Logging
Test comms
Test tab in main screen you can subscribe to dev2iot/1001 to receive test messages from the sdk you just ran.
You can publish message into the iot2dev/1001 which will only be delivered to your device.
Best Practices:
- Use one cert per device.
- Make sure your device can only talk to its own topics so there is no cross talk.
- You could create a generic template that could be applied across certificates just like the one above which will evaluate which topic the device is allowed to interact with.
The nuance which I took a long time to figure out (thankfully from a guy who posted on stackoverflow) that the client ID needs to match the Thing Name in order for the iot variable in policy to work i.e. ${iot:Connection.Thing.ThingName}. Completely crazy as it may sound this hack does work. This is important as it means you don’t end up with 10k policy document for 10k devices and also if you wanted to add capability or restrict something you don’t have to modify 10k documents.
So when you download the pack for your device if you follow the wizard just edit your start.sh to match the Client ID to the Thing Name. In above steps we are using 1001. You will also need to change the script to publish to dev2iot/1001 and subscribe to iot2dev/1001
In the following blogs will go through other bits such as Rules, Device Shadows, Jobs, Cert revocation, Auto Registration, OTA etc.