From 69fd2ae657d5f94181d5c4e7a05211af811ebabb Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Sun, 16 Aug 2015 17:23:00 -0700 Subject: [PATCH] jaws new: create stages, iam roles, persist data to awsm.json --- lib/command_new.js | 153 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 132 insertions(+), 21 deletions(-) diff --git a/lib/command_new.js b/lib/command_new.js index 4f0093e91..7a9d9be49 100644 --- a/lib/command_new.js +++ b/lib/command_new.js @@ -11,16 +11,71 @@ var Promise = require('bluebird'), fs = Promise.promisifyAll(require('fs')), os = require('os'), +async = require('async'), +AWS = require('aws-sdk'), inquirer = require('inquirer'), chalk = require('chalk'), jsonfile = Promise.promisifyAll(require('jsonfile')), shortid = require('shortid'); +// AWS IAM Role True Policy +var iamRoleTrustPolicy = JSON.stringify({ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +}); + +// AWS IAM Role Access Policy +var iamRoleAccessPolicy = JSON.stringify({ + "Version": "2012-10-17", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "cloudwatch:*", + "cognito-identity:ListIdentityPools", + "cognito-sync:GetCognitoEvents", + "cognito-sync:SetCognitoEvents", + "dynamodb:*", + "iam:ListAttachedRolePolicies", + "iam:ListRolePolicies", + "iam:ListRoles", + "iam:PassRole", + "kinesis:DescribeStream", + "kinesis:ListStreams", + "kinesis:PutRecord", + "lambda:*", + "logs:*", + "s3:*", + "sns:ListSubscriptions", + "sns:ListSubscriptionsByTopic", + "sns:ListTopics", + "sns:Subscribe", + "sns:Unsubscribe" + ], + "Resource": "*" + } + ] +}); + + module.exports = function(JAWS) { JAWS.new = function() { + var iam = new AWS.IAM(); var project = {}; // Define User Prompts @@ -32,29 +87,45 @@ module.exports = function(JAWS) { { type: 'input', name: 'name', - message: '****** WELCOME TO JAWS: Type a name for your new project (max 20 chars):', + message: '**** WELCOME TO JAWS: Type a name for your new project (max 20 chars):', default: 'jaws-new-' + shortid.generate() }, // Request AWS Admin API Key { type: 'input', name: 'awsAdminKeyId', - message: '****** JAWS: Please enter the ACCESS KEY ID for your AWS IAM User:' + message: '**** JAWS: Please enter the ACCESS KEY ID for your AWS IAM User:' }, // Request AWS Admin API Secret Key { type: 'input', name: 'awsAdminSecretKey', - message: '****** JAWS: Please enter the SECRET ACCESS KEY for your AWS IAM User:' + message: '**** JAWS: Please enter the SECRET ACCESS KEY for your AWS IAM User:' + }, + // Request Stages + { + type: 'rawlist', + name: 'stages', + message: '**** JAWS: Which stages would you like to use (you can change later)?', + choices: [ + 'dev,prod', + 'dev,staging,prod', + 'dev,staging,test,prod' + ] } ]; inquirer.prompt(prompts, function( answers ) { + // Validate + if (!answers.awsAdminKeyId) return console.log('****** JAWS Error: An AWS Access Key ID is required'); + if (!answers.awsAdminSecretKey) return console.log('****** JAWS Error: An AWS Secret Key is required'); + // Set and sanitize project info project.name = answers.name.toLowerCase().trim().replace(/[^a-zA-Z-\d\s:]/g, '').replace(/\s/g, '-').substring(0,19); project.awsAdminKeyId = answers.awsAdminKeyId.trim(); project.awsAdminSecretKey = answers.awsAdminSecretKey.trim(); + project.stages = answers.stages.split(','); return resolve(); }); @@ -63,10 +134,13 @@ module.exports = function(JAWS) { // Process userPrompts.then(function(){ - // Set project root path. Append unique id if directory already exists. + // Set project root path. Append unique id if name is in use if (fs.existsSync(JAWS._meta.cwd + '/' + project.name)) { + + // Name must be unique or lots of things will break project.name = project.name + '-' + shortid.generate(); JAWS._meta.projectRootPath = './' + project.name; + } else { JAWS._meta.projectRootPath = project.name.replace(/\s/g, '-'); } @@ -89,21 +163,6 @@ module.exports = function(JAWS) { // Create project/front return fs.mkdirAsync(JAWS._meta.projectRootPath + '/tests'); - }).then(function(){ - - // Create awsm.json - var awsmJson = { - name: project.name, - version: JAWS._meta.version, - type: 'aws_v1', - profile: 'project', - location: '', - cfTemplate: {} - }; - - jsonfile.spaces = 2; - return jsonfile.writeFile(JAWS._meta.projectRootPath + '/awsm.json', awsmJson); - }).then(function(){ // Create admin.env @@ -116,9 +175,61 @@ module.exports = function(JAWS) { }).finally(function() { - // End - console.log('******** JAWS: Your project ' + project.name + ' has been successfully created in the current directory.'); + // Configure AWS SDK + AWS.config.update({ + accessKeyId: project.awsAdminKeyId, + secretAccessKey: project.awsAdminSecretKey + }); + // Create IAM Role for each stage + async.eachSeries(project.stages, function(stage, stageCallback) { + + // Inform + console.log('****** JAWS: Creating an IAM Role for stage: ' + stage); + + var params = { + AssumeRolePolicyDocument: iamRoleTrustPolicy, + RoleName: stage + '_-_' + project.name + '_-_' + 'jaws-role' + }; + + iam.createRole(params, function(err, data) { + + if (err) return console.log(err, err.stack); + + project.stages[project.stages.indexOf(stage)] = { + stage: stage, + iamRole: data.Role.RoleName, + iamRoleArn: data.Role.Arn + }; + + // Inform + console.log('****** JAWS: IAM Role created successfully for stage: ' + stage); + + // Callback + return stageCallback(); + }); + + }, function(error) { + + // Create awsm.json + var awsmJson = { + name: project.name, + version: JAWS._meta.version, + type: 'aws_v1', + profile: 'project', + author: 'Seymore Serverless http://seymore.io', + location: '', + stages: project.stages, + awsRegions: [ 'us-east-1' ], + cfTemplate: {} + }; + jsonfile.spaces = 2; + jsonfile.writeFileSync(JAWS._meta.projectRootPath + '/awsm.json', awsmJson); + + // End + console.log('****** JAWS FINISHED: Your project "' + project.name + '" has been successfully created in the current directory.'); + + }); }); }; };