Setting up AWS IoT using Serverless Framework for Multiple IoT Devices

I have dealt with this topic many times and had to realize that it depends a lot on the use case, which makes more sense. Also security is an aspect to keep an eye on. You don't want to have a public API responsible for JIT device registration accessible by the whole Internet.

Setting up AWS IoT using Serverless Framework for Multiple IoT Devices

I have dealt with this topic many times and had to realize that it depends a lot on the use case, which makes more sense. Also security is an aspect to keep an eye on. You don't want to have a public API responsible for JIT device registration accessible by the whole Internet.

A simple Programmatic Provisioning-based scenario could look like this: You build a thing (maybe a sensor), which should be abled to connect to AWS IoT and have an in-house provisioning process.

Simple provisioning process:

  1. Thing built
  2. Thing has a serial number
  3. Thing registers itself via an internal server

The registration code running on the server could look something like this (JS + AWS JS SDK):

// Modules
const AWS = require('aws-sdk')

// AWS
const iot = new AWS.Iot({ region: process.env.region })

// Config
const templateBodyJson = require('./register-thing-template-body.json')

// registerThing
const registerThing = async ({ serialNumber = null } = {}) => {
  if (!serialNumber) throw new Error('`serialNumber` required!')

  const {
    certificateArn = null,
    certificateId = null,
    certificatePem = null,
    keyPair: {
      PrivateKey: privateKey = null,
      PublicKey: publicKey = null
    } = {}
  } = await iot.createKeysAndCertificate({ setAsActive: true }).promise()
  const registerThingParams = {
    templateBody: JSON.stringify(templateBodyJson),
    parameters: {
      ThingName: serialNumber,
      SerialNumber: serialNumber,
      CertificateId: certificateId
    }
  }
  const { resourceArns = null } = await iot.registerThing(registerThingParams).promise()

  return {
    certificateArn,
    certificateId,
    certificatePem,
    privateKey,
    publicKey,
    resourceArns
  }
}

const unregisterThing = async ({ serialNumber = null } = {}) => {
  if (!serialNumber) throw new Error('`serialNumber` required!')

  try {
    const thingName = serialNumber
    const { principals: thingPrincipals } = await iot.listThingPrincipals({ thingName }).promise()
    const certificates = thingPrincipals.map((tp) => ({ certificateId: tp.split('/').pop(), certificateArn: tp }))

    for (const { certificateId, certificateArn } of certificates) {
      await iot.detachThingPrincipal({ thingName, principal: certificateArn }).promise()
      await iot.updateCertificate({ certificateId, newStatus: 'INACTIVE' }).promise()
      await iot.deleteCertificate({ certificateId, forceDelete: true }).promise()
    }

    await iot.deleteThing({ thingName }).promise()

    return {
      deleted: true,
      thingPrincipals
    }
  } catch (err) {
    // Already deleted!
    if (err.code && err.code === 'ResourceNotFoundException') {
      return {
        deleted: true,
        thingPrincipals: []
      }
    }

    throw err
  }
}

register-thing-template-body.json:

{
  "Parameters": {
     "ThingName": {
       "Type": "String"
     },
     "SerialNumber": {
       "Type": "String"
     },
     "CertificateId": {
       "Type": "String"
     }
  },
  "Resources": {
    "thing": {
      "Type": "AWS::IoT::Thing",
      "Properties": {
        "ThingName": {
          "Ref": "ThingName"
        },
        "AttributePayload": {
          "serialNumber": {
            "Ref": "SerialNumber"
          }
        },
        "ThingTypeName": "NewDevice",
        "ThingGroups": ["NewDevices"]
      }
    },
    "certificate": {
      "Type": "AWS::IoT::Certificate",
      "Properties": {
        "CertificateId": {
          "Ref": "CertificateId"
        }
      }
    },
    "policy": {
      "Type": "AWS::IoT::Policy",
      "Properties": {
        "PolicyName": "DefaultNewDevicePolicy"
      }
    }
  }
}

Make sure you got all the "NewDevice" Thing types, groups and policies in place. Also keep in mind ThingName = SerialNumber (important for unregisterThing).