aws_ami_cleanup 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +5 -0
- data/README.md +20 -0
- data/aws_ami_cleanup.gemspec +30 -0
- data/bin/cleanup_amis +9 -0
- data/lib/aws_ami_cleanup.rb +7 -0
- data/lib/aws_ami_cleanup/cleanup_amis.rb +115 -0
- data/lib/aws_ami_cleanup/commands.rb +28 -0
- data/lib/aws_ami_cleanup/version.rb +6 -0
- metadata +110 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: db5bf4d59de51127ee6dab8a547cce124a734dd1c628096e59509ea21dd572d4
|
4
|
+
data.tar.gz: 5a6aa3840422b64a71be65ce11ac2a290e4e46a2e5f8730865537ce8676c3368
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8f6000074b50a25f566478022a78a341b676a0fa10eb8cc7945043889035bf5b0b080bac0eede9748ce1886db13c0f92745f0fe2f0abe254acc2a80d93f95f16
|
7
|
+
data.tar.gz: 212429abd100adba8cf1be8cbda5a322237a5114ffba2114b3266bce54e1be56d5f1a1dad184f1d2a5245b9713f039063ab2c33f6359a3d506f1d0b3769fadf3
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
aws_ami_cleanup
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.6.6
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# AWS AMI Cleanup
|
2
|
+
|
3
|
+
This gem provides the `cleanup_amis` script that allows deregistering unused AMIs. The IAM user running the command must have at the very least the following permissions:
|
4
|
+
|
5
|
+
- ec2:DescribeImages
|
6
|
+
- ec2:DescribeLaunchTemplateVersions
|
7
|
+
- ec2:DescribeInstances
|
8
|
+
- ec2:DeregisterImage
|
9
|
+
- ec2:DeleteSnapshot
|
10
|
+
- autoscaling:DescribeAutoScalingGroups
|
11
|
+
|
12
|
+
Script should be invoked as follows:
|
13
|
+
|
14
|
+
```
|
15
|
+
cleanup_amis clean_amis --ami_name 'my-ami' --ami_owner 'self'
|
16
|
+
```
|
17
|
+
|
18
|
+
Where `ami_owner` can be a combination of AWS account IDs, `self`, `amazon`, and `aws-marketplace`.
|
19
|
+
|
20
|
+
Additionally you can provide the `number_of_amis_to_keep` argument to specify how many AMIs to keep (default is 3) and `region` for the AWS region (default is `us-east-1`).
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "aws_ami_cleanup/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "aws_ami_cleanup".freeze
|
9
|
+
s.version = AwsAmiCleanup::VERSION
|
10
|
+
|
11
|
+
s.authors = ["Diego Marcet"]
|
12
|
+
s.date = "2021-02-19"
|
13
|
+
s.summary = "Script for deleting obsolete AMIs"
|
14
|
+
s.email = "diego@controlshiftlabs.com"
|
15
|
+
s.executables = ["cleanup_amis"].freeze
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.homepage = "http://github.com/controlshift/aws_ami_cleanup"
|
18
|
+
s.license = "MIT"
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
s.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
23
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
24
|
+
end
|
25
|
+
|
26
|
+
s.add_runtime_dependency "aws-sdk-ec2", "~> 1"
|
27
|
+
s.add_runtime_dependency "aws-sdk-autoscaling", "~> 1"
|
28
|
+
s.add_runtime_dependency "thor", "~> 1"
|
29
|
+
s.add_development_dependency("byebug", "~> 11")
|
30
|
+
end
|
data/bin/cleanup_amis
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aws-sdk-ec2'
|
4
|
+
require 'aws-sdk-autoscaling'
|
5
|
+
|
6
|
+
module AwsAmiCleanup
|
7
|
+
class CleanupAmis
|
8
|
+
DEFAULT_NUMBER_OF_AMIS_TO_KEEP = 3
|
9
|
+
|
10
|
+
attr_accessor :region, :number_of_amis_to_keep
|
11
|
+
|
12
|
+
def initialize(region, number_of_amis_to_keep)
|
13
|
+
@region = region
|
14
|
+
|
15
|
+
@number_of_amis_to_keep = number_of_amis_to_keep || DEFAULT_NUMBER_OF_AMIS_TO_KEEP
|
16
|
+
if number_of_amis_to_keep <= 0
|
17
|
+
raise 'Number of AMIs to keep must be higher than 0.'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute!(ami_name:, ami_owner:)
|
22
|
+
potential_amis_to_remove = amis(ami_name, ami_owner)
|
23
|
+
ami_ids = potential_amis_to_remove.collect(&:image_id)
|
24
|
+
ami_ids_to_remove = ami_ids - amis_in_use
|
25
|
+
potential_amis_to_remove.keep_if {|a| ami_ids_to_remove.include?(a.image_id) }
|
26
|
+
|
27
|
+
if potential_amis_to_remove.count > number_of_amis_to_keep
|
28
|
+
amis_to_remove = potential_amis_to_remove[number_of_amis_to_keep..-1]
|
29
|
+
amis_to_keep = potential_amis_to_remove[0..(number_of_amis_to_keep-1)]
|
30
|
+
|
31
|
+
puts "Deregistering old AMIs..."
|
32
|
+
amis_to_remove.each do |ami|
|
33
|
+
ebs_mappings = ami.block_device_mappings
|
34
|
+
puts "Deregistering #{ami.image_id}"
|
35
|
+
ami.deregister
|
36
|
+
delete_ami_snapshots(ebs_mappings)
|
37
|
+
end
|
38
|
+
|
39
|
+
puts "Currently active AMIs..."
|
40
|
+
amis_to_keep.each do |ami|
|
41
|
+
puts ami.image_id
|
42
|
+
end
|
43
|
+
else
|
44
|
+
puts "no AMIs to clean."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def ec2
|
51
|
+
@__ec2 ||= Aws::EC2::Client.new(region: region)
|
52
|
+
end
|
53
|
+
|
54
|
+
def auto_scaling
|
55
|
+
@__auto_scaling ||= Aws::AutoScaling::Client.new(region: region)
|
56
|
+
end
|
57
|
+
|
58
|
+
def amis(ami_name, ami_owner)
|
59
|
+
return @__amis unless @__amis.nil?
|
60
|
+
|
61
|
+
# Cannot lookup by Name tag because that's only available from the owner account.
|
62
|
+
describe_images_params = { owners: [ ami_owner ] }
|
63
|
+
all_images_from_owner = ec2.describe_images(describe_images_params).images
|
64
|
+
name_matching_images = all_images_from_owner.filter {|i| i.name.match?(ami_name) }
|
65
|
+
|
66
|
+
@__amis = sort_by_created_at(name_matching_images)
|
67
|
+
end
|
68
|
+
|
69
|
+
def sort_by_created_at(collection)
|
70
|
+
# Returns items oredered by creation_date from newer to older
|
71
|
+
collection.sort {|a, b| DateTime.parse(b.creation_date) <=> DateTime.parse(a.creation_date) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def amis_in_use
|
75
|
+
image_ids = []
|
76
|
+
|
77
|
+
autoscaling_groups = auto_scaling.describe_auto_scaling_groups.auto_scaling_groups
|
78
|
+
|
79
|
+
# Find AMIs used by auto scaling groups with launch templates
|
80
|
+
launch_template_ids = autoscaling_groups.reject {|asg| asg.launch_template.nil? }
|
81
|
+
.collect {|asg| asg.launch_template.launch_template_id }
|
82
|
+
launch_template_ids.each do |launch_template_id|
|
83
|
+
image_ids << ec2.describe_launch_template_versions(launch_template_id: launch_template_id, max_results: 1)
|
84
|
+
.launch_template_versions
|
85
|
+
.first
|
86
|
+
.launch_template_data
|
87
|
+
.image_id
|
88
|
+
end
|
89
|
+
|
90
|
+
# Find AMIs used by auto scaling groups with launch configurations
|
91
|
+
launch_configuration_names = autoscaling_groups.filter {|asg| asg.launch_template.nil? }
|
92
|
+
.collect {|asg| asg.launch_configuration_name }
|
93
|
+
launch_configurations = auto_scaling.describe_launch_configurations(launch_configuration_names: launch_configuration_names).launch_configurations
|
94
|
+
image_ids += launch_configurations.map(&:image_id)
|
95
|
+
|
96
|
+
# Finally, find AMIs used by instances not belonging to auto scaling groups
|
97
|
+
ec2_reservations = ec2.describe_instances
|
98
|
+
image_ids += ec2_reservations.reservations.collect {|res| res.instances.map(&:image_id) }.flatten
|
99
|
+
|
100
|
+
image_ids.flatten
|
101
|
+
end
|
102
|
+
|
103
|
+
def delete_ami_snapshots(ebs_mappings)
|
104
|
+
ebs_mappings.each do |ebs_mapping|
|
105
|
+
# Skip ephimeral block devices
|
106
|
+
next if ebs_mapping.ebs.nil? || ebs_mapping.ebs.snapshot_id.nil?
|
107
|
+
|
108
|
+
snapshot_id = ebs_mapping.ebs.snapshot_id
|
109
|
+
puts "Deleting snapshot #{snapshot_id}"
|
110
|
+
snapshot = Aws::EC2::Snapshot.new(snapshot_id)
|
111
|
+
snapshot.delete
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AwsAmiCleanup
|
4
|
+
class Commands < Thor
|
5
|
+
desc "clean_amis", "delete unused AMIs owned by ami_owner with ami_name name"
|
6
|
+
option :ami_name, required: true
|
7
|
+
option :ami_owner, required: true
|
8
|
+
option :number_of_amis_to_keep, required: false
|
9
|
+
option :region, required: false
|
10
|
+
def clean_amis
|
11
|
+
cleanup_amis = AwsAmiCleanup::CleanupAmis.new(region, options[:number_of_amis_to_keep]&.to_i)
|
12
|
+
|
13
|
+
cleanup_amis.execute!(ami_name: options[:ami_name], ami_owner: options[:ami_owner])
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "console", "interactive session"
|
17
|
+
def console
|
18
|
+
require 'byebug'
|
19
|
+
byebug
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def region
|
25
|
+
options[:region] || 'us-east-1'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aws_ami_cleanup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Diego Marcet
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-02-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk-ec2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk-autoscaling
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: thor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '11'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '11'
|
69
|
+
description:
|
70
|
+
email: diego@controlshiftlabs.com
|
71
|
+
executables:
|
72
|
+
- cleanup_amis
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".ruby-gemset"
|
78
|
+
- ".ruby-version"
|
79
|
+
- Gemfile
|
80
|
+
- README.md
|
81
|
+
- aws_ami_cleanup.gemspec
|
82
|
+
- bin/cleanup_amis
|
83
|
+
- lib/aws_ami_cleanup.rb
|
84
|
+
- lib/aws_ami_cleanup/cleanup_amis.rb
|
85
|
+
- lib/aws_ami_cleanup/commands.rb
|
86
|
+
- lib/aws_ami_cleanup/version.rb
|
87
|
+
homepage: http://github.com/controlshift/aws_ami_cleanup
|
88
|
+
licenses:
|
89
|
+
- MIT
|
90
|
+
metadata: {}
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubygems_version: 3.0.8
|
107
|
+
signing_key:
|
108
|
+
specification_version: 4
|
109
|
+
summary: Script for deleting obsolete AMIs
|
110
|
+
test_files: []
|