Provision Devices Globally with AWS IoT

Customers are building globally distributed IoT solutions with AWS IoT. A device that connects to AWS IoT must be provisioned in an AWS Region. However, when devices are shipped globally, you don’t know in which AWS IoT Region the device will connect when it starts operation. In this blog post, I will describe a global AWS IoT device provisioning process.

Provisioning an IoT device

A device must be provisioned to work with AWS IoT Core. To provision a device:

  • The device should be created as a thing in the registry.
  • Its X.509 certificate must be registered and associated with the thing.
  • An IoT policy must be attached to the certificate.

To connect to AWS IoT, the device must know the AWS IoT endpoint.

Global device provisioning

To automate global device provisioning, I use an architecture built on Amazon API Gateway, AWS Lambda, AWS IoT Core, and Amazon DynamoDB. The device sends a provisioning request to an Amazon API Gateway endpoint. Amazon API Gateway calls an AWS Lambda function, which implements a method to find the best region for the device and then provisions the device in that region.

You’ll find this sample implementation, documentation, and an AWS CloudFormation template at GitHub. The AWS CloudFormation template is provided to create the AWS resources for the sample implementation. For information about setting up the sample implementation and provisioning a device, see https://github.com/aws-samples/aws-iot-global-device-provisioning.

In this example, the best region is the one geographically closest to the device. To determine the closest region, you must determine the device’s IP address and geolocation so the distance to the closest AWS Region can be calculated. If the geolocation cannot be retrieved, a default AWS Region is used.

In my implementation, I use ipstack.com to look up the geolocation for the device’s IP address. To use the ipstack.com API, you need an API access key. Follow the sign-up steps at https://ipstack.com. The AWS Lambda function that determines the best region gets the API access key from an environment variable.

Upon successful provisioning, the following are returned to the device:

  • A private key (if the device does not already have a private key).
  • A X.509 certificate.
  • The AWS IoT endpoint.

To secure the provisioning process, the device must sign the thing name and send the signature in the provisioning request to Amazon API Gateway. This approach is similar to the one used for custom authorizers in AWS IoT. Every device must be fitted with the private key for provisioning and the URL for the API gateway.

After the signature is verified, a lookup in a DynamoDB table is performed. An “unprovisioned” status means that the device can be provisioned and the provisioning process will start.

Architecture

Provisioning Process

The GitHub repository includes a program that is able to interact with the provisioning solution on the device. In addition to installing the program onto your device, you also need a private key and the URL for your API gateway.

Here is the workflow for provisioning a device:

  1. The device signs its thing name with a private key and sends a provisioning request to Amazon API Gateway. The device uses its own private key or uses a private key issued by AWS IoT Core. If the device uses its own private key, it provides a code-signing request (CSR) in the provisioning request. If “CSR” is not present in the request, AWS IoT creates the private key.

    Sample provisioning request:

    {'thing-name': 'mydevice3', 'thing-name-sig': 'R725rxa+vnrMkvsydqS/lbZDDPzBTBXlKI5teO4OX1pKE9jRn/cUailOJczie2zMXUFUtO83sPr+HtRkjJQHDBrA2HDH87G21nMQdJT8K4RGHP6KRfpOhBYT7e162TnKc8DdUBh+Yh4T78dMePuaW4/PPkEbaRf6O7ieBZMITYmeETRDkkDwRD/jAcuEthmBSxRFw1YOzphw36atqS3+J0chc6lnAgCwbZhfPDI98HkLzgVaaXJlJ12ryXtyPA3D1Ptf+mUIci+DbharLsRCiaGsLrnCnoaL4y+vnD2LO0SwS05xhQtFI+0khq3pvGBMtw4HC/+AExI3I1jV3f9EBA=='}

  2. Amazon API Gateway calls the AWS Lambda function
  3. The AWS Lambda function performs the following steps:
    1. Verifies the signature for the thing name. If the verification fails, the Lambda function sends an error message and then terminates.
    2. Determines if the device is a provisioning candidate by performing a lookup for the thing name in a DynamoDB table. The Lambda function checks if the prov_status attribute for the device (thing_name) is set to unprovisioned. If so, the device will be provisioned. If not, an error message will be returned. There are more sophisticated and secure ways to verify if a device should be provisioned (a token, for example). Similar to the approach for signing the thing name, tokens can also be signed by a key.

      Sample entry in DynamoDB:

      { "thing_name": { "S": "mydevice3" }, "prov_status": { "S": "unprovisioned" } }

    3. Calculates the best AWS Region for IoT. The calculation finds the shortest distance between the location of the device’s IP address and an AWS Region.
    4. Provisions the device. That means the device will be created in the device registry. A certificate and, optionally, a key will be created. If one does not already exist, an IoT policy will be created, too.
    5. Updates the DynamoDB table with the time and the region in which the device has been provisioned.
    6. Returns the answer, in JSON format. The answer contains the IoT endpoint, certificate, and private key, if no CSR was sent in the provisioning request.
  4. After receiving the response, the device stores the key, certificate and endpoint. The device is now ready to immediately interact with the AWS IoT Core.

Conclusion

AWS IoT Device Management offers several device provisioning options to make it easy to onboard devices. When you use other AWS services you can implement solutions to register your devices automatically with AWS IoT Core in the AWS Region of your choice automatically.

Leave your feedback in the comments. If you have questions or issues implementing this solution, open a thread on the AWS IoT forum.