aws_auditor 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +23 -5
- data/aws_auditor.gemspec +2 -0
- data/lib/aws_auditor/cache_instance.rb +38 -0
- data/lib/aws_auditor/commands/audit.rb +8 -1
- data/lib/aws_auditor/commands/export.rb +9 -0
- data/lib/aws_auditor/commands/inspect.rb +12 -0
- data/lib/aws_auditor/convenience_wrappers.rb +26 -1
- data/lib/aws_auditor/{instance.rb → ec2_instance.rb} +19 -19
- data/lib/aws_auditor/google.rb +40 -0
- data/lib/aws_auditor/google_sheet.rb +80 -0
- data/lib/aws_auditor/instance_helper.rb +28 -0
- data/lib/aws_auditor/rds_instance.rb +51 -0
- data/lib/aws_auditor/scripts/audit.rb +70 -19
- data/lib/aws_auditor/scripts/export.rb +98 -0
- data/lib/aws_auditor/scripts/inspect.rb +48 -0
- data/lib/aws_auditor/stack.rb +19 -6
- data/lib/aws_auditor/version.rb +1 -1
- data/lib/aws_auditor.rb +4 -1
- metadata +40 -5
- data/lib/aws_auditor/commands/stack-audit.rb +0 -8
- data/lib/aws_auditor/scripts/stack-audit.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3aaac3737b9cf61e89d42dc0bb7d553a60190e79
|
4
|
+
data.tar.gz: e1b1852944cdf66ea8f4766ddcb297ad001e5ebe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edb9b18f94fd362a32eefa44577cdb050985338bbcf93bed4c281103bb60578a42ebce033bbabf08187966a06971e72ab84b3b94bf1f87d77d1c338959d10711
|
7
|
+
data.tar.gz: 1d51d364acc3d5152a1da77e4e1d9ecfa5041ba0843d99a8ac437a814164143f165e9bbaddb186d98a5034f08da714728b34759b6e0d2f8444b67c0fe2f174b2
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -18,27 +18,45 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
$ gem install aws_auditor
|
20
20
|
|
21
|
-
##
|
21
|
+
## How-to
|
22
22
|
|
23
|
-
|
23
|
+
### AWS Setup
|
24
|
+
Create a `.aws.yml` file in the root directory with the following structure.
|
24
25
|
|
25
26
|
```yaml
|
26
27
|
---
|
27
28
|
account1:
|
28
29
|
access_key_id: 'ACCESS_KEY_ID'
|
29
|
-
secret_access_key: 'SECRET_ACCESS_KEY
|
30
|
+
secret_access_key: 'SECRET_ACCESS_KEY'
|
30
31
|
account2:
|
31
32
|
access_key_id: 'ACCESS_KEY_ID'
|
32
33
|
secret_access_key: 'SECRET_ACCESS_KEY
|
33
34
|
```
|
34
35
|
|
36
|
+
### Google Setup (optional)
|
37
|
+
You can export audit information to a Google Spreadsheet, but you must first create a `.google.yml` in the root directory with the following structure.
|
38
|
+
|
39
|
+
```yaml
|
40
|
+
---
|
41
|
+
login:
|
42
|
+
email: 'GOOGLE_EMAIL_ADDRESS'
|
43
|
+
password: 'GOOGLE_EMAIL_PASSWORD'
|
44
|
+
file:
|
45
|
+
path: 'DESIRED_PATH_TO_FILE' #optional, creates in root directory otherwise
|
46
|
+
name: 'NAME_OF_FILE'
|
47
|
+
```
|
48
|
+
|
35
49
|
To find discrepancies between number of running instances and purchased instances, run:
|
36
50
|
|
37
51
|
$ aws_auditor audit account1
|
38
52
|
|
39
|
-
To list instances for all stacks in your account, run:
|
53
|
+
To list running instances for all stacks in your account, run:
|
54
|
+
|
55
|
+
$ aws_auditor inspect account1
|
56
|
+
|
57
|
+
To export audit information to a Google Spreadsheet, make sure you added a `.google.yml` and run:
|
40
58
|
|
41
|
-
$ aws_auditor
|
59
|
+
$ aws_auditor export account1
|
42
60
|
|
43
61
|
## Contributing
|
44
62
|
|
data/aws_auditor.gemspec
CHANGED
@@ -21,6 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_dependency 'aws-sdk', '~>1'
|
22
22
|
spec.add_dependency 'hashie', '~> 3.3'
|
23
23
|
spec.add_dependency 'gli', '~> 2.10'
|
24
|
+
spec.add_dependency 'highline', '~> 1.6'
|
25
|
+
spec.add_dependency 'google_drive', '~> 0.3'
|
24
26
|
|
25
27
|
spec.add_development_dependency "bundler", "~> 1.7"
|
26
28
|
spec.add_development_dependency "rake", "~> 10.0"
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative './instance_helper'
|
2
|
+
|
3
|
+
module AwsAuditor
|
4
|
+
class CacheInstance
|
5
|
+
extend InstanceHelper
|
6
|
+
extend CacheWrapper
|
7
|
+
|
8
|
+
attr_accessor :id, :name, :instance_type, :engine, :count
|
9
|
+
def initialize(cache_instance)
|
10
|
+
@id = cache_instance[:cache_cluster_id] || cache_instance[:reserved_cache_node_id]
|
11
|
+
@name = cache_instance[:cache_cluster_id] || cache_instance[:reserved_cache_node_id]
|
12
|
+
@instance_type = cache_instance[:cache_node_type]
|
13
|
+
@engine = cache_instance[:engine] || cache_instance[:product_description]
|
14
|
+
@count = cache_instance[:cache_node_count] || 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"#{engine} #{instance_type}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.get_instances
|
22
|
+
instances = cache.describe_cache_clusters[:cache_clusters]
|
23
|
+
instances.map do |instance|
|
24
|
+
next unless instance[:cache_cluster_status].to_s == 'available'
|
25
|
+
new(instance)
|
26
|
+
end if instances
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.get_reserved_instances
|
30
|
+
instances = cache.describe_reserved_cache_nodes[:reserved_db_instances]
|
31
|
+
instances.map do |instance|
|
32
|
+
next unless instance[:state].to_s == 'active'
|
33
|
+
new(instance)
|
34
|
+
end if instances
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -1,7 +1,14 @@
|
|
1
|
+
arg :aws_account
|
1
2
|
desc 'Reviews Reserved Instances'
|
2
3
|
command 'audit' do |c|
|
4
|
+
c.switch [:e, :ec2], :desc => "Only audit EC2 instances"
|
5
|
+
c.switch [:d, :rds], :desc => "Only audit RDS instances"
|
6
|
+
c.switch [:c, :cache], :desc => "Only audit ElastiCache instances"
|
7
|
+
c.switch [:r, :reserved], :desc => "Shows reserved instance counts"
|
8
|
+
c.switch [:i, :instances], :desc => "Shows current instance counts"
|
3
9
|
c.action do |global_options, options, args|
|
4
10
|
require_relative '../scripts/audit'
|
5
|
-
|
11
|
+
raise ArgumentError, 'You must specify an AWS account' unless args.first
|
12
|
+
AwsAuditor::Scripts::Audit.execute(args.first,options)
|
6
13
|
end
|
7
14
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
arg :aws_account
|
2
|
+
desc 'Reviews Reserved Instances'
|
3
|
+
command 'export' do |c|
|
4
|
+
c.action do |global_options, options, args|
|
5
|
+
require_relative '../scripts/export'
|
6
|
+
raise ArgumentError, 'You must specify an AWS account' unless args.first
|
7
|
+
AwsAuditor::Scripts::Export.execute(args.first, options)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
arg :aws_account
|
2
|
+
desc 'Reviews Stack Instances'
|
3
|
+
command 'inspect' do |c|
|
4
|
+
c.switch [:e, :ec2], :desc => "Only audit EC2 instances"
|
5
|
+
c.switch [:d, :rds], :desc => "Only audit RDS instances"
|
6
|
+
c.switch [:c, :cache], :desc => "Only audit ElastiCache instances"
|
7
|
+
c.action do |global_options, options, args|
|
8
|
+
require_relative '../scripts/inspect'
|
9
|
+
raise ArgumentError, 'You must specify an AWS account' unless args.first
|
10
|
+
AwsAuditor::Scripts::Inspect.execute(args.first,options)
|
11
|
+
end
|
12
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative './aws'
|
2
|
+
require_relative './google'
|
2
3
|
|
3
4
|
module AwsAuditor
|
4
5
|
module AWSWrapper
|
@@ -13,7 +14,7 @@ module AwsAuditor
|
|
13
14
|
attr_accessor :ec2
|
14
15
|
|
15
16
|
def ec2
|
16
|
-
@ec2 ||= AWS::EC2.new
|
17
|
+
@ec2 ||= AWS::EC2.new
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -24,5 +25,29 @@ module AwsAuditor
|
|
24
25
|
@opsworks ||= AWS::OpsWorks.new.client
|
25
26
|
end
|
26
27
|
end
|
28
|
+
|
29
|
+
module RDSWrapper
|
30
|
+
attr_accessor :rds
|
31
|
+
|
32
|
+
def rds
|
33
|
+
@rds ||= AWS::RDS.new.client
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module CacheWrapper
|
38
|
+
attr_accessor :cache
|
39
|
+
|
40
|
+
def cache
|
41
|
+
@cache ||= AWS::ElastiCache.new.client
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module GoogleWrapper
|
46
|
+
attr_accessor :google
|
47
|
+
|
48
|
+
def google
|
49
|
+
@google ||= AwsAuditor::Google.configuration
|
50
|
+
end
|
51
|
+
end
|
27
52
|
|
28
53
|
end
|
@@ -1,13 +1,16 @@
|
|
1
|
+
require_relative './instance_helper'
|
2
|
+
|
1
3
|
module AwsAuditor
|
2
|
-
class
|
4
|
+
class EC2Instance
|
5
|
+
extend InstanceHelper
|
3
6
|
extend EC2Wrapper
|
4
7
|
|
5
8
|
attr_accessor :id, :platform, :availability_zone, :instance_type, :count
|
6
|
-
def initialize(
|
7
|
-
@id =
|
8
|
-
@platform = platform_helper(
|
9
|
-
@availability_zone =
|
10
|
-
@instance_type =
|
9
|
+
def initialize(ec2_instance, count=1)
|
10
|
+
@id = ec2_instance.id
|
11
|
+
@platform = platform_helper(ec2_instance)
|
12
|
+
@availability_zone = ec2_instance.availability_zone
|
13
|
+
@instance_type = ec2_instance.instance_type
|
11
14
|
@count = count
|
12
15
|
end
|
13
16
|
|
@@ -15,12 +18,12 @@ module AwsAuditor
|
|
15
18
|
"#{@platform} #{@availability_zone} #{@instance_type}"
|
16
19
|
end
|
17
20
|
|
18
|
-
def platform_helper(
|
19
|
-
if
|
20
|
-
if
|
21
|
+
def platform_helper(ec2_instance)
|
22
|
+
if ec2_instance.class.to_s == 'AWS::EC2::Instance'
|
23
|
+
if ec2_instance.vpc?
|
21
24
|
return 'VPC'
|
22
|
-
elsif
|
23
|
-
if
|
25
|
+
elsif ec2_instance.platform
|
26
|
+
if ec2_instance.platform.downcase.include? 'windows'
|
24
27
|
return 'Windows'
|
25
28
|
else
|
26
29
|
return 'Linux'
|
@@ -28,10 +31,10 @@ module AwsAuditor
|
|
28
31
|
else
|
29
32
|
return 'Linux'
|
30
33
|
end
|
31
|
-
elsif
|
32
|
-
if
|
34
|
+
elsif ec2_instance.class.to_s == 'AWS::EC2::ReservedInstances'
|
35
|
+
if ec2_instance.product_description.downcase.include? 'vpc'
|
33
36
|
return 'VPC'
|
34
|
-
elsif
|
37
|
+
elsif ec2_instance.product_description.downcase.include? 'windows'
|
35
38
|
return 'Windows'
|
36
39
|
else
|
37
40
|
return 'Linux'
|
@@ -43,7 +46,7 @@ module AwsAuditor
|
|
43
46
|
instances = ec2.instances
|
44
47
|
instances.map do |instance|
|
45
48
|
next unless instance.status.to_s == 'running'
|
46
|
-
|
49
|
+
new(instance)
|
47
50
|
end if instances
|
48
51
|
end
|
49
52
|
|
@@ -51,12 +54,9 @@ module AwsAuditor
|
|
51
54
|
reserved_instances = ec2.reserved_instances
|
52
55
|
reserved_instances.map do |ri|
|
53
56
|
next unless ri.state == 'active'
|
54
|
-
|
57
|
+
new(ri, ri.instance_count)
|
55
58
|
end if reserved_instances
|
56
59
|
end
|
57
60
|
|
58
|
-
def self.instance_hash
|
59
|
-
Hash[get_instances.map {|instance| [instance.id, instance]}]
|
60
|
-
end
|
61
61
|
end
|
62
62
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module AwsAuditor
|
2
|
+
class GoogleConfig < Hash
|
3
|
+
include Hashie::Extensions::IndifferentAccess
|
4
|
+
end
|
5
|
+
|
6
|
+
class Google
|
7
|
+
FILE_NAMES = %w[.google.yml]
|
8
|
+
|
9
|
+
def self.configuration
|
10
|
+
credentials = load_config[:login]
|
11
|
+
GoogleDrive.login(credentials[:email],credentials[:password])
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.file
|
15
|
+
load_config[:file]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.load_config
|
19
|
+
return @config if @config
|
20
|
+
@config = GoogleConfig[YAML.load_file(config_path)]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.config_path
|
24
|
+
if filepath = FILE_NAMES.detect {|filename| File.exists?(filename)}
|
25
|
+
File.join(Dir.pwd, filepath)
|
26
|
+
else
|
27
|
+
old_dir = Dir.pwd
|
28
|
+
Dir.chdir('..')
|
29
|
+
if old_dir != Dir.pwd
|
30
|
+
config_path
|
31
|
+
else
|
32
|
+
puts "Could not find #{FILE_NAMES.join(' or ')}"
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'google_drive'
|
2
|
+
|
3
|
+
module AwsAuditor
|
4
|
+
class GoogleSheet
|
5
|
+
extend GoogleWrapper
|
6
|
+
|
7
|
+
attr_accessor :sheet, :worksheet, :path
|
8
|
+
def initialize(title, path, environment)
|
9
|
+
@sheet = self.class.create_sheet(title, path)
|
10
|
+
@worksheet = self.class.worksheet(sheet, environment)
|
11
|
+
end
|
12
|
+
|
13
|
+
def write_header(header)
|
14
|
+
worksheet.list.keys = header.unshift('name')
|
15
|
+
worksheet.save
|
16
|
+
end
|
17
|
+
|
18
|
+
def write_row(value_hash)
|
19
|
+
worksheet.list.push(value_hash)
|
20
|
+
worksheet.save
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.first_or_create(title)
|
24
|
+
spreadsheet = google.root_collection.files("title" => title, "title-exact" => true)[0]
|
25
|
+
spreadsheet ? spreadsheet : google.create_spreadsheet(title)
|
26
|
+
end
|
27
|
+
|
28
|
+
#returns a spreadsheet object
|
29
|
+
def self.create_sheet(title, path)
|
30
|
+
folder = go_to_collection(path) if path
|
31
|
+
if folder
|
32
|
+
spreadsheet = folder.files("title" => title, "title-exact" => true)[0]
|
33
|
+
if spreadsheet
|
34
|
+
return spreadsheet
|
35
|
+
else
|
36
|
+
file = first_or_create(title)
|
37
|
+
folder.add(file)
|
38
|
+
google.root_collection.remove(file)
|
39
|
+
return folder.files("title" => title, "title-exact" => true)[0]
|
40
|
+
end
|
41
|
+
else
|
42
|
+
first_or_create(title)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#returns a worksheet object
|
47
|
+
def self.worksheet(spreadsheet, title)
|
48
|
+
worksheet = spreadsheet.worksheet_by_title(title)
|
49
|
+
worksheet ? delete_all_rows(worksheet) : spreadsheet.add_worksheet(title)
|
50
|
+
end
|
51
|
+
|
52
|
+
#returns a collection object
|
53
|
+
def self.go_to_collection(directory)
|
54
|
+
if directory
|
55
|
+
path = directory.split('/')
|
56
|
+
go_to_subcollection(google.collection_by_title(path[0]),path[1..-1])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
#returns a collection object
|
61
|
+
def self.go_to_subcollection(base, subs)
|
62
|
+
puts "Folder doesn't exist in specified path" and exit if base.nil?
|
63
|
+
if subs.empty?
|
64
|
+
return base
|
65
|
+
else
|
66
|
+
base = base.subcollection_by_title(subs[0])
|
67
|
+
go_to_subcollection(base,subs[1..-1])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.delete_all_rows(worksheet)
|
72
|
+
worksheet.list.each do |row|
|
73
|
+
row.clear
|
74
|
+
end
|
75
|
+
worksheet.save
|
76
|
+
worksheet
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module AwsAuditor
|
2
|
+
module InstanceHelper
|
3
|
+
def instance_hash
|
4
|
+
Hash[get_instances.map { |instance| instance.nil? ? next : [instance.id, instance]}.compact]
|
5
|
+
end
|
6
|
+
|
7
|
+
def instance_count_hash(instances)
|
8
|
+
instance_hash = Hash.new()
|
9
|
+
instances.each do |instance|
|
10
|
+
next if instance.nil?
|
11
|
+
instance_hash[instance.to_s] = instance_hash.has_key?(instance.to_s) ? instance_hash[instance.to_s] + instance.count : instance.count
|
12
|
+
end if instances
|
13
|
+
instance_hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def compare
|
17
|
+
differences = Hash.new()
|
18
|
+
instances = instance_count_hash(get_instances)
|
19
|
+
ris = instance_count_hash(get_reserved_instances)
|
20
|
+
instances.keys.concat(ris.keys).uniq.each do |key|
|
21
|
+
instance_count = instances.has_key?(key) ? instances[key] : 0
|
22
|
+
ris_count = ris.has_key?(key) ? ris[key] : 0
|
23
|
+
differences[key] = ris_count - instance_count
|
24
|
+
end
|
25
|
+
differences
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative './instance_helper'
|
2
|
+
|
3
|
+
module AwsAuditor
|
4
|
+
class RDSInstance
|
5
|
+
extend InstanceHelper
|
6
|
+
extend RDSWrapper
|
7
|
+
|
8
|
+
attr_accessor :id, :name, :multi_az, :instance_type, :engine, :count
|
9
|
+
def initialize(rds_instance)
|
10
|
+
@id = rds_instance[:db_instance_identifier] || rds_instance[:reserved_db_instances_offering_id]
|
11
|
+
@name = rds_instance[:db_instance_identifier] || rds_instance[:db_name]
|
12
|
+
@multi_az = rds_instance[:multi_az]
|
13
|
+
@instance_type = rds_instance[:db_instance_class]
|
14
|
+
@engine = rds_instance[:engine] || rds_instance[:product_description]
|
15
|
+
@count = rds_instance[:db_instance_count] || 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"#{engine_helper} #{multi_az?} #{instance_type}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def multi_az?
|
23
|
+
multi_az ? "Multi-AZ" : "Single-AZ"
|
24
|
+
end
|
25
|
+
|
26
|
+
def engine_helper
|
27
|
+
if engine.downcase.include? "post"
|
28
|
+
return "PostgreSQL"
|
29
|
+
elsif engine.downcase.include? "mysql"
|
30
|
+
return "MySQL"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.get_instances
|
35
|
+
instances = rds.describe_db_instances[:db_instances]
|
36
|
+
instances.map do |instance|
|
37
|
+
next unless instance[:db_instance_status].to_s == 'available'
|
38
|
+
new(instance)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_reserved_instances
|
43
|
+
instances = rds.describe_reserved_db_instances[:reserved_db_instances]
|
44
|
+
instances.map do |instance|
|
45
|
+
next unless instance[:state].to_s == 'active'
|
46
|
+
new(instance)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -1,36 +1,87 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
|
1
3
|
module AwsAuditor
|
2
4
|
module Scripts
|
3
5
|
class Audit
|
4
6
|
extend AWSWrapper
|
5
|
-
extend EC2Wrapper
|
6
7
|
|
7
|
-
def self.execute(environment)
|
8
|
+
def self.execute(environment, options=nil)
|
8
9
|
aws(environment)
|
9
|
-
|
10
|
-
|
10
|
+
if options[:ec2]
|
11
|
+
audit_ec2(options)
|
12
|
+
elsif options[:rds]
|
13
|
+
audit_rds(options)
|
14
|
+
elsif options[:cache]
|
15
|
+
audit_cache(options)
|
16
|
+
else
|
17
|
+
audit_ec2(options)
|
18
|
+
audit_rds(options)
|
19
|
+
audit_cache(options)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.audit_rds(options)
|
25
|
+
puts "=============== RDS ==============="
|
26
|
+
if options[:instances]
|
27
|
+
RDSInstance.instance_count_hash(RDSInstance.get_instances).each do |key,value|
|
28
|
+
say "<%= color('#{key}: #{value}', :white) %>"
|
29
|
+
end
|
30
|
+
elsif options[:reserved]
|
31
|
+
RDSInstance.instance_count_hash(RDSInstance.get_reserved_instances).each do |key,value|
|
32
|
+
say "<%= color('#{key}: #{value}', :white) %>"
|
33
|
+
end
|
34
|
+
else
|
35
|
+
RDSInstance.compare.each do |key, value|
|
36
|
+
colorize(key,value)
|
37
|
+
end
|
11
38
|
end
|
12
39
|
end
|
13
40
|
|
14
|
-
def self.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
41
|
+
def self.audit_ec2(options)
|
42
|
+
puts "=============== EC2 ==============="
|
43
|
+
if options[:instances]
|
44
|
+
EC2Instance.instance_count_hash(EC2Instance.get_instances).each do |key,value|
|
45
|
+
say "<%= color('#{key}: #{value}', :white) %>"
|
46
|
+
end
|
47
|
+
elsif options[:reserved]
|
48
|
+
EC2Instance.instance_count_hash(EC2Instance.get_reserved_instances).each do |key,value|
|
49
|
+
say "<%= color('#{key}: #{value}', :white) %>"
|
50
|
+
end
|
51
|
+
else
|
52
|
+
EC2Instance.compare.each do |key,value|
|
53
|
+
colorize(key,value)
|
54
|
+
end
|
19
55
|
end
|
20
|
-
instance_hash
|
21
56
|
end
|
22
57
|
|
23
|
-
def self.
|
24
|
-
|
25
|
-
instances
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
58
|
+
def self.audit_cache(options)
|
59
|
+
puts "============== CACHE =============="
|
60
|
+
if options[:instances]
|
61
|
+
CacheInstance.instance_count_hash(CacheInstance.get_instances).each do |key,value|
|
62
|
+
say "<%= color('#{key}: #{value}', :white) %>"
|
63
|
+
end
|
64
|
+
elsif options[:reserved]
|
65
|
+
CacheInstance.instance_count_hash(CacheInstance.get_reserved_instances).each do |key,value|
|
66
|
+
say "<%= color('#{key}: #{value}', :white) %>"
|
67
|
+
end
|
68
|
+
else
|
69
|
+
CacheInstance.compare.each do |key,value|
|
70
|
+
colorize(key,value)
|
71
|
+
end
|
31
72
|
end
|
32
|
-
differences
|
33
73
|
end
|
74
|
+
|
75
|
+
def self.colorize(key,value)
|
76
|
+
if value < 0
|
77
|
+
say "<%= color('#{key}: #{value}', :yellow) %>"
|
78
|
+
elsif value == 0
|
79
|
+
say "<%= color('#{key}: #{value}', :green) %>"
|
80
|
+
elsif value > 0
|
81
|
+
say "<%= color('#{key}: #{value}', :red) %>"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
34
85
|
end
|
35
86
|
end
|
36
87
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require_relative "../google"
|
2
|
+
|
3
|
+
module AwsAuditor
|
4
|
+
module Scripts
|
5
|
+
class Export
|
6
|
+
extend GoogleWrapper
|
7
|
+
extend AWSWrapper
|
8
|
+
|
9
|
+
def self.execute(environment, options = nil)
|
10
|
+
aws(environment)
|
11
|
+
file = GoogleSheet.new(Google.file[:name], Google.file[:path], environment)
|
12
|
+
file.write_header(get_all_keys)
|
13
|
+
write_opsworks_stacks(file)
|
14
|
+
write_rds(file)
|
15
|
+
write_cache(file)
|
16
|
+
write_totals(file)
|
17
|
+
`open #{file.sheet.human_url}`
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.write_opsworks_stacks(file)
|
21
|
+
file.write_row({name: "EC2"})
|
22
|
+
opsworks_stacks.each do |stack|
|
23
|
+
next if stack.instances.empty?
|
24
|
+
value_hash = EC2Instance.instance_count_hash(stack.instances)
|
25
|
+
value_hash[:name] = stack.name
|
26
|
+
file.write_row(value_hash)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.write_rds(file)
|
31
|
+
file.write_row({name: "RDS"})
|
32
|
+
rds_instances.each do |db|
|
33
|
+
value_hash = Hash({:name => db.name, :"#{db.to_s}" => '1'})
|
34
|
+
file.write_row(value_hash)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.write_cache(file)
|
39
|
+
file.write_row({name: "CACHE"})
|
40
|
+
cache_instances.each do |cache|
|
41
|
+
value_hash = Hash({:name => cache.name, :"#{cache.to_s}" => '1'})
|
42
|
+
file.write_row(value_hash)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.write_totals(file)
|
47
|
+
file.write_row({name: "TOTALS"})
|
48
|
+
instance_counts = get_all_instance_counts.merge({name: "Running Instances"})
|
49
|
+
file.write_row(instance_counts)
|
50
|
+
reserved_counts = get_all_reserved_counts.merge({name: "Reserved Instances"})
|
51
|
+
file.write_row(reserved_counts)
|
52
|
+
differences = get_difference_counts.merge({name: "Differences"})
|
53
|
+
file.write_row(differences)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.get_all_keys
|
57
|
+
ec2 = EC2Instance.instance_hash.values.map{ |x| x.to_s }.uniq.sort! { |a,b| a.downcase <=> b.downcase }
|
58
|
+
rds = RDSInstance.instance_hash.values.map{ |x| x.to_s }.uniq.sort! { |a,b| a.downcase <=> b.downcase }
|
59
|
+
cache = CacheInstance.instance_hash.values.map{ |x| x.to_s }.uniq.sort! { |a,b| a.downcase <=> b.downcase }
|
60
|
+
ec2.concat(rds).concat(cache).compact
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.get_all_instance_counts
|
64
|
+
ec2 = EC2Instance.instance_count_hash(EC2Instance.get_instances)
|
65
|
+
rds = RDSInstance.instance_count_hash(RDSInstance.get_instances)
|
66
|
+
cache = CacheInstance.instance_count_hash(CacheInstance.get_instances)
|
67
|
+
ec2.merge(rds).merge(cache)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.get_all_reserved_counts
|
71
|
+
ec2 = EC2Instance.instance_count_hash(EC2Instance.get_reserved_instances)
|
72
|
+
rds = RDSInstance.instance_count_hash(RDSInstance.get_reserved_instances)
|
73
|
+
cache = CacheInstance.instance_count_hash(CacheInstance.get_reserved_instances)
|
74
|
+
ec2.merge(rds).merge(cache)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.get_difference_counts
|
78
|
+
ec2 = EC2Instance.compare
|
79
|
+
rds = RDSInstance.compare
|
80
|
+
cache = CacheInstance.compare
|
81
|
+
ec2.merge(rds).merge(cache)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.opsworks_stacks
|
85
|
+
@opsworks_stacks ||= Stack.all
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.rds_instances
|
89
|
+
@rds_instances ||= RDSInstance.get_instances
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.cache_instances
|
93
|
+
@cache_instances ||= CacheInstance.get_instances
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module AwsAuditor
|
2
|
+
module Scripts
|
3
|
+
class Inspect
|
4
|
+
extend AWSWrapper
|
5
|
+
extend OpsWorksWrapper
|
6
|
+
|
7
|
+
def self.execute(environment, options=nil)
|
8
|
+
aws(environment)
|
9
|
+
if options[:ec2]
|
10
|
+
inspect_stacks
|
11
|
+
elsif options[:rds]
|
12
|
+
inspect_dbs
|
13
|
+
elsif options[:cache]
|
14
|
+
inspect_caches
|
15
|
+
else
|
16
|
+
puts "You must use a switch. See `aws-auditor inspect --help` for more info."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.inspect_stacks
|
21
|
+
Stack.all.each do |stack|
|
22
|
+
stack.pretty_print
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.inspect_dbs
|
27
|
+
RDSInstance.get_instances.each do |db|
|
28
|
+
puts "========================"
|
29
|
+
puts "#{db.name}"
|
30
|
+
puts "========================"
|
31
|
+
puts db.to_s
|
32
|
+
puts "\n"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.inspect_caches
|
37
|
+
CacheInstance.get_instances.each do |cache|
|
38
|
+
puts "========================"
|
39
|
+
puts "#{cache.name}"
|
40
|
+
puts "========================"
|
41
|
+
puts cache.to_s
|
42
|
+
puts "\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/aws_auditor/stack.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
|
1
3
|
module AwsAuditor
|
2
4
|
class Stack
|
3
5
|
extend OpsWorksWrapper
|
@@ -14,7 +16,13 @@ module AwsAuditor
|
|
14
16
|
instances = self.class.opsworks.describe_instances({stack_id: id})[:instances]
|
15
17
|
instances.map do |instance|
|
16
18
|
next unless instance[:status].to_s == 'online'
|
17
|
-
all_instances[instance[:ec2_instance_id]]
|
19
|
+
self.class.all_instances[instance[:ec2_instance_id]]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def print_instances
|
24
|
+
EC2Instance.instance_count_hash(self.instances).each do |key,value|
|
25
|
+
say "<%= color('#{key}: #{value}', :white) %>"
|
18
26
|
end
|
19
27
|
end
|
20
28
|
|
@@ -22,14 +30,19 @@ module AwsAuditor
|
|
22
30
|
puts "----------------------------------"
|
23
31
|
puts "#{@name}"
|
24
32
|
puts "----------------------------------"
|
25
|
-
|
26
|
-
puts instance.to_s
|
27
|
-
end
|
33
|
+
print_instances
|
28
34
|
puts "\n"
|
29
35
|
end
|
36
|
+
|
37
|
+
def self.all
|
38
|
+
stacks = opsworks.describe_stacks
|
39
|
+
stacks.data[:stacks].map do |stack|
|
40
|
+
new(stack)
|
41
|
+
end.sort! { |a,b| a.name.downcase <=> b.name.downcase } if stacks
|
42
|
+
end
|
30
43
|
|
31
|
-
def all_instances
|
32
|
-
@all_instances ||=
|
44
|
+
def self.all_instances
|
45
|
+
@all_instances ||= EC2Instance.instance_hash
|
33
46
|
end
|
34
47
|
|
35
48
|
end
|
data/lib/aws_auditor/version.rb
CHANGED
data/lib/aws_auditor.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
require 'aws_auditor/version'
|
2
2
|
require_relative 'aws_auditor/convenience_wrappers'
|
3
|
-
require_relative 'aws_auditor/
|
3
|
+
require_relative 'aws_auditor/ec2_instance'
|
4
|
+
require_relative 'aws_auditor/rds_instance'
|
5
|
+
require_relative 'aws_auditor/cache_instance'
|
4
6
|
require_relative 'aws_auditor/stack'
|
7
|
+
require_relative 'aws_auditor/google_sheet'
|
5
8
|
|
6
9
|
module AwsAuditor
|
7
10
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws_auditor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elliot Hursh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '2.10'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: highline
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: google_drive
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.3'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.3'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: bundler
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,12 +127,19 @@ files:
|
|
99
127
|
- bin/aws-auditor
|
100
128
|
- lib/aws_auditor.rb
|
101
129
|
- lib/aws_auditor/aws.rb
|
130
|
+
- lib/aws_auditor/cache_instance.rb
|
102
131
|
- lib/aws_auditor/commands/audit.rb
|
103
|
-
- lib/aws_auditor/commands/
|
132
|
+
- lib/aws_auditor/commands/export.rb
|
133
|
+
- lib/aws_auditor/commands/inspect.rb
|
104
134
|
- lib/aws_auditor/convenience_wrappers.rb
|
105
|
-
- lib/aws_auditor/
|
135
|
+
- lib/aws_auditor/ec2_instance.rb
|
136
|
+
- lib/aws_auditor/google.rb
|
137
|
+
- lib/aws_auditor/google_sheet.rb
|
138
|
+
- lib/aws_auditor/instance_helper.rb
|
139
|
+
- lib/aws_auditor/rds_instance.rb
|
106
140
|
- lib/aws_auditor/scripts/audit.rb
|
107
|
-
- lib/aws_auditor/scripts/
|
141
|
+
- lib/aws_auditor/scripts/export.rb
|
142
|
+
- lib/aws_auditor/scripts/inspect.rb
|
108
143
|
- lib/aws_auditor/stack.rb
|
109
144
|
- lib/aws_auditor/version.rb
|
110
145
|
homepage: https://github.com/elliothursh/aws_auditor
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module AwsAuditor
|
2
|
-
module Scripts
|
3
|
-
class StackAudit
|
4
|
-
extend AWSWrapper
|
5
|
-
extend EC2Wrapper
|
6
|
-
extend OpsWorksWrapper
|
7
|
-
|
8
|
-
def self.execute(environment)
|
9
|
-
aws(environment)
|
10
|
-
get_stacks
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.get_stacks
|
14
|
-
stacks = opsworks.describe_stacks
|
15
|
-
stacks.data[:stacks].map do |stack|
|
16
|
-
stck = Stack.new(stack)
|
17
|
-
stck.pretty_print
|
18
|
-
stck
|
19
|
-
end if stacks
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|