Skip to content

Instantly share code, notes, and snippets.

@takus
Forked from cognusion/riaudit.rb
Last active September 17, 2015 05:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takus/612a52c785dc875557cf to your computer and use it in GitHub Desktop.
Save takus/612a52c785dc875557cf to your computer and use it in GitHub Desktop.
Simple script to audit AWS EC2 Reserved Instances vs Instances running.
#!/usr/bin/env ruby
# Audits your reserved instances vs your running instances, and reports back.
# If you want to run this from an AWS instance, its IAM Role must be granted at least the following:
#
#{
# "Statement": [
# {
# "Action": [
# "ec2:DescribeInstances",
# "ec2:DescribeReservedInstances",
# ],
# "Effect": "Allow",
# "Resource": "*"
# }
# ]
# }
require 'rubygems'
require 'trollop' # Option parsing made stupid easy
require 'aws-sdk-v1'
class Hash
# Compares another Hash to this one, and returns a Hash of the differences
def deep_diff(b)
a = self
(a.keys | b.keys).inject({}) do |diff, k|
if a[k] != b[k]
if a[k].respond_to?(:deep_diff) && b[k].respond_to?(:deep_diff)
diff[k] = a[k].deep_diff(b[k])
else
diff[k] = [a[k], b[k]]
end
end
diff
end
end
end
class AZ
attr_accessor :name, :instances, :ris
def initialize(name)
@name = name
@instances = Hash.new
@ris = Hash.new
end
# Add a running instance (I)
def add_instance(entry)
@instances[entry] = @instances.has_key?(entry) ? @instances[entry] + 1 : 1
end
# Add a reserved instance (RI), and an optional number of them
def add_ris(entry, ricount = 1)
@ris[entry] = @ris.has_key?(entry) ? @ris[entry] + ricount : ricount
end
# Analyze the current AZ object, outputting relevant differences
def analyze(prefix = '', verbose = false)
@ris.deep_diff(@instances).sort.each {|key, value|
(ri,i) = value
ri = 0 if ri.nil?
i = 0 if i.nil?
d = i - ri
if verbose
puts ["#{prefix}#{key}", "Reserved Instances: #{ri}", "Instances Running: #{i}", "Difference: #{d}"].join("\t")
else
puts ["#{prefix}#{key}", "RI: #{ri}", "I: #{i}", "D: #{d}"].join("\t")
end
}
end
end
# Option parsing via Trollop
opts = Trollop::options do
opt :region, "AWS Region",
:type => String,
:default => 'us-east-1'
opt :verbose, "Spell out the variables"
opt :debug, "Be noisy"
end
# If you need to deal with keys/credentials, do that here.
ec2 = AWS::EC2.new(:region => opts[:region])
AZs = Hash.new
AWS.memoize do
# Collect active RIs, add them to the appropriate AZ object
ec2.reserved_instances.each {|ri|
next unless ri.state == 'active'
puts "[d] RI id: #{ri.id} #{ri.availability_zone} #{ri.instance_type} #{ri.instance_count}" if opts[:debug]
unless AZs.has_key?(ri.availability_zone)
AZs[ri.availability_zone] = AZ.new(ri.availability_zone)
end
AZs[ri.availability_zone].add_ris(ri.instance_type, ri.instance_count)
}
# Collect running instances, add them to the appropriate AZ object
ec2.instances.each {|i|
next unless i.status.to_s == 'running'
next if i.spot_instance?
puts "[d] I id: #{i.id} #{i.availability_zone} #{i.instance_type}" if opts[:debug]
unless AZs.has_key?(i.availability_zone)
AZs[i.availability_zone] = AZ.new(i.availability_zone)
end
AZs[i.availability_zone].add_instance(i.instance_type)
}
end
# Analyze each AZ
AZs.sort.each {|name, az|
puts "\nAZ: #{name}"
az.analyze("\t", opts[:verbose]) # we prefix a tab, for show
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment