Skip to content
This repository has been archived by the owner on Sep 17, 2024. It is now read-only.

Commit

Permalink
Merge branch 'cross-account' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Elliott Spira committed Oct 6, 2015
2 parents ef5da90 + da0326e commit e6264eb
Show file tree
Hide file tree
Showing 17 changed files with 301 additions and 83 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
lib/
*.bak
*.sh
sample*.js
!test_create_instances.sh
121 changes: 70 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,56 @@
# Autotag

This is an open-source tagging solution for AWS. Deploy autotag to lambda and set up CloudTrail and have each of your resources tagged with the resource who created it. It was written by GorillaStack.
# Auto Tag

This is an open-source tagging solution for AWS. Deploy auto tag to lambda and set up CloudTrail and have each of your resources tagged with the resource who created it. It was written by [GorillaStack](http://www.gorillastack.com/).

[Read a blog post about the project](http://blog.gorillastack.com/gorillastack-presents-auto-tag).

## Setup

#### Baseline policies for your lambda IAM role
### 1. Turn on CloudTrail for your region

If you install your lambda function and don't plan on tagging resources, at very least you will need these permissions:
1. Turn on CloudTrail.
2. Create a new Amazon S3 bucket for storing your log files, or specify an existing bucket where you want the log files delivered.
3. (Optional and NOT REQUIRED for auto tag) Create a new Amazon SNS topic in order to receive notifications when new log files are delivered.

1. Permissions to save logs for your lambda execution.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
```
More [documentation on creating a Trail](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-create-and-update-a-trail.html)

### 2. Create a lambda function

1. Within lambda, press the 'Create a Lambda Function' button
2. Press the 'Skip' button to bypass the suggested blueprints
3. Enter the lambda function name (e.g. 'autotag')
4. Select 'Node.js' as the Runtime
5. Upload the [latest release's zip file](https://github.com/GorillaStack/auto-tag/releases)
6. Under 'Handler' add 'autotag.handler'
7. Under 'Advanced settings' set the Timeout to 30s

More [documentation on Lambda](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html)

2. Permissions to retrieve zipped CloudTrail log items from S3.
### 3. Configure the access policy for your lambda role

Your lambda function will run as an IAM role. This is where we configure the permissions required.

#### Lambda function master policy
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Sid": "Stmt1442379848000",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
"s3:ListBucket",
"sts:*"
],
"Resource": [
"*"
Expand All @@ -48,43 +60,28 @@ If you install your lambda function and don't plan on tagging resources, at very
}
```

This contains permissions for:

#### Necessary policies for your lambda's IAM role
Actions to allow for all resources:
1. Saving logs for your lambda execution.
2. Retrieving zipped CloudTrail log items from S3.
3. Assuming the auto-tag role on targeted accounts.

* S3: `s3:GetBucketTagging`
`s3:PutBucketTagging`
* EC2: `ec2:CreateTags`
* ELB: `elasticloadbalancing:AddTags`
* AutoScaling: `autoscaling:CreateOrUpdateTags`
* EBS: `ec2:CreateTags` (Same as EC2)
* VPC: `ec2:CreateTags` (Same as EC2)
* Subnet: `ec2:CreateTags` (Same as EC2)
* InternetGateway: `ec2:CreateTags` (Same as EC2)
* RDS: `rds:AddTagsToResource`
* EMR: `elasticmapreduce:AddTags`
* DataPipeline: `datapipeline:AddTags`
### 4. Configure the access policy for your Auto-Tag role

## Whole master policy
When auto-tag finds an event that indicated the creation of a resource, it needs permissions to tag that resource. On each account, a role for cross account access will need to be created, with the following permissions.

*NOTE;* The role must be named 'AutoTagRole'.
(This is a temporary hard requirement, given that after uploading the code via zip file, AWS doesn't allow the user to edit the main file inline. This introduces complexity in user defined configuration for the lambda function.)

#### Auto-Tag role master policy
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Sid": "Stmt1442379848000",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:GetBucketTagging",
"s3:PutBucketTagging",
"ec2:CreateTags",
Expand All @@ -102,6 +99,22 @@ Actions to allow for all resources:
}
```

Details on specific requirements for each service, in case you want a subset of this master policy:

* S3: `s3:GetBucketTagging`
`s3:PutBucketTagging`
* EC2: `ec2:CreateTags`
* ELB: `elasticloadbalancing:AddTags`
* AutoScaling: `autoscaling:CreateOrUpdateTags`
* EBS: `ec2:CreateTags` (Same as EC2)
* VPC: `ec2:CreateTags` (Same as EC2)
* Subnet: `ec2:CreateTags` (Same as EC2)
* InternetGateway: `ec2:CreateTags` (Same as EC2)
* RDS: `rds:AddTagsToResource`
* EMR: `elasticmapreduce:AddTags`
* DataPipeline: `datapipeline:AddTags`


## Contributing

If you have questions, feature requests or bugs to report, please do so on the github repository.
Expand All @@ -110,7 +123,7 @@ If you are interested in contributing, please get started by forking our github

### Development guide

Autotag is implemented in Javascript (ECMAScript 2015 - a.k.a. es6). To make this compatible with lambda and other es5 environments, we use [babel](https://babeljs.io/) to transpile the es6 code to es5. For this reason, you will need to install babel globally to get started:
Auto tag is implemented in Javascript (ECMAScript 2015 - a.k.a. es6). To make this compatible with lambda and other es5 environments, we use [babel](https://babeljs.io/) to transpile the es6 code to es5. For this reason, you will need to install babel globally to get started:

```bash
$ npm install -g babel
Expand All @@ -127,3 +140,9 @@ To assist you in packaging and deploying your code to your lambda function, I ha
```bash
$ bash deploy_lambda_code.sh
```

To assist with running during development without having to deploy to lambda, use the `main.js` and `sample_data.js` files we have provided.

```bash
$ node main.js
```
3 changes: 2 additions & 1 deletion src/autotag.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const AwsCloudTrailListener = require('./aws_cloud_trail_listener');
const exports = {};

exports.handler = function(cloudtrailEvent, context) {
let enabledListeners = [
const enabledListeners = [
AwsCloudTrailListener.EC2.name,
AwsCloudTrailListener.S3.name,
AwsCloudTrailListener.AUTOSCALE_GROUPS.name,
Expand Down
24 changes: 12 additions & 12 deletions src/autotag_factory.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
const _ = require('underscore');
const AutotagDefaultWorker = require('./autotag_default_worker.js');
const AutotagEC2Worker = require('./autotag_ec2_worker.js');
const AutotagS3Worker = require('./autotag_s3_worker.js');
const AutotagELBWorker = require('./autotag_elb_worker.js');
const AutotagEBSWorker = require('./autotag_ebs_worker.js');
const AutotagAutoscaleWorker = require('./autotag_autoscale_worker.js');
const AutotagVPCWorker = require('./autotag_vpc_worker.js');
const AutotagSubnetWorker = require('./autotag_subnet_worker.js');
const AutotagInternetGatewayWorker = require('./autotag_internet_gateway_worker.js');
const AutotagRDSWorker = require('./autotag_rds_worker.js');
const AutotagEMRWorker = require('./autotag_emr_worker.js');
const AutotagDataPipelineWorker = require('./autotag_data_pipeline_worker.js');
const AutotagDefaultWorker = require('./workers/autotag_default_worker.js');
const AutotagEC2Worker = require('./workers/autotag_ec2_worker.js');
const AutotagS3Worker = require('./workers/autotag_s3_worker.js');
const AutotagELBWorker = require('./workers/autotag_elb_worker.js');
const AutotagEBSWorker = require('./workers/autotag_ebs_worker.js');
const AutotagAutoscaleWorker = require('./workers/autotag_autoscale_worker.js');
const AutotagVPCWorker = require('./workers/autotag_vpc_worker.js');
const AutotagSubnetWorker = require('./workers/autotag_subnet_worker.js');
const AutotagInternetGatewayWorker = require('./workers/autotag_internet_gateway_worker.js');
const AutotagRDSWorker = require('./workers/autotag_rds_worker.js');
const AutotagEMRWorker = require('./workers/autotag_emr_worker.js');
const AutotagDataPipelineWorker = require('./workers/autotag_data_pipeline_worker.js');
const CONFIG = require('./cloud_trail_event_config');

let AutotagFactory = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const AutotagDefaultWorker = require('./autotag_default_worker');
const AWS = require('aws-sdk');
const co = require('co');

class AutotagAutoscaleWorker extends AutotagDefaultWorker {
constructor(event) {
super(event);
this.autoscaling = new AWS.AutoScaling({region: event.awsRegion});
}

/* tagResource
Expand All @@ -14,6 +14,18 @@ class AutotagAutoscaleWorker extends AutotagDefaultWorker {
*/

tagResource() {
let _this = this;
return co(function* () {
let credentials = yield _this.assumeRole();
_this.autoscaling = new AWS.AutoScaling({
region: _this.event.awsRegion,
credentials: credentials
});
yield _this.tagAutoscalingGroup();
});
}

tagAutoscalingGroup() {
let _this = this;
return new Promise(function(resolve, reject) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const AutotagDefaultWorker = require('./autotag_default_worker');
const AWS = require('aws-sdk');
const co = require('co');
const _ = require('underscore');

class AutotagDataPipelineWorker extends AutotagDefaultWorker {
constructor(event) {
super(event);
this.dataPipeline = new AWS.DataPipeline({region: event.awsRegion});
}

/* tagResource
Expand All @@ -15,6 +15,18 @@ class AutotagDataPipelineWorker extends AutotagDefaultWorker {
*/

tagResource() {
let _this = this;
return co(function* () {
let credentials = yield _this.assumeRole();
_this.dataPipeline = new AWS.DataPipeline({
region: _this.event.awsRegion,
credentials: credentials
});
yield _this.tagDataPipelineResource();
});
}

tagDataPipelineResource() {
let _this = this;
return new Promise(function(resolve, reject) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const AWS = require('aws-sdk');
const AUTOTAG_TAG_NAME = 'AutoTag_Creator';
const ROLE_PREFIX = 'arn:aws:iam::';
const ROLE_SUFFIX = ':role/AutoTagRole';

class AutotagDefaultWorker {
constructor(event) {
Expand All @@ -22,6 +25,34 @@ class AutotagDefaultWorker {
});
}

assumeRole() {
let _this = this;
return new Promise(function(resolve, reject) {
try {
AWS.config.region = 'us-east-1';
let sts = new AWS.STS();
sts.assumeRole({
RoleArn: ROLE_PREFIX + _this.event.recipientAccountId + ROLE_SUFFIX,
RoleSessionName: 'AutoTag-' + (new Date()).getTime(),
DurationSeconds: 900
}, function(err, data) {
if (err) {
reject(err);
} else {
let credentials = {
accessKeyId: data.Credentials.AccessKeyId,
secretAccessKey: data.Credentials.SecretAccessKey,
sessionToken: data.Credentials.SessionToken
};
resolve(credentials);
}
});
} catch (err) {
reject(err);
}
});
}

dumpEventInfo() {
console.log('Event Name: ' + this.event.eventName);
console.log('Event Type: ' + this.event.eventType);
Expand Down
11 changes: 10 additions & 1 deletion src/autotag_ebs_worker.js → src/workers/autotag_ebs_worker.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const AutotagEC2Worker = require('./autotag_ec2_worker');
const AWS = require('aws-sdk');
const co = require('co');

class AutotagEBSWorker extends AutotagEC2Worker {
/* tagResource
Expand All @@ -9,7 +10,15 @@ class AutotagEBSWorker extends AutotagEC2Worker {
*/

tagResource() {
return this.tagEC2Resources([this.getVolumeId()]);
let _this = this;
return co(function* () {
let credentials = yield _this.assumeRole();
_this.ec2 = new AWS.EC2({
region: _this.event.awsRegion,
credentials: credentials
});
yield _this.tagEC2Resources([_this.getVolumeId()]);
});
}

getVolumeId() {
Expand Down
22 changes: 15 additions & 7 deletions src/autotag_ec2_worker.js → src/workers/autotag_ec2_worker.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
const AutotagDefaultWorker = require('./autotag_default_worker');
const AWS = require('aws-sdk');
const co = require('co');

class AutotagEC2Worker extends AutotagDefaultWorker {
constructor(event) {
super(event);
this.ec2 = new AWS.EC2({region: event.awsRegion});

}

/* tagResource
** method: tagResource
**
** Do nothing
** Tag the ec2 instance
*/

tagResource() {
return this.tagEC2Resources([this.getInstanceId()]);
let _this = this;
return co(function* () {
let credentials = yield _this.assumeRole();
_this.ec2 = new AWS.EC2({
region: _this.event.awsRegion,
credentials: credentials
});
yield _this.tagEC2Resources([_this.getInstanceId()]);
});
}

tagEC2Resources(resources) {
Expand All @@ -27,10 +34,11 @@ class AutotagEC2Worker extends AutotagDefaultWorker {
_this.getAutotagPair()
]
}, function(err, res) {
if (err)
if (err) {
reject(err);
else
} else {
resolve(true);
}
});
} catch(e) {
reject(e);
Expand Down
Loading

0 comments on commit e6264eb

Please sign in to comment.