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' | |
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).each_pair {|key, value| | |
(ri,i) = value | |
ri = 0 if ri.nil? | |
d = i - ri | |
if verbose | |
puts "#{prefix}#{key} Reserved Instances: #{ri} Instances Running: #{i} Difference: #{d}" | |
else | |
puts "#{prefix}#{key} RI: #{ri} I: #{i} D: #{d}" | |
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 | |
# 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' | |
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) | |
} | |
# Analyze each AZ | |
AZs.each_pair {|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