aws_auditor 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|