バージョニングが有効化された 大量のAmazon S3 バケットをシェルスクリプトで一撃削除してみた(ver.2)

大量のS3バケットを手動で削除するのはめんどくさい…シェルスクリプトで一撃削除しましょう。S3のオプジェクトが1000を超える場合に使えるシェルスクリプトです。
2023.04.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!AWS事業本部のおつまみです!

S3バケットを一撃で削除したいなぁと思ったことはありますか?私はあります。
そう思い昨日こちらのブログを執筆しました。

このブログのスクリプトでも問題なく動くのですが、とある方からTwitterでこちらのコメントをいただきました。

そしてアドバイスをいただきスクリプトを修正したのでver.2として公開したいと思います!

バージョニングが有効化された Amazon S3 バケットを削除するシェルスクリプト(ver.2)

修正したシェルスクリプトです。(修正箇所はハイライトがかかっている箇所になります。)

deleteS3.sh

#!/usr/bin/bash

bucket_name="$1"
page_size=999

# Abort the process if there is no argument
if [ $# != 1 ]; then
    echo "Please enter an argument."
    exit 1
fi

# Get the list of buckets to be deleted
backet_list=`aws s3api list-buckets | \
    jq -r ".Buckets[].Name" | \
    grep ${bucket_name} `

# There is no target bucket
if [ -z "${backet_list}" ]; then
    echo "There was no target bucket."
    exit 0
fi

# Confirm that you want to delete the bucket
echo "${backet_list}"
while true; do
    read -p "Do you want to delete this buckets? (y/n)" yn
    case $yn in
        [Yy]* ) break;;
        [Nn]* ) exit 0;;
        * ) echo "Please answer yes or no.";;
    esac
done

echo "Start deleting the bucket."


# Delete the bucket
echo "${backet_list}" | while read backet_name
do
    echo "Deleting ${backet_name} ..."
    next_token=""
    while true; do
        output=$(aws s3api list-object-versions --bucket "$bucket_name" --max-items "$page_size")
        output=$(aws s3api list-object-versions --bucket "$bucket_name" --max-items "$page_size" --starting-token "$next_token")
        delete_items=$(echo "$output" | jq '.Versions + .DeleteMarkers | {Objects: map({Key, VersionId}), Quiet: true}')
        count=$(echo "$delete_items" | jq '.Objects | length')
        next_token=$(echo "$output" | jq -r '.NextToken')

        if [ -z "$count" ] || [ "$count" -eq 0 ]; then
            echo "$bucket_name の全てのオブジェクトが削除されました。"
            break
        else
            aws s3api delete-objects --bucket "$bucket_name" --delete "$delete_items"
            echo "$bucket_name の$count 個のオブジェクトを削除しました。"
        fi

        if [ -z "$next_token" ] || [ "$next_token" == "null" ]; then
            break
        fi
        
    done
    aws s3 rb s3://$backet_name --force
done

echo "done"

こちらのシェルスクリプト、ほぼ先人(のんピさん)が作成されたものを流用させていただきました。ありがとうございました。

処理のフローは変わっていません。(のんピさんのブログより引用)

  • 引数として文字列を受け取る
    • 引数が1つじゃない = 正しいフォーマットではない場合は異常終了する
  • aws s3 api list-bucketsjqgrepで、引数として受け取った文字列がマッチするS3バケット名を取得する
    • マッチしたS3バケットが何もなければ正常終了する
  • マッチしたS3バケットの一覧を出力し、処理を続けるのか入力を受け付ける
    • 削除するのであればyesで、次の処理に進む
    • 削除しないのであれば、noで、正常終了する
    • yesもしくはno以外の文字列が入力された場合は、繰り返し確認する
  • マッチしたS3バケットを順に削除する

s3api delete-objectsの罠

aws s3api list-objectsは一度に指定出来るキーが最大 1,000 までという罠があります。
そのため、それ以上のオブジェクトを含むバケットの場合の考慮が必要でした。

AWS CLI のページ分割オプションの使用 - AWS Command Line Interface

デフォルトでは、AWS CLI は、個々のサービスによって決定されるページサイズを使用し、利用可能なすべての項目を取得します。例えば、Amazon S3 では、デフォルトのページサイズは 1,000 です。3,500 のオブジェクトを含む Amazon S3 バケットで aws s3api list-objectsを実行すると、AWS CLI は Simple Storage Service (Amazon S3) に対して 4 つの呼び出しを自動的に実行し、サービス固有の分割ロジックをバックグラウンドで処理して、最終的な出力で 3,500 オブジェクトのすべてを返します。

よってs3api list-objectsで最大 1,000 に引っかからないよう、オプション--page-sizeで1000未満を指定し、複数回delete-objectsでループするようにしました。 ちなみにこの指摘も先人(大瀧さん)が優しく教えてくれました。ありがとうございます。

そして修正はもちろんChatGPTにお願いしました。

実行してみた

Cloudshellから実行してみました。ログは以下のようになりました。

[cloudshell-user@ip-10-6-30-202 ~]$ sh deleteS3.sh aws-sam-cli-managed-default-samclisourcebucket-pz07v14ifkek
aws-sam-cli-managed-default-samclisourcebucket-pz07v14ifkek
Do you want to delete this buckets? (y/n)y
Start deleting the bucket.
Deleting aws-sam-cli-managed-default-samclisourcebucket-pz07v14ifkek ...

aws-sam-cli-managed-default-samclisourcebucket-pz07v14ifkek の80 個のオブジェクトを削除しました。
aws-sam-cli-managed-default-samclisourcebucket-pz07v14ifkek の全てのオブジェクトが削除されました。
remove_bucket: aws-sam-cli-managed-default-samclisourcebucket-pz07v14ifkek
done

オブジェクトが多いバケットでも問題なく削除できました!

さいごに

今回はバージョニングが有効化された Amazon S3 バケットをAWS CLIで一撃削除する方法(ver.2)をご紹介しました。

先人の方々のコメントでまた1つ学びが増えました。感謝です。

最後までお読みいただきありがとうございました!
どなたかのお役に立てれば幸いです。

以上、おつまみ(@AWS11077)でした!