-
-
Save acumenix/ef4f89946a679842d44b09728fe02115 to your computer and use it in GitHub Desktop.
Simple script to audit AWS EC2 Reserved Instances vs Instances running.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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