Skip to content

Instantly share code, notes, and snippets.

@epsalt epsalt/awair-request.js
Last active Mar 6, 2020

Embed
What would you like to do?
Backing up data from an Awair Sensor with AWS - Terraform config

Awair Sensor Data Backup

The Terraform file awair.tf sets up the AWS services and permissions necessary to run a nightly Lambda job to back up sensor data from the Awair API to a S3 bucket.

In order to query the Awair API you need a developer account. Register for a developer account here. Once you have sucessfully registered your account, copy your API access token from this page.

The next step is obtaining your device_id and device_type from the Awair API. The curl request below will return information about your sensor from the Awair API. Enter your own account token after Bearer in the Authorization header. Take note of your device_id and device_type and enter them and your API access token into the awair.tfvars file.

curl 'https://developer-apis.awair.is/v1/users/self/devices' \
  --header 'Authorization: Bearer YOUR-ACCOUNT-TOKEN`

Read through the awair.tf Terraform file and review the execution plan to make sure you understand and are OK with what is going on.

# Clone this gist 
$ git clone https://gist.github.com/epsalt/94a9fc09574c52d7baa532bd1c072ed3.git awair-backup-terraform
$ cd awair-backup-terraform

# Add your awair token and device info to awair.tfvars
$ nano awair.tfvars

# Archive the Lambda function
$ zip awair-request.zip awair-request.js

# Make sure everything looks good in the execution plan
$ terraform init
$ terraform plan -vars-file awair.tfvars

# Execute
$ terraform apply -vars-file awair-tfvars

Data from the awair 5-min-avg endpoint will be downloaded to an S3 bucket called awair-backup-bucket at 10:00 UTC daily. Resource names, AWS region, backup frequency, and other parameters can be altered by changing fields in awair.tf.

// Modified from the https:request lambda blueprint
const https = require('https');
const aws = require('aws-sdk');
const s3 = new aws.S3({ apiVersion: '2006-03-01' });
exports.handler = (event, context, callback) => {
let d = Date.now();
let params = {
Bucket: event.s3.bucket,
Key: `${d}.json`
};
const req = https.request(event.options, (res) => {
let body = '';
console.log('Status:', res.statusCode);
console.log('Headers:', JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
console.log('Successfully processed HTTPS response');
params.Body = body;
s3.putObject(params, function(err, data) {
if (err) {
callback(err, null);
} else {
params.ETag = JSON.parse(data.ETag);
params.Body = JSON.parse(params.Body);
callback(null, params);
}
});
});
});
req.on('error', callback);
req.end();
};
variable "awair_token" {
type = string
}
variable "awair_device_id" {
type = string
}
variable "awair_device_type" {
type = string
}
provider "aws" {
region = "us-west-2"
}
resource "aws_s3_bucket" "awair_backup_bucket" {
bucket = "awair-backup-bucket"
acl = "private"
}
resource "aws_lambda_function" "awair_request_lambda" {
function_name = "awair-request"
filename = "awair-request.zip"
handler = "awair-request.handler"
runtime = "nodejs10.x"
role = aws_iam_role.awair_request_role.arn
}
resource "aws_iam_role" "awair_request_role" {
name = "awair-request-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_policy" "awair_request_s3_policy" {
name = "awair-request-s3-policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::${aws_s3_bucket.awair_backup_bucket.bucket}*",
"arn:aws:s3:::${aws_s3_bucket.awair_backup_bucket.bucket}"
]
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "s3_policy_attach" {
role = aws_iam_role.awair_request_role.name
policy_arn = aws_iam_policy.awair_request_s3_policy.arn
}
resource "aws_iam_role_policy_attachment" "lambda_exec_policy_attach" {
role = aws_iam_role.awair_request_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_cloudwatch_event_rule" "awair_request_schedule" {
name = "awair-request-schedule"
schedule_expression = "cron(0 10 * * ? *)"
}
resource "aws_cloudwatch_event_target" "awair_request_schedule_lambda_target" {
rule = aws_cloudwatch_event_rule.awair_request_schedule.name
arn = aws_lambda_function.awair_request_lambda.arn
input = <<EOF
{
"options": {
"host": "developer-apis.awair.is",
"path": "/v1/users/self/devices/${var.awair_device_type}/${var.awair_device_id}/air-data/5-min-avg",
"method": "GET",
"headers": {
"Authorization": "Bearer ${var.awair_token}"
}
},
"s3": {
"bucket": "${aws_s3_bucket.awair_backup_bucket.bucket}"
}
}
EOF
}
resource "aws_lambda_permission" "allow_cloudwatch" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.awair_request_lambda.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.awair_request_schedule.arn
}
awair_token = "your-token-here"
awair_device_id = "your-device-id-here"
awair_device_type = "your-device-type-here"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.