aws_ami_cleanup 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|