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

Commit

Permalink
Adding cross-account cloudtrail support for auto-tag
Browse files Browse the repository at this point in the history
  • Loading branch information
Elliott Spira committed Oct 6, 2015
2 parents 6721529 + e6264eb commit ab42cf4
Show file tree
Hide file tree
Showing 17 changed files with 292 additions and 82 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
111 changes: 61 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

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/).

## Setup

### 1. Turn on CloudTrail for your region
[Read a blog post about the project](http://blog.gorillastack.com/gorillastack-presents-auto-tag).

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.
Expand All @@ -27,39 +25,51 @@ More [documentation on Lambda](https://docs.aws.amazon.com/lambda/latest/dg/gett

For the complete role's policy, scroll down for the master policy. Read on for finer details on the access permissions required below.

#### 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)

### 3. Configure the access policy for your lambda role

2. Permissions to retrieve zipped CloudTrail log items from S3.
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 @@ -69,43 +79,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

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.

## Whole master policy
*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 @@ -123,6 +118,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 Down
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 ab42cf4

Please sign in to comment.