Amazon Ec2

特定のEBSに紐づくAMI・スナップショットをまとめて削除するPythonスクリプトを書いてみた

はじめに

こんにちは、omkです。
先日、1000以上のAMIがある環境で、特定のEBSに紐づくAMI・スナップショットを削除することになり、別のEBSに紐づくものと目視での判別が困難だったのでスクリプトを作成して処理しました。

余談ですが、自動で確認無しで全部消えてしまうのを避けたいのでRecycle Bin(ごみ箱)の保持ルールを設定することで、消える前に確認できるようにしていましたのでスクリプトで消しても安心でした。

スクリプト

Python3で動くスクリプトを作成しました。

#!/bin/python3.7
import boto3
import argparse
import time
import re

parser = argparse.ArgumentParser()

parser.add_argument("--delete_target") # expect snapshots / images / both
parser.add_argument("--vol_id") # expect vol-********
args = parser.parse_args()

client = boto3.client('ec2')
delete_target = args.delete_target
vol_id =  args.vol_id

# Define functions
def delete_snaps(snapids):
    for snap in snapids:
        try:
            response = client.delete_snapshot(
                SnapshotId = snap
            )
        except Exception as e:
            print("An error was occured when delete snapshot")
            print(e)
            exit(1)
        time.sleep(0.5)

def delete_images(images):
    for image in images['Images']:
        try:
            response = client.deregister_image(
                ImageId=image['ImageId'],
            )
        except Exception as e:
            print("An error was occured when deregister images")
            print(e)
            exit(1)
        time.sleep(0.5)

# Check argment
if delete_target not in ["snapshots", "images", "both"]:
    print("Check your delete_target is in ['snapshots', 'images', 'both']")
    exit(1)

if not re.fullmatch(r'vol-.+', vol_id):
    print("Check your vol_id is matching to 'vol-********'")
    exit(1)

# Get Snapshots
try:
    snaps = client.describe_snapshots(
        Filters=[
            {
                'Name': 'volume-id',
                'Values': [vol_id]
            }
        ],
    MaxResults=1000
    )
except Exception as e:
    print("An error was occured when get snapshots")
    print(e)
    exit(1)

# Check having Snapshots
if len(snaps['Snapshots']) == 0:
    print("Nopthing to do that there are no snapshots")
    exit(0)

# Get SnapshotIDs
snap_ids = []
for snap in snaps['Snapshots']: 
    snap_ids.append(snap['SnapshotId'])

# Delete Resources
if delete_target=="images" or delete_target == "both":
    try:
        images = client.describe_images(
            Filters=[
                    {
                        'Name': 'block-device-mapping.snapshot-id',
                        'Values': snap_ids
                    },
            ]
        )
    except Exception as e:
        print("An error was occured when get images")
        print(e)
        exit(1)

    if len(images['Images']) == 0:
        print("Nopthing to do that there are no images")
        exit(0)

    delete_images(images)
    print("All images are deleted")

if delete_target == "snapshots" or delete_target == "both":
    delete_snaps(snap_ids)
    print("All snapshots are deleted")
exit(0)

今回は、引数付きでコマンドラインから本スクリプトを実行することでボリュームIDの指定や削除対象の指定を行えるようにすることで汎用的?なスクリプトになるようにしてみました。

--delete_targetで、AMIだけを消すケース・スナップショットだけを消すケース・両方消すケースに対応しています。

実際に本スクリプトを利用して削除したときの流れとしては、

  1. AMIだけを削除
  2. 削除されたAMIの対象が正しいことを確認
  3. スナップショットを削除
  4. 削除されたスナップショットの対象が正しいことを確認

というフローを取りました。

AMIを消す際にはそれを利用しているEC2インスタンスが無いこと、スナップショットを消す際には紐づいているAMIが無いことが条件になるので分けて処理した方がエラー時に対応しやすいという意図でこのフローを取り、それに対応する目的で単体で消すパターン2つと両方一気に消すパターン1つの合計3パターンを用意しています。

処理の内容としては、入力されたボリュームIDに紐付くスナップショットIDを取得してスナップショットIDからAMIIDを取得、削除という内容になっています。

実行する際には

delete-snap.py --delete_target both --vol_id vol-0123456789abcdefg

みたいに実行します。

正常に終われば以下のように出力します。(対象リソースのIDの出力は一旦入れていないので必要に応じて)

All images are deleted
All snapshots are deleted

エラーが出たら例外処理でキャッチします。

おわりに

AWS Backupやスナップショットのライフサイクルを活用しつつ手動バックアップについてはこういう対応になるのかなと思っています。
Recycle Binが利用可能になったので万が一削除対象を誤っても復旧できるので便利ですね。

以上、最後までお付き合いありがとうございました。

返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA