DEV Community

Hai Tran
Hai Tran

Posted on

AWS IoT Demo with CDK and Amplify

Architecture

aws_devops-Expriment (2)

  • Demo

  • CDK

    • Create an IoT thing, attach a policy to X509 certificate, then attach the cert to the IoT thing
    • Create IoT rules to deliver data to Kinesis Firehose, DB
    • Create a Lambda function to query from DB
  • Amplify

    • Cognito and PubSub to subscribe to IoT topics
    • Attach an AWS IoT policy to the Cognito ID
    • Pre-built authentication UI (useAuthenticator)
  • CharJs

    • Plot and update two simple charts
    • Chart 1. Keep pulling data from DB via an API
    • Chart 2. Subscribe to an IoT topic
    • Amplify to host the react app
  • IoT Thing and Certificate

    • Attach a X509 certificate to the physical IoT device
    • Attach a AWS IoT policy to the X509 ceritifcate to allow read/write AWS IoT topics
  • AWS IoT topic rules

    • Attach a role to a rule to enable AWS IoT rules write to S3, Firehose, DyanmoDB

Check AWS IoT Service Endpoint

aws iot describe-endpoint --region ap-southeast-1
Enter fullscreen mode Exit fullscreen mode

Download AWS CA certificate

    https://www.amazontrust.com/repository/AmazonRootCA1.pem \
    https://www.amazontrust.com/repository/AmazonRootCA2.pem \
    https://www.amazontrust.com/repository/AmazonRootCA3.pem \
    https://www.amazontrust.com/repository/AmazonRootCA4.pem \
Enter fullscreen mode Exit fullscreen mode
  • An alternative way is to create a CA certificate reference 3
openssl genrsa -out root_CA_key_filename.key 2048
Enter fullscreen mode Exit fullscreen mode
openssl req -x509 -new -nodes \
    -key root_CA_key_filename.key \
    -sha256 -days 1024 \
    -out root_CA_cert_filename.pem
Enter fullscreen mode Exit fullscreen mode

Create key and certificate

  • I have to create key and certificates from CLI or AWS console. To create them from CDK, follow this custom resource reference 4
aws iot create-keys-and-certificate \
--set-as-active \
--certificate-pem-outfile esp-certificate.crt \
--public-key-outfile esp-public.key \
--private-key-outfile esp-private.key \
--region ap-southeast-1
Enter fullscreen mode Exit fullscreen mode
  • Take not the CERTIFICATE_ARN

  • Re-download the certificate given its ID
    configure aws cli output as text

aws iot describe-certificate --certificate-id 
Enter fullscreen mode Exit fullscreen mode

IoT Thing, X509 Certificate, and Policy

To allow the IoT thing can write data to AWS IoT core, we need to attach a X509 certificate the the physical IoT device. We also need to attach a policy to the X509 certificate in AWS to specify ALLOW actions.

  • Create a policy for the X509 certificate
const policy = new aws_iot.CfnPolicy(
      this,
      'PolicyForDemoDevice',
      {
        policyName: 'PolicyForDemoDevice',
        policyDocument: new aws_iam.PolicyDocument(
          {
            statements: [
              new aws_iam.PolicyStatement(
                {
                  actions: ['iot:*'],
                  resources: ['*'],
                  effect: aws_iam.Effect.ALLOW
                }
              )
            ]
          }
        )
      }
    )
Enter fullscreen mode Exit fullscreen mode
  • Attach the policy to the X509 certificate
const attachPolicy = new aws_iot.CfnPolicyPrincipalAttachment(
      this,
      'AttachPolicyForDemoDevice',
      {
        policyName: policy.policyName!.toString(),
        principal: props.certificateArn
      }
    )

    attachPolicy.addDependsOn(
      policy
    )
Enter fullscreen mode Exit fullscreen mode
  • Attach the X509 certificate to the IoT thing
const attachCert = new aws_iot.CfnThingPrincipalAttachment(
      this,
      'AttachCertificiateToThing',
      {
        thingName: thing.thingName!.toString(),
        principal: props.certificateArn
      }
    )

    attachCert.addDependsOn(
      thing
    )
Enter fullscreen mode Exit fullscreen mode

IoT Rules and Role

To allow IoT rules to delivery data to other services such as S3, DynamoDB, Firehose, we need to attach a role to each rule.

  • Create a role
 const role = new aws_iam.Role(
      this,
      'RoleForIoTCoreToAccessS3',
      {
        roleName: 'RoleForIoTCoreToAccessS3',
        assumedBy: new aws_iam.ServicePrincipal('iot.amazonaws.com')
      }
    )
Enter fullscreen mode Exit fullscreen mode
  • Attach inline policies to the role
 role.attachInlinePolicy(
      new aws_iam.Policy(
        this,
        'PolicyForIoTcoreToAccessS3',
        {
          policyName: 'PolicyForIoTcoreToAccessS3',
          statements: [
            new aws_iam.PolicyStatement(
              {
                actions: ['s3:*'],
                resources: ['arn:aws:s3:::bucketName/*']
              }
            ),
            new aws_iam.PolicyStatement(
              {
                actions: ['firehose:*'],
                resources: ['*']
              }
            ),
            new aws_iam.PolicyStatement(
              {
                actions: ['dynamodb:*'],
                resources: ['*']
              }
            )
          ]
        }
      )
    )
Enter fullscreen mode Exit fullscreen mode
  • Create rules with actions and attached the role
const topicRule = new aws_iot.CfnTopicRule(
      this,
      'TopicRuleDemo',
      {
        ruleName: 'TopicRuleDemo',
        topicRulePayload: {
          actions: [
            {
              firehose: {
                deliveryStreamName: firehoseDeilvery.deliveryStreamName,
                roleArn: role.roleArn
              }
            },
            {
              s3: {
                bucketName: 'bucketName',
                key: 'iot-one',
                roleArn: role.roleArn
              },
            },
            {
              dynamoDb: {
                hashKeyField: 'id',
                hashKeyValue: 'device01',
                hashKeyType: 'STRING',
                rangeKeyField: 'timestamp',
                rangeKeyValue: '${timestamp()}',
                rangeKeyType: 'STRING',
                roleArn: role.roleArn,
                tableName: table.tableName
              }
            }
          ],
          sql: `SELECT *, cast(timestamp() as STRING) AS timestamp FROM 'topic/subtopic'`
        }
      }
    )
Enter fullscreen mode Exit fullscreen mode

Amplify, Cognito and AWS IoT Policy

To allow Amplify (react web app) subscribe to an AWS IoT topic, we need to attach policy to Conigto ID.

  • Find the Cognito ID from the react web app as following
import Amplify, { Auth, PubSub } from 'aws-amplify';
Auth.currentCredentials().then(creds => console.log(creds));
Enter fullscreen mode Exit fullscreen mode
The Cognito ID can be found from the creds log
Enter fullscreen mode Exit fullscreen mode
  • Attach AWS IoT policy to the Cognito ID as following
aws iot attach-policy --policy-name 'PolicyForDemoDevice' --target ap-southeast-1:41f19265-5ecd-4c86-8b34-cc58dee6c2f0
aws iot attach-policy --policy-name 'PolicyForDemoDevice' --target ap-southeast-1:2d1afbd5-2d7d-43ec-b906-a40ac2416c10
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
mmuller88 profile image
Martin Muller 🇩🇪🇧🇷🇵🇹

Wow very cool post. Not much post out there with such an amazing combination CDK + IOT <3

Collapse
 
entest profile image
Hai Tran

Thank you!!

Collapse
 
tsamaya profile image
Arnaud Ferrand

Great post. I love the links for reference.

The GitHub repo is no longer available, though. This is sad since it would help glue every element together. Can you put it back/make public?

Collapse
 
nikolamilovic profile image
Nikola Milovic

Why do we attach the IoT policy via CLI and not programatically? I would be nice to see that part as well