sport_ngin_aws_auditor 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.octopolo.yml +4 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.soyuz.yml +13 -0
  8. data/.travis.yml +13 -0
  9. data/CHANGELOG.markdown +30 -0
  10. data/Gemfile +4 -0
  11. data/LICENSE.txt +22 -0
  12. data/README.md +103 -0
  13. data/Rakefile +2 -0
  14. data/bin/sport-ngin-aws-auditor +27 -0
  15. data/lib/sport_ngin_aws_auditor/aws.rb +44 -0
  16. data/lib/sport_ngin_aws_auditor/cache_instance.rb +69 -0
  17. data/lib/sport_ngin_aws_auditor/commands/audit.rb +17 -0
  18. data/lib/sport_ngin_aws_auditor/commands/export.rb +11 -0
  19. data/lib/sport_ngin_aws_auditor/commands/inspect.rb +12 -0
  20. data/lib/sport_ngin_aws_auditor/config.rb +51 -0
  21. data/lib/sport_ngin_aws_auditor/convenience_wrappers.rb +59 -0
  22. data/lib/sport_ngin_aws_auditor/ec2_instance.rb +119 -0
  23. data/lib/sport_ngin_aws_auditor/google.rb +62 -0
  24. data/lib/sport_ngin_aws_auditor/google_sheet.rb +80 -0
  25. data/lib/sport_ngin_aws_auditor/instance_helper.rb +71 -0
  26. data/lib/sport_ngin_aws_auditor/notify_slack.rb +31 -0
  27. data/lib/sport_ngin_aws_auditor/output.rb +13 -0
  28. data/lib/sport_ngin_aws_auditor/rds_instance.rb +78 -0
  29. data/lib/sport_ngin_aws_auditor/scripts/audit.rb +124 -0
  30. data/lib/sport_ngin_aws_auditor/scripts/export.rb +146 -0
  31. data/lib/sport_ngin_aws_auditor/scripts/inspect.rb +44 -0
  32. data/lib/sport_ngin_aws_auditor/stack.rb +63 -0
  33. data/lib/sport_ngin_aws_auditor/version.rb +3 -0
  34. data/lib/sport_ngin_aws_auditor.rb +14 -0
  35. data/spec/spec_helper.rb +45 -0
  36. data/spec/sport_ngin_aws_auditor/aws_spec.rb +48 -0
  37. data/spec/sport_ngin_aws_auditor/cache_instance_spec.rb +125 -0
  38. data/spec/sport_ngin_aws_auditor/config_spec.rb +44 -0
  39. data/spec/sport_ngin_aws_auditor/ec2_instance_spec.rb +181 -0
  40. data/spec/sport_ngin_aws_auditor/notify_slack_spec.rb +33 -0
  41. data/spec/sport_ngin_aws_auditor/rds_instance_spec.rb +140 -0
  42. data/sport_ngin_aws_auditor.gemspec +33 -0
  43. metadata +251 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7d0c635ba58b86cec68263f3c929d54ffbede978
4
+ data.tar.gz: ff8c7fb16a94ef122280a08396eaf27edb79c32f
5
+ SHA512:
6
+ metadata.gz: 49491ebb5acbf0c6df12525bf54c070e7caaf2936ba21e911e9665e5efca353f324d8b71ab80a2ea53a6c51d728155d0e6a1f8c6c6a5f2118727e17945da121c
7
+ data.tar.gz: f6311b455eb7ba7e7c0e04c4562195bab4e209715e5e4498f5ac6344930ea6b8bc64927953313582eb87766271a00d1c376f1e5da224cad4de0f3743f8cb200b
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+
16
+ .DS_Store
17
+ .aws.yml
18
+ .google.yml
19
+ *.gem
20
+ .idea
data/.octopolo.yml ADDED
@@ -0,0 +1,4 @@
1
+ github_repo: sportngin/sport_ngin_aws_auditor
2
+ semantic_versioning: true
3
+ branches_to_keep:
4
+ - master
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ sport_ngin_aws_auditor
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.2.5
data/.soyuz.yml ADDED
@@ -0,0 +1,13 @@
1
+ defaults:
2
+ deploy_cmd: gem push *.gem
3
+ before_deploy_cmds:
4
+ - /usr/local/bin/op tag-release
5
+ - sed -i "" -e "s/\".*/\"$(git tag| sort -n -t. -k1,1 -k2,2 -k3,3 | tail -1 | sed s/v//)\"/" lib/sport_ngin_aws_auditor/version.rb
6
+ - git add lib/sport_ngin_aws_auditor/version.rb
7
+ - git commit -m "Version Bump" && git push
8
+ - gem build sport_ngin_aws_auditor.gemspec
9
+ after_deploy_cmds:
10
+ - rm *.gem
11
+ environments:
12
+ -
13
+ rubygems: {}
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ sudo: false
2
+ branches:
3
+ only:
4
+ - master
5
+ - /^deployable.*$/
6
+
7
+ language: ruby
8
+ rvm:
9
+ - 2.0.0
10
+ - 2.1
11
+ - 2.2
12
+ cache: bundler
13
+ script: bundle exec rspec
@@ -0,0 +1,30 @@
1
+ #### v3.0.2
2
+ #### v3.0.1
3
+ #### v3.0.0
4
+ * Rename gem directories and modules
5
+
6
+ > Emma Sax: Brian Bergstrom: https://github.com/sportngin/sport_ngin_aws_auditor/pull/6
7
+
8
+ #### v2.1.0
9
+ * Adding option to print audit results to Slack channel
10
+
11
+ > Emma Sax, Matt Krieger: Brian Bergstrom: https://github.com/sportngin/aws_auditor/pull/4
12
+
13
+ * Adding option to print audit results to Slack channel
14
+
15
+ > Emma Sax, Matt Krieger: Brian Bergstrom: https://github.com/sportngin/aws_auditor/pull/4
16
+
17
+ #### v2.0.0
18
+ * Adding enhancements for taking no-reserved-instance tag into consideration during audit
19
+
20
+ > Emma Sax: Brian Bergstrom: https://github.com/sportngin/aws_auditor/pull/2
21
+
22
+ #### v1.0.0
23
+ * Upgrading aws-sdk version from v1 to v2
24
+
25
+ > Emma Sax: Brian Bergstrom: https://github.com/sportngin/aws_auditor/pull/3
26
+
27
+ * First tests, Travis CI, MFA support, and fog file compatibility
28
+
29
+ > Brian Bergstrom: Emma Sax: https://github.com/sportngin/aws_auditor/pull/1
30
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aws_auditor.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Elliot Hursh
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # SportNginAwsAuditor
2
+
3
+ Audits your AWS accounts to find discrepancies between the number of running instances and purchased reserved instances.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'sport_ngin_aws_auditor'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install sport_ngin_aws_auditor
20
+
21
+ ## How-to
22
+
23
+ ### AWS Setup
24
+ Create an `~/.aws/credentials` file that should have the following structure:
25
+
26
+ ```
27
+ [ACCOUNT 1]
28
+ aws_access_key_id = [AWS ACCESS KEY]
29
+ aws_secret_access_key = [SECRET ACCESS KEY]
30
+
31
+ [ACCOUNT 2]
32
+ aws_access_key_id = [AWS ACCESS KEY]
33
+ aws_secret_access_key = [SECRET ACCESS KEY]
34
+
35
+ [ACCOUNT 3]
36
+ aws_access_key_id = [AWS ACCESS KEY]
37
+ aws_secret_access_key = [SECRET ACCESS KEY]
38
+ ```
39
+
40
+ ### Google Setup (optional)
41
+ You can export audit information to a Google Spreadsheet, but you must first follow “Create a client ID and client secret” on [this page](https://developers.google.com/drive/web/auth/web-server) to get a client ID and client secret for OAuth. Then create a `.google.yml` in your home directory with the following structure.
42
+
43
+ ```yaml
44
+ ---
45
+ credentials:
46
+ client_id: 'GOOGLE_CLIENT_ID'
47
+ client_secret: 'GOOGLE_CLIENT_ID'
48
+ file:
49
+ path: 'DESIRED_PATH_TO_FILE' # optional, creates in root directory otherwise
50
+ name: 'NAME_OF_FILE'
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ ### The Audit Command
56
+
57
+ To find discrepancies between number of running instances and purchased instances, run:
58
+
59
+ $ sport_ngin_aws_auditor audit account1
60
+
61
+ Any running instances that are not matched with a reserved instance with show up as yellow (with the negative number indicating the amount), the reserved instances that are not matched with an running instance will show up in red (with the positive number indicating the amount), and any reserved instances and running instances that match will show up in green. Any instances in blue with asteriks have a special tag that can either be specified in the audit command or will be defaulted to `no-reserved-instance`.
62
+
63
+ To specify your own tag name, run:
64
+
65
+ $ sport_ngin_aws_auditor audit --tag=your_custom_tag account1
66
+
67
+ If you don't want to use any tag at all, run:
68
+
69
+ $ sport_ngin_aws_auditor audit --no_tag account1
70
+
71
+ To print a condensed version of the discrepancies to a Slack account (instead of printing to the terminal), run:
72
+
73
+ $ sport_ngin_aws_auditor audit --slack account1
74
+
75
+ For this option to use a designated channel, username, icon/emoji, and webhook, set up a global config file (called `.aws_auditor.yml`) in your home directory. The webhook urls for slack can be obtained [here](https://api.slack.com/incoming-webhooks). The config file should look something like this:
76
+
77
+ ```
78
+ slack:
79
+ username: [AN AWESOME USERNAME]
80
+ icon_url: [AN AWESOME IMAGE]
81
+ channel: "#[AN SUPER COOL CHANNEL]"
82
+ webhook: [YOUR WEBHOOK URL]
83
+ ```
84
+
85
+ ### The Inspect Command
86
+
87
+ To list information about all running instances in your account, run:
88
+
89
+ $ sport_ngin_aws_auditor inspect account1
90
+
91
+ ### The Export Command
92
+
93
+ To export audit information to a Google Spreadsheet, make sure you added a `.google.yml` and run:
94
+
95
+ $ sport_ngin_aws_auditor export -d account1
96
+
97
+ ## Contributing
98
+
99
+ 1. Fork it (https://github.com/sportngin/sport_ngin_aws_auditor/fork)
100
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
101
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
102
+ 4. Push to the branch (`git push origin my-new-feature`)
103
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'gli'
4
+ require_relative '../lib/sport_ngin_aws_auditor'
5
+
6
+ include GLI::App
7
+
8
+ program_desc 'Sport Ngin AWS Auditor'
9
+ version SportNginAwsAuditor::VERSION
10
+
11
+ wrap_help_text :verbatim
12
+
13
+ flag [:config], :desc => 'SportNginAwsAuditor config file path', :default_value => SportNginAwsAuditor::DefaultPaths.config
14
+
15
+ program_long_desc """
16
+ DOCUMENTATION
17
+ """
18
+
19
+ commands_from File.expand_path(File.dirname(__FILE__) + '/../lib/sport_ngin_aws_auditor/commands')
20
+
21
+ pre do |global,command,options,args|
22
+ SportNginAwsAuditor::Config.load(global[:config])
23
+ SportNginAwsAuditor::Config.merge! global
24
+ true
25
+ end
26
+
27
+ exit run(ARGV)
@@ -0,0 +1,44 @@
1
+ require 'aws-sdk'
2
+ require 'yaml'
3
+ require 'hashie'
4
+
5
+ module SportNginAwsAuditor
6
+ class AwsConfig < Hash
7
+ include Hashie::Extensions::IndifferentAccess
8
+ end
9
+
10
+ class AWSSDK
11
+ def self.authenticate(environment)
12
+ shared_credentials = Aws::SharedCredentials.new(profile_name: environment)
13
+ Aws.config.update({region: 'us-east-1', credentials: shared_credentials})
14
+
15
+ iam = Aws::IAM::Client.new
16
+
17
+ # this will be an array of 0 or 1 because iam.list_mfa_devices.mfa_devices will only return 0 or 1 device per user;
18
+ # if user doesn't have MFA enabled, then this loop won't even execute
19
+ iam.list_mfa_devices.mfa_devices.each do |mfadevice|
20
+ mfa_serial_number = mfadevice.serial_number
21
+ mfa_token = Output.ask("Enter MFA token: "){ |q| q.validate = /^\d{6}$/ }
22
+ session_credentials_hash = get_session(mfa_token,
23
+ mfa_serial_number,
24
+ shared_credentials.credentials.access_key_id,
25
+ shared_credentials.credentials.secret_access_key).credentials
26
+
27
+ session_credentials = Aws::Credentials.new(session_credentials_hash.access_key_id,
28
+ session_credentials_hash.secret_access_key,
29
+ session_credentials_hash.session_token)
30
+ Aws.config.update({region: 'us-east-1', credentials: session_credentials})
31
+ end
32
+ end
33
+
34
+ def self.get_session(mfa_token, mfa_serial_number, access_key_id, secret_access_key)
35
+ return @session if @session
36
+ sts = Aws::STS::Client.new(access_key_id: access_key_id,
37
+ secret_access_key: secret_access_key,
38
+ region: 'us-east-1')
39
+ @session = sts.get_session_token(duration_seconds: 3600,
40
+ serial_number: mfa_serial_number,
41
+ token_code: mfa_token)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,69 @@
1
+ require_relative './instance_helper'
2
+
3
+ module SportNginAwsAuditor
4
+ class CacheInstance
5
+ extend InstanceHelper
6
+ extend CacheWrapper
7
+ extend AWSWrapper
8
+
9
+ class << self
10
+ attr_accessor :instances, :reserved_instances
11
+ end
12
+
13
+ attr_accessor :id, :name, :instance_type, :engine, :count, :tag_value
14
+ def initialize(cache_instance, account_id=nil, tag_name=nil, cache=nil)
15
+ if cache_instance.class.to_s == "Aws::ElastiCache::Types::ReservedCacheNode"
16
+ self.id = cache_instance.reserved_cache_node_id
17
+ self.name = cache_instance.reserved_cache_node_id
18
+ self.instance_type = cache_instance.cache_node_type
19
+ self.engine = cache_instance.product_description
20
+ self.count = cache_instance.cache_node_count
21
+ elsif cache_instance.class.to_s == "Aws::ElastiCache::Types::CacheCluster"
22
+ self.id = cache_instance.cache_cluster_id
23
+ self.name = cache_instance.cache_cluster_id
24
+ self.instance_type = cache_instance.cache_node_type
25
+ self.engine = cache_instance.engine
26
+ self.count = cache_instance.num_cache_nodes
27
+
28
+ if tag_name
29
+ region = cache_instance.preferred_availability_zone.split(//).first(9).join
30
+ region = "us-east-1" if region == "Multiple"
31
+ arn = "arn:aws:elasticache:#{region}:#{account_id}:cluster:#{self.id}"
32
+
33
+ # go through to see if the tag we're looking for is one of them
34
+ cache.list_tags_for_resource(resource_name: arn).tag_list.each do |tag|
35
+ if tag.key == tag_name
36
+ self.tag_value = tag.value
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def to_s
44
+ "#{engine} #{instance_type}"
45
+ end
46
+
47
+ def self.get_instances(tag_name=nil)
48
+ return @instances if @instances
49
+ account_id = get_account_id
50
+ @instances = cache.describe_cache_clusters.cache_clusters.map do |instance|
51
+ next unless instance.cache_cluster_status.to_s == 'available'
52
+ new(instance, account_id, tag_name, cache)
53
+ end.compact
54
+ end
55
+
56
+ def self.get_reserved_instances
57
+ return @reserved_instances if @reserved_instances
58
+ @reserved_instances = cache.describe_reserved_cache_nodes.reserved_cache_nodes.map do |instance|
59
+ next unless instance.state.to_s == 'active'
60
+ new(instance)
61
+ end.compact
62
+ end
63
+
64
+ def no_reserved_instance_tag_value
65
+ @tag_value
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,17 @@
1
+ arg :aws_account
2
+ desc 'Audits Reserved Instance Counts'
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"
9
+ c.flag [:t, :tag], :default_value => "no-reserved-instance", :desc => "Read a tag and group separately during audit"
10
+ c.switch [:n, :no_tag], :desc => "Ignore all tags during audit"
11
+ c.switch [:s, :slack], :desc => "Will print condensed version of audit to a Slack channel"
12
+ c.action do |global_options, options, args|
13
+ require_relative '../scripts/audit'
14
+ raise ArgumentError, 'You must specify an AWS account' unless args.first
15
+ SportNginAwsAuditor::Scripts::Audit.execute(args.first, options)
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ arg :aws_account
2
+ desc 'Export an Audit to Google SpreadSheets'
3
+ command 'export' do |c|
4
+ c.switch [:c, :csv], :desc => "Exports to CSV"
5
+ c.switch [:d, :drive], :desc => "Exports to Google Drive"
6
+ c.action do |global_options, options, args|
7
+ require_relative '../scripts/export'
8
+ raise ArgumentError, 'You must specify an AWS account' unless args.first
9
+ SportNginAwsAuditor::Scripts::Export.execute(args.first, options)
10
+ end
11
+ 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 inspect EC2 instances"
5
+ c.switch [:d, :rds], :desc => "Only inspect RDS instances"
6
+ c.switch [:c, :cache], :desc => "Only inspect 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
+ SportNginAwsAuditor::Scripts::Inspect.execute(args.first,options)
11
+ end
12
+ end
@@ -0,0 +1,51 @@
1
+ require 'yaml'
2
+ require 'hashie'
3
+
4
+ module SportNginAwsAuditor
5
+
6
+ class DefaultPaths
7
+ class << self
8
+ def config
9
+ File.join(self.home,'.aws_auditor.yml')
10
+ end
11
+
12
+ def home
13
+ ENV['HOME'] ? ENV['HOME'] : "."
14
+ end
15
+ end
16
+ end
17
+
18
+ class Config
19
+ class << self
20
+
21
+ def config
22
+ config_data.to_hash
23
+ end
24
+
25
+ def load(path)
26
+ if File.exist?(path)
27
+ load_config(path)
28
+ return config
29
+ else
30
+ return {}
31
+ end
32
+ end
33
+
34
+ def config_data
35
+ @config_data ||= Hashie::Mash.new
36
+ end
37
+ private :config_data
38
+
39
+ def method_missing(method, args=false)
40
+ config_data.send(method, args)
41
+ end
42
+ private :method_missing
43
+
44
+ def load_config(file)
45
+ YAML.load_file(file).each{ |key,value| config_data.assign_property(key, value) }
46
+ end
47
+ private :load_config
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,59 @@
1
+ require_relative './aws'
2
+ require_relative './google'
3
+
4
+ module SportNginAwsAuditor
5
+ attr_accessor :creds
6
+
7
+ module AWSWrapper
8
+ attr_accessor :aws, :account_id
9
+
10
+ def aws(environment)
11
+ SportNginAwsAuditor::AWSSDK.authenticate(environment)
12
+ end
13
+
14
+ def get_account_id
15
+ @account_id ||= Aws::STS::Client.new.get_caller_identity.account
16
+ end
17
+ end
18
+
19
+ module EC2Wrapper
20
+ attr_accessor :ec2
21
+
22
+ def ec2
23
+ @ec2 ||= Aws::EC2::Client.new
24
+ end
25
+ end
26
+
27
+ module OpsWorksWrapper
28
+ attr_accessor :opsworks
29
+
30
+ def opsworks
31
+ @opsworks ||= Aws::OpsWorks::Client.new
32
+ end
33
+ end
34
+
35
+ module RDSWrapper
36
+ attr_accessor :rds
37
+
38
+ def rds
39
+ @rds ||= Aws::RDS::Client.new
40
+ end
41
+ end
42
+
43
+ module CacheWrapper
44
+ attr_accessor :cache
45
+
46
+ def cache
47
+ @cache ||= Aws::ElastiCache::Client.new
48
+ end
49
+ end
50
+
51
+ module GoogleWrapper
52
+ attr_accessor :google
53
+
54
+ def google
55
+ @google ||= SportNginAwsAuditor::Google.configuration
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,119 @@
1
+ require_relative './instance_helper'
2
+
3
+ module SportNginAwsAuditor
4
+ class EC2Instance
5
+ extend InstanceHelper
6
+ extend EC2Wrapper
7
+
8
+ class << self
9
+ attr_accessor :instances, :reserved_instances
10
+ end
11
+
12
+ attr_accessor :id, :name, :platform, :availability_zone, :instance_type, :count, :stack_name, :tag_value
13
+ def initialize(ec2_instance, tag_name, count=1)
14
+ if ec2_instance.class.to_s == "Aws::EC2::Types::ReservedInstances"
15
+ self.id = ec2_instance.reserved_instances_id
16
+ self.name = nil
17
+ self.platform = platform_helper(ec2_instance)
18
+ self.availability_zone = ec2_instance.availability_zone
19
+ self.instance_type = ec2_instance.instance_type
20
+ self.count = count
21
+ self.stack_name = nil
22
+ elsif ec2_instance.class.to_s == "Aws::EC2::Types::Instance"
23
+ self.id = ec2_instance.instance_id
24
+ self.name = nil
25
+ self.platform = platform_helper(ec2_instance)
26
+ self.availability_zone = ec2_instance.placement.availability_zone
27
+ self.instance_type = ec2_instance.instance_type
28
+ self.count = count
29
+ self.stack_name = nil
30
+
31
+ # go through to see if the tag we're looking for is one of them
32
+ if tag_name
33
+ ec2_instance.tags.each do |tag|
34
+ if tag.key == tag_name
35
+ self.tag_value = tag.value
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def to_s
43
+ "#{platform} #{availability_zone} #{instance_type}"
44
+ end
45
+
46
+ def self.get_instances(tag_name=nil)
47
+ return @instances if @instances
48
+ @instances = ec2.describe_instances.reservations.map do |reservation|
49
+ reservation.instances.map do |instance|
50
+ next unless instance.state.name == 'running'
51
+ new(instance, tag_name)
52
+ end.compact
53
+ end.flatten.compact
54
+ get_more_info
55
+ end
56
+
57
+ def self.get_reserved_instances
58
+ return @reserved_instances if @reserved_instances
59
+ @reserved_instances = ec2.describe_reserved_instances.reserved_instances.map do |ri|
60
+ next unless ri.state == 'active'
61
+ new(ri, nil, ri.instance_count)
62
+ end.compact
63
+ end
64
+
65
+ def no_reserved_instance_tag_value
66
+ @tag_value
67
+ end
68
+
69
+ def platform_helper(ec2_instance)
70
+ if ec2_instance.class.to_s == "Aws::EC2::Types::Instance"
71
+ if ec2_instance.vpc_id
72
+ return 'VPC'
73
+ elsif ec2_instance.platform
74
+ if ec2_instance.platform.downcase.include? 'windows'
75
+ return 'Windows'
76
+ else
77
+ return 'Linux'
78
+ end
79
+ else
80
+ return 'Linux'
81
+ end
82
+ elsif ec2_instance.class.to_s == "Aws::EC2::Types::ReservedInstances"
83
+ if ec2_instance.product_description.downcase.include? 'vpc'
84
+ return 'VPC'
85
+ elsif ec2_instance.product_description.downcase.include? 'windows'
86
+ return 'Windows'
87
+ else
88
+ return 'Linux'
89
+ end
90
+ end
91
+ end
92
+ private :platform_helper
93
+
94
+ def self.get_more_info
95
+ get_instances.each do |instance|
96
+ tags = ec2.describe_tags(:filters => [{:name => "resource-id", :values => [instance.id]}]).tags
97
+ tags = Hash[tags.map { |tag| [tag[:key], tag[:value]]}.compact]
98
+ instance.name = tags["Name"]
99
+ instance.stack_name = tags["opsworks:stack"]
100
+ end
101
+ end
102
+ private_class_method :get_more_info
103
+
104
+ def self.bucketize
105
+ buckets = {}
106
+ get_instances.map do |instance|
107
+ name = instance.stack_name || instance.name
108
+ if name
109
+ buckets[name] = [] unless buckets.has_key? name
110
+ buckets[name] << instance
111
+ else
112
+ puts "Could not sort #{instance.id}, as it has no stack_name or name"
113
+ end
114
+ end
115
+ buckets.sort_by{|k,v| k }
116
+ end
117
+
118
+ end
119
+ end