Use HashiCorp Terraform, AWS, Node, Webpack, and Vue to scaffold an example architecture for "internet of things" usage
- Note your AWS account ID (it should be a 12-digit number)
- Sign into the AWS console using the AWS account you just created
- This is your root AWS account
- Secure your root AWS account with Multi-Factor Authentication:
- Click your name in the header
- Click "Security Credentials"
- Follow the prompts to enable MFA and add an MFA device
- Create an AWS Identity and Access Management admin user for yourself on your root AWS account:
- Go to IAM
- Create a new user
- Enable "programmatic access" and "AWS Management Console" access for your new user
- Set a password for your new user
- Create an IAM group for your new user
1. Name the group (e.g.
admin
) 1. Add theAdministratorAccess
IAM policy to the group 1. Create the group, and add your user to the group - Finish creating your new IAM user
-
Copy your new IAM user's Access Key ID and Secret Access Key
-
Put your AWS Access Key ID and Secret Access Key in a file on your computer at
~/.aws/credentials
using the following format:
# this file is located at ~/.aws/credentials
[default]
aws_access_key_id = XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
For more information, see Configuring the AWS CLI
- Secure your new IAM user with Multi-Factor Authentication
- In IAM, click on the user name
- Click the "Security Credentials" tab
- Click the edit icon for "Assigned MFA device"
- Follow the instructions for adding a new MFA device
-
Sign out of your root AWS account
-
Sign back into the AWS console using your AWS account number, new IAM user, password, and MFA code
-
Install Node (version 10 or greater)
-
Install Git or GitHub Desktop (which installs Git by default)
-
Create a GitHub account (if you don't have one already)
-
Install the AWS Command Line Interface
- The easiest way to do this on macOS is with Homebrew
- First, install Homebrew on your Mac by running this command in Terminal:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- Then, install the AWS CLI by running this command:
brew install awscli
- First, install Homebrew on your Mac by running this command in Terminal:
- Clone this repo by running this command in Terminal:
git clone [email protected]:thegreatsunra/aws-terraform-iot.git
-
Make a copy of
terraform/config.tf.example
and name itconfig.tf
-
Edit the values in the first section of
terraform/config.tf
to match your own AWS Account ID, target AWS region, and your "company name"
- "Company name" is used in Cognito email templates for user account confirmations and messages
- If your computer has multiple AWS profiles configured in
~/.aws/credentials
, change thedefault
value foraws_profile
to the name of the profile you want to use - Change the value for
s3_web_app
to the domain (and subdomain, if applicable) you want to use for hosting your web app- Note that this value must be unique, and cannot be the same as any other S3 bucket in the whole world
- Change the value for
s3_cloudtrail
to a random (but recognizable by you) value that is unique, and will not be the same as any other S3 bucket in the whole world
- Don't touch the values for
authorizer_id
,authorization
, orcognito_app_client_id
yet; we'll change those after we finish our first run of theterraform apply
script
-
Save your changes to
terraform/config.tf
-
Open a Terminal window and navigate to your project's
terraform
folder -
At the command line, run this command to initialize your Terraform scripts and install all dependencies:
terraform init
- Run this command to "plan" the execution of your Terraform scripts, and calculate how they will affect your AWS environment when applied:
terraform plan
- Run this command to "apply" your Terraform scripts to your AWS environment, and reply
yes
when prompted:
terraform apply
Your architecture should now be deployed on your AWS account!
Now we have some manual work to do.
-
Copy the Authorizer ID value that is outputted by
terraform apply
and replace the placeholderauthorizer_id
value interraform/config.tf
with it -
In
terraform/config.tf
change theauthorization
value fromNONE
toCOGNITO_USER_POOLS
-
Log in to the AWS Management Console using your IAM admin user, and make sure you are in the AWS region (e.g.
us-east-1
) where Terraform deployed your architecture -
In the AWS Management Console, navigate to Cognito
-
Click to manage your User Pools
-
Click the User Pool created by Terraform
-
Under General Settings, click App Clients
-
Add a new App Client
-
Name your App Client
-
Uncheck "Generate client secret" (if "Generate client secret" is checked, your web app hosted on S3 will not work)
-
Create your new App Client, and copy its App Client ID
-
In
terraform/config.tf
change the placeholder value forcognito_app_client_id
to your new App Client ID from Cognito -
At the command line, navigate to your project's
terraform
folder and runterraform plan
-
Run
terraform apply
and replyyes
when prompted
Note to self: When I originally developed this architecture you couldn't create App Clients via Terraform. Whenever you run terraform apply
now, however, Terraform deletes any App Clients that were created manually. This isn't desirable, of course, but it appears to be the reality until I isolate and fix the issue, likely by creating the App Client via Terraform rather than manually.
-
In the AWS Management Console, navigate to API Gateway
-
Choose the API Gateway created by Terraform (if you didn't rename it, it's probably called "Api")
-
Click Actions and click "Deploy API"
-
Choose "Prod" as your Deployment stage
-
Deploy your API
-
Copy the Invoke URL for your API (you'll add it to your web app later)
-
Access to your API should now be secured with Cognito
This step is necessary because there's a bug between Terraform and the AWS Management Console, where even though a Lambda function appears selected in the Triggers UI, it isn't.
-
In the AWS Management Console, navigate to Cognito
-
Open your User Pool
-
Go to Triggers, and change the Pre Sign-Up Lambda function to "None" (even if it's already set to "Cognito")
-
Scroll down and click "Save Changes"
-
Go back to Triggers, and change the Pre Sign-Up Lambda function back to Cognito
-
Scroll down and click "Save Changes"
-
When a user creates a new account, they will now "trigger" the "Cognito" Lambda function
-
Open a Terminal window, and navigate to the
gateway
folder of this repo -
Run
npm install
to install the Node dependencies for the Gateway -
Make a copy of
gateway/config.example.js
and name itconfig.js
-
Return to the AWS Management Console
-
Navigate to IoT Core
-
Click Manage and then click Register a Thing
-
Click "Create a Single Thing"
-
Give your Thing a name and click Next
-
Click Create Certificate
-
Download the certificate, public key, private key, and the "Root CA for AWS IoT" (four files in total) Note to self: the Root CA flow has changed, and now takes you to documentation that does a poor job explaining which of the four Root CA options you may want to download. You could probably do worse than use Amazon Root CA 1
-
Click Activate to activate your certificate (if you miss this step, it won't work!)
-
From your Thing's details screen, click Attach a Policy
-
Check the box next to the Policy that was created by Terraform (e.g. "thing")
-
Click Register Thing
-
Return to the AWS Management Console's AWS IoT home screen
-
Click Act
-
Click Create a Rule
-
Name your Rule (e.g. events)
-
For the Rule Query Statement, enter
SELECT * FROM 'events'
seems to work) -
Click Add Action
-
Choose "Send a message to a Lambda function"
-
Click Configure Action
-
Choose your AWS IoT Lambda Function from the dropdown (i.e.
events
unless you changed it in Terraform) -
Click Add Action
-
Review your Rule and click "Create Rule"
-
On your computer, rename the certificate files you downloaded from AWS IoT and put them in your local Gateway app's
gateway/cert
folder. Refer to thegateway/cert/*.example
files for the proper naming convention -
Open
gateway/config.js
in your favorite text editor -
Replace the
xxxxxxxx
values with your actual values from the AWS Management Console
- You can find your AWS IoT host URI on the "Settings" cog on AWS IoT portal main screen
-
Save your changes to
gateway/config.js
-
At the command line, run
npm start
to start your Gateway
-
Open a Terminal window, and navigate to the
app
folder of this repo -
Run
npm install
to install the Node dependencies for the app -
Make a copy of
app/.env.example
and name it.env
(.env.example
is a dotfile so it might be hidden by default on your computer) -
Open
app/.env
in a text editor -
Replace the
xxxxxxxxxxxx
values with your actual values from AWS
- If you "edit" your Cognito Identity Pool, you can copy its ID from the AWS Management Console
-
Save your changes to
app/.env
-
At the command line, run
npm run serve
to start the app -
The local development server for the app should start and automatically launch the app at http://localhost:9000 in your default web browser
-
Create a new user account and password in your app's web UI (or log in with your existing Cognito credentials if you already have them)
- By default, the Cognito Pre Sign-Up Trigger Lambda function requires that all users creating a new account use an email address with
@gmail.com
as the domain
-
Open your email, copy the verification code you received from Cognito, and submit it in your app's web UI
-
Finally, log into your app with the username and password you provided
-
The app should show incoming data from your Gateway
- If the app doesn't work, make sure your
.env
variables are set correctly - If you do change your
.env
variables, stop and start the dev server to make sure your changes are applied
Note: The npm run deploy
script requires you have the AWS CLI installed and the proper credentials configured on your computer to access your AWS S3 bucket.
-
From your web app's root folder, run
npm run build
to generate a distribution version of your app -
Edit
app/package.json
and change the URL values in thedeploy
script to match the location of your own AWS S3 bucket you created with Terraform -
Save your changes to
app/package.json
-
At the command line, navigate to the
app
folder of this repo and runnpm deploy
to use the AWS CLI to upload your app to S3 -
Test your app at your S3 URL and make sure it works