Douglas Yoon
Created December 1, 2016 © MIT

Otto AWS monitoring surface

Giving the enterprise operator a voice. Otto provides voice commands to get the status of their AWS environment.

IntermediateFull instructions provided20 hours70
Otto AWS monitoring surface

Things used in this project

Hardware components

Echo Dot
Amazon Alexa Echo Dot
×1

Software apps and online services

AWS Lambda
Amazon Web Services AWS Lambda
Accessing a DynamoDB table and VPC subnet

Story

Read more

Schematics

Otto VUI and Network Diagram

PPT document with VUI and Network Diagram.

Code

Otto Lambda Function

JavaScript
The lambda function which gets the linked AWS account information and describes the EC2 instances running in a subnet
/**
    Copyright 2016 Douglas Yoon
*/

/**
 * Otto - an entprise monitoring surface
 * This Alexa demo is tied to the Relus IoT button demo
 *
 * Example Phrases:
 *  User: "Alexa, ask Monitor Board the current status"
 *  Alexa: "Hi Matt! Happy to hear from you. You can ask me how many instances are running"
 */

'use strict';

const AWS = require('aws-sdk');
AWS.config.update({region:"us-east-1"});
var dynamodb = new AWS.DynamoDB();
var ec2={};
var demoEnv={};
var storeParams={};


/**
 * ***************************************************************************************************************************************
 */

/**
 * App ID for the skill
 */
var APP_ID = 'amzn1.ask.skill.a5b23691-6bff-45d0-a1e1-c6eb33749dc5'; 

/**
 * The AlexaSkill prototype and helper functions
 */
var AlexaSkill = require('./AlexaSkill');

/**
 * Otto is a child of AlexaSkill.
 */
var Otto = function () {
    AlexaSkill.call(this, APP_ID);
};

// Extend AlexaSkill
Otto.prototype = Object.create(AlexaSkill.prototype);
Otto.prototype.constructor = Otto;

// from Amazon Template...
Otto.prototype.eventHandlers.onSessionStarted = function (sessionStartedRequest, session) {
    console.log("Otto onSessionStarted requestId: " + sessionStartedRequest.requestId
        + ", sessionId: " + session.sessionId);
    // any initialization logic goes here
};

// from Amazon Template...
Otto.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
    console.log("Otto onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);
    var speechOutput = "Welcome to the Alexa Skills Kit, you can say hello";
    var repromptText = "You can say hello";
    response.ask(speechOutput, repromptText);
};

// from Amazon Template...
Otto.prototype.eventHandlers.onSessionEnded = function (sessionEndedRequest, session) {
    console.log("Otto onSessionEnded requestId: " + sessionEndedRequest.requestId
        + ", sessionId: " + session.sessionId);
    // any cleanup logic goes here
};

Otto.prototype.intentHandlers = {
    // register custom intent handlers
    "Status": function (intent, session, response) {
				let theTextResponse = "The Relus demo currently has "+demoEnv.allCount+" instances. "+demoEnv.runCount+" are running and "+demoEnv.stopCount+" are stopped.";
				if (demoEnv.allCount == 0) {
					theTextResponse = "The Relus demo currently has nothing running";
				}
        response.tellWithCard(theTextResponse, "Demo Return", theTextResponse+" card");
    },
    "AMAZON.HelpIntent": function (intent, session, response) {
        response.ask("You can say hello to me!", "You can say hello to me!");
    }
};

// Grab the ec2 list from the subnet
function getEC2List() {
  console.log("running again "+new Date().toLocaleString());
  ec2.describeInstances(
    {   // TODO: This is hard coded!!! 
				// A JSON structure needs to be designed to allow reporting of 
				// different AWS Components
      Filters: [{"Name": "subnet-id", Values: ["subnet-de997cb9"]}],
    }, 
    function(err, data) {
      if (err) {  // TODO: better error trapping is needed here
        console.log(err, err.stack); // an error occurred
      } else {
        console.log(data);           // successful response
				demoEnv.allCount = 0;
				demoEnv.runCount = 0;
				demoEnv.stopCount = 0;

				// parse the status of each ec2 instance reported back
				for (let i = 0; i < data.Reservations.length; i++) {
					for (let j = 0; j < data.Reservations[i].Instances.length; j++) {
						demoEnv.allCount++;

						if (data.Reservations[i].Instances[j].State.Name == "pending"){
							demoEnv.runCount++;
						}
						if (data.Reservations[i].Instances[j].State.Name == "running"){
							demoEnv.runCount++;
						}
						if (data.Reservations[i].Instances[j].State.Name == "stopping"){
							demoEnv.stopCount++;
						}
						if (data.Reservations[i].Instances[j].State.Name == "stopped"){
							demoEnv.stopCount++;
						}
					}
				}
			}

			// Create an instance of the Otto skill.
			var ottoDemo = new Otto();
			ottoDemo.execute(storeParams.event, storeParams.context);
		});
}

// Grab the AWS credentials from DynamoDB
function setAccessCreds(err, data) {
	if (err) {
		console.log(err, err.stack); // an error occurred
	} else {
		console.log(data);           // successful response
		AWS.config.update({ "region":"us-west-2", "accessKeyId": data.Item.accessKeyId.S, "secretAccessKey": data.Item.secretAccessKey.S});
		ec2 = new AWS.EC2({apiVersion: '2016-09-15'});
		getEC2List();
	}
}

// The handler that responds to the Alexa Request.
exports.handler = function (event, context) {
	storeParams={event,context};

	// We are doing this here, instead of inside the Alexa Initialization Method
	// for fear of timing out...
	
	// Get the AWS keys linked to this Alexa Account
	var params = {
		"TableName" : "OttoUsers",
		"Key": { "AlexaUserID" : { "S" : event.session.user.userId } }
	};
	dynamodb.getItem(params, setAccessCreds);
};
		 

Credits

Douglas Yoon

Douglas Yoon

1 project • 1 follower

Comments