Deregister an AWS AMI and remove associated S3 snapshots

Deregister AMIs and delete snapshots (click to enlarge)
Deregister AMIs and delete snapshots (click to enlarge)

Update 2016-04-28: I’ve written an updated version of the script below that offers a graphical interface for managing AMIs. Of course, the script below still works.

Recently, I wrote a PowerShell script that uses an EC2 instance’s Name tag to create an Amazon Machine Image (AMI) of that running instance. This post is about a bookend script to that one: the PowerShell script below deletes an EBS-backed AMI and all its associated S3 snapshots.

When you create an AMI, AWS creates a S3 snapshot of all of the instance’s EBS volumes. These snapshots (which are deltas of any previously existing snapshots, thereby saving on overall storage) are associated with the AMI that is created. When the AMI is launched as an instance, those snapshots are then used to re-create the EBS volumes of the original instance at that instance’s mount points and with all the data on the volume as of the date and time of the snapshot.

It’s all very cool. And I, like many AWS architects, live or die in my client engagements by how frequently I have an AMI to rollback to. At my current gig, I manage more than 35 EC2 instances, some containing terabytes of data. Just last week an AMI of a development environment I’d taken before an upgrade saved my bacon when the client suddenly decided to rollback that major upgrade.

But there’s a catch: when you delete (or as AWS calls it, “deregister”) an AMI, the snapshots are left behind. The AWS documentation describes this and recommends that you clean up after yourself. But they don’t make it easy. Their recommended procedure for removing leftover S3 snapshots has two really nasty flaws.

First, the AWS procedure for EBS-backed instance cleanup assumes you will do the cleanup right away. The problem is that if you do not record the name of the deregistered AMI and decide to come back later to cleanup the snapshots, you will have trouble finding them. The snapshots still have an ImageID set in their properties but the AMI itself has probably disappeared from the console. In any case, you are responsible for maintaining the link between the (now gone) AMI and the associated snapshots. IMO, this is very nasty and AWS should have fixed it a long time ago. It’s too manual and prone to error.

The second flaw is the one my other script corrects: when a snapshot is created for any given AMI its Name tag is left blank. That means if you deregister the AMI intending to come back later to delete the snapshots and you lose the AMI ID, you will have a hard time searching for them by anything other than the deleted AMIs ImageID. This is also something AWS should fix — or you can simply adapt my AMI-creation script to your needs to add tags to both the AMI itself and its associated snapshots. That way, if you delete the AMI but not the snapshots, you will have meaningful Name tags to search for snapshots to be deleted.

Unlike that script —  which I struggled to write — the one below was a breeze. It’s actually just two simple AWS PowerShell cmdlet calls wrapped in a couple of foreach loops plus some ham-handed confirmation dialogs. This script is designed to be run from the command line and thus requires the AMI IDs to be passed as command line parameters.

The entire script is below. Use the controls at the top of the code window to expand the window, copy the code , open it in a separate window or wrap the code in the code window. One other thing: the script assumes you have set up your AWS credentials in a profile that’s accessible to your PowerShell environment.

I hope you find this script useful and look forward to your feedback. If you’re a PowerShell guru, you can probably tell I’m still a relative newbie so any comments on style would be very much appreciated.

<# 
.SYNOPSIS 
    De-register AWS AMIs and cleanup (remove) S3 snapshots associated with the de-registered AMIs 

.DESCRIPTION
    Calls Get-EC2Image with supplied AMIs to obtain array of associated snapshots; de-registers (deletes) AMIs via Unregister-EC2Image 
        and then removes snapshots via Remove-EC2Snapshot
    
.USAGE
    .\RemoveAMIandCleanupAssociatedSnapshots.ps1 -$amis "ami-12345678", "ami-87654321", ...

.NOTES 
    A very simple script with little error checking but some "chattiness" with respect to confirmations. Note that if you wanted to
        avoid prompts confirming snapshot deletion, you could add -Force to Remove-EC2Snapshot

.AUTHOR
    Alex Neihaus 2014-10-28

.LICENSE
    (c) Air11 Technology LLC 2014
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

#> 

param( [Parameter(Mandatory=$true)][string[] ]$amis) # Insist on at least one AMI id

$array = @($amis)

Write-Host "The following AMIs will be deleted along with associated snapshots: $array"
$title = 'Are you sure?'
$prompt = '[C]ontinue or [E]xit?'
$continue = New-Object System.Management.Automation.Host.ChoiceDescription '&Continue','Continues'
$exit = New-Object System.Management.Automation.Host.ChoiceDescription '&Exit','Exits'
$options = [System.Management.Automation.Host.ChoiceDescription[]] ($continue,$exit)
$choice = $host.ui.PromptForChoice($title,$prompt,$options,0)

If ($choice -eq 1) {
    Write-Host "Exited"
    Exit
} # End if

foreach ($ami in $array) {

    $ami = Get-EC2Image -ImageId $ami
    $snapshots = @($ami.BlockDeviceMapping.ebs.snapshotid)

    Unregister-EC2Image -ImageId $ami.ImageId # Snapshots of root volumes cannot be deleted unless the AMI is deregistered first
    Write-Host $ami.ImageId "Unregistered"
        
    foreach ($SnapshotID in $snapshots) {
        Remove-EC2Snapshot $SnapshotID # Delete leftover snapshots
        Write-Host $SnapshotID "Removed"
    } # End foreach $snapshots

    
} # End foreach $ami

 

 


Posted

in

, , ,

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *