📣 Limited offer: subscribe to cloudonaut plus, get a t-shirt for free

📣 Limited offer: free cloudonaut t-shirt

Cronjob at the edge with AWS IoT

Michael Wittig – 29 May 2019

I’m working on a project where I have to manage a cronjob that runs on a small computer with an unreliable Internet connection. I want to configure the cronjob schedule expression (e.g., 0 10 * * *) remotely but the cronjob should not be interrupted if the Internet connection fails. If an Internet connection is available, I would also like to report if the cronjob execution was successful.

Cronjob at the edge

An excellent fit to manage the state of a device with an unreliable Internet connection is the Device Shadow Service for AWS IoT. A device shadow has a desired and reported state. Let’s assume the following state.

{
"desired": {
"cronexpression": "0 10 * * *"
},
"reported": {
}
}

As you can see, the desired state contains the cronexpression attribute which is not present on the reported state. The device has not yet reported anything to the device shadow.

If the device comes online, it receives the new desired state, applies the changes, and reports the new state on the device to AWS IoT. The state will look like this:

{
"desired": {
"cronexpression": "0 10 * * *"
},
"reported": {
"cronexpression": "0 10 * * *"
}
}

The device is now in sync with the device shadow. You can change the desired state of the device shadow at any time by making a call to AWS IoT.

{
"desired": {
"cronexpression": "0 11 * * *"
},
"reported": {
"cronexpression": "0 10 * * *"
}
}

Again, if the device is online it will receive the new desrired state and apply changes.

Implementation

First, you need to create an IoT Thing.

Special offer: cloudonaut t-shirt

Do you love our blog posts and podcast episodes? Unlock our weekly videos and online events by subscribing to cloudonaut plus.

Special offer: Join cloudonaut plus before November 30th, and we will send you a cloudonaut t-shirt for free.

Subscribe now!
  1. Visit the AWS IoT Core Management Console
  2. Click on Secure > Policies on the left
  3. Click the Create button
  4. Set Name to cloudonaut
  5. Click on Advanced mode
  6. Enter the following policy, replace REGION with your AWS region and ACCOUNT_ID with your AWS account id, and click the Create button

    {
    "Version": "2012-10-17",
    "Statement": [
    {
    "Effect": "Allow",
    "Action": "iot:Subscribe",
    "Resource": "arn:aws:iot:REGION:ACCOUNT_ID:topicfilter/$aws/things/cloudonaut/shadow/*"
    },
    {
    "Effect": "Allow",
    "Action": [
    "iot:Publish",
    "iot:Receive"
    ],
    "Resource": "arn:aws:iot:REGION1:ACCOUNT_ID:topic/$aws/things/cloudonaut/shadow/*"
    },
    {
    "Effect": "Allow",
    "Action": "iot:Connect",
    "Resource": "arn:aws:iot:REGION:ACCOUNT_ID:client/cloudonaut-*"
    },
    {
    "Effect": "Allow",
    "Action": [
    "iot:GetThingShadow",
    "iot:UpdateThingShadow"
    ],
    "Resource": "arn:aws:iot:REGION:ACCOUNT_ID:thing/cloudonaut"
    }
    ]
    }
  7. Click on Manage > Things on the left

  8. Click the Create button
  9. Click the Create a single thing button
  10. Set the Name to cloudonaut
  11. Click the Next button
  12. Click Create certificate
  13. Download the certificate, private key, public key, and root CA
  14. Click the Activate button
  15. Click the Attach a policy button
  16. Select the cloudonaut
  17. Click the Register Thing button
  18. Click on Settings on the left
  19. Take a note of the endpoint

The AWS IoT service is now configured. Let’s implement the device logic in Node.js using the aws-iot-device-sdk package. To install the package, run npm i aws-iot-device-sdk.

First, the device needs to connect to AWS IoT using MQTT.

const awsIot = require('aws-iot-device-sdk');
const thingName = 'cloudonaut';

const thingShadow = awsIot.thingShadow({
keyPath: '***-private.pem.key', // replace with downloaded file name
certPath: '***-certificate.pem.crt', // replace with downloaded file name
caPath: 'AmazonRootCA1.pem', // replace with downloaded file name
clientId: `${thingName}-node`,
host: '***.iot.***.amazonaws.com' // replace with endpoint from settings
});

To connect to its shadow the device has to call the register method using a name that identifies the IoT thing. As soon the device has registered, it asks for the latest state of its shadow.

thingShadow.register(thingName, {}, function(err) {
if (err) {
throw err;
} else {
thingShadow.get(thingName);
}
});

Finally, the device receives the initial status and delta updates of its shadow.

thingShadow.on('status',  function(thingName, stat, clientToken, stateObject) {
if ('desired' in stateObject.state) {
createOrUpdateCronjob(stateObject.state.desired.cronexpression);
}
});
thingShadow.on('delta', function(thingName, stateObject) {
if ('cronexpression' in stateObject.state) {
createOrUpdateCronjob(stateObject.state.cronexpression);
}
});

The only missing piece is the implementation of createOrUpdateCronjob that also reports the cronexpression back AWS IoT. The device also reports the last time the cronjob executed via the cronexecution attribute. If the device has no Internet connection, the cronjob still executes. To install the package, run npm i node-cron.

const cron = require('node-cron');

let cronjob;
function createOrUpdateCronjob(cronexpression) {
if (cronjob) {
cronjob.destroy();
}
cronjob = cron.schedule(cronexpression, () => {
console.log('cronjob executed');
thingShadow.update(thingName, {
state: {
reported: {
cronexecution: new Date().toISOString()
}
}
});
});
thingShadow.update(thingName, {
state: {
reported: {
cronexpression
}
}
});
}

Summary

A device shadow is an excellent choice to deploy configuration information to devices that are temporarily online. You can manage the desired configuration of your device at any time by updating the device shadow. Once the device comes online, the desired state is applied and reported back to the shadow.

Tags: aws iot
Michael Wittig

Michael Wittig

I’m an independent consultant, technical writer, and programming founder. All these activities have to do with AWS. I’m writing this blog and all other projects together with my brother Andreas.

In 2009, we joined the same company as software developers. Three years later, we were looking for a way to deploy our software—an online banking platform—in an agile way. We got excited about the possibilities in the cloud and the DevOps movement. It’s no wonder we ended up migrating the whole infrastructure of Tullius Walden Bank to AWS. This was a first in the finance industry, at least in Germany! Since 2015, we have accelerated the cloud journeys of startups, mid-sized companies, and enterprises. We have penned books like Amazon Web Services in Action and Rapid Docker on AWS, we regularly update our blog, and we are contributing to the Open Source community. Besides running a 2-headed consultancy, we are entrepreneurs building Software-as-a-Service products.

We are available for projects.

You can contact me via Email, Twitter, and LinkedIn.

Briefcase icon
Hire me