AWS CLI を使ってバージョニングが有効化された Amazon S3 バケットを空にしてみた

2022.10.23

いわさです。

Amazon S3 を使っているとバケットを削除したいシーンが出てくると思います。

マネジメントコンソールから削除を行うことが出来ますが S3 バケットではバケット内にファイルが存在している場合は削除することが出来ないので、まず「空にする」機能を使ってバケット内のファイルを削除する必要があります。

今回削除したいバケットの数が多かったのでこのバケットを空にする操作を AWS CLI で行おうとしたのですが、思いの外面倒だったので方法を共有しておきます。

公式ドキュメントでは AWS CLI の場合 s3 rm を使うように案内されている

まず、S3 バケットを空にする方法は公式ドキュメントでも案内されています。

ここでは以下のようにs3 rmコマンドを使ってファイルを削除する方法が案内されています。

% aws s3 rm s3://hoge1023bucket --recursive
delete: s3://hoge1023bucket/piyo1/hoge2.txt
delete: s3://hoge1023bucket/piyo2/hoge1.txt
delete: s3://hoge1023bucket/piyo2/hoge2.txt
delete: s3://hoge1023bucket/piyo1/hoge1.txt

削除されましたね。
バケットを削除してみましょう。

「このバケットは空ではありません」と表示され削除が出来ません。
確認したところ対象のバケットはバージョニングが有効化されていました。先程のコマンドでは旧バージョンのファイルまでは削除してくれません。

s3 rb コマンドでバケットの削除が出来ますが、force オプションを使うとバケット削除前にファイルを削除してくれます。
しかし、それも旧バージョンは対象外のようで使うことが出来ませんでした。

% aws s3 rb s3://hoge1023bucket
remove_bucket failed: s3://hoge1023bucket An error occurred (BucketNotEmpty) when calling the DeleteBucket operation: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
% aws s3 rb s3://hoge1023bucket --force
remove_bucket failed: s3://hoge1023bucket An error occurred (BucketNotEmpty) when calling the DeleteBucket operation: The bucket you tried to delete is not empty. You must delete all versions in the bucket.

一方で、マネジメントコンソールから「空にする」ボタンを使ってバケット内を空にした場合は旧バージョンファイルを含めて全て削除してくれていることがわかりました。

そこで、この「空にする」相当のことを AWS CLI でやってやれば良いのですが、どうやら AWS CLI の S3 コマンドでどうやら用意されていないようだということもわかりました。
マネジメントコンソールでの操作後に CloudTrail も見てみましたが「空にする」操作の履歴は確認出来ませんでした。DeleteBucket は確認出来たのですが。

AWS CLIでどうにかやってみる

AWS CLI では旧バージョンも含めてファイルを削除するs3api delete-objectsというコマンドがあります。

ただ、こちらは削除対象のファイル情報として以下の指定が必要で、配列で複数指定することで一括で削除することが可能ではあるのですがキーを明示的に指定する必要があります。パスを指定して再帰的に旧バージョン含めて全て削除してほしいところでしたがそれは出来ないようです。

{
  "Objects": [
    {
      "Key": "string",
      "VersionId": "string"
    }
    ...
  ],
  "Quiet": true|false
}

VersionId と Key の指定が必要ですが、旧バージョンを含めたファイルの一覧は以下のコマンドで取得することが出来ます。

% aws s3api list-object-versions --bucket hoge1021fuga4 
{
    "Versions": [
        {
            "ETag": "\"d6b3c89fb1fd34be8efa2c861fc2afaf\"",
            "Size": 11,
            "StorageClass": "STANDARD",
            "Key": "piyo1/hoge1.txt",
            "VersionId": "NOiRCfJgfdQAZGCi5fO.CYhraOwQ4wPo",
            "IsLatest": true,
            "LastModified": "2022-10-21T07:46:26+00:00",
            "Owner": {
                "DisplayName": "members-30312",
                "ID": "76c32f5081dc4906984229846e5ed7ee77257da3c7405e56f73f39afd6801fb5"
            }
        },
        {
            "ETag": "\"f4288da1c441491df98df891e8406cd1\"",
            "Size": 7,
            "StorageClass": "STANDARD",
            "Key": "piyo1/hoge1.txt",
            "VersionId": "TKCdCQc9YXCLzazKtu.ZCLhzd6.tVs1h",
            "IsLatest": false,
            "LastModified": "2022-10-21T07:45:58+00:00",
            "Owner": {
                "DisplayName": "members-30312",
                "ID": "76c32f5081dc4906984229846e5ed7ee77257da3c7405e56f73f39afd6801fb5"
            }
        },
        {
            "ETag": "\"5c9597f3c8245907ea71a89d9d39d08e\"",
            "Size": 4,
            "StorageClass": "STANDARD",
            "Key": "piyo1/hoge1.txt",
            "VersionId": "MdnMqTnkF86sK_axeAcQC65pphBUNLZA",
            "IsLatest": false,
            "LastModified": "2022-10-21T07:40:41+00:00",
            "Owner": {
                "DisplayName": "members-30312",
                "ID": "76c32f5081dc4906984229846e5ed7ee77257da3c7405e56f73f39afd6801fb5"
            }
        },
:
    ]
}

そこでこの2つのコマンドを組み合わせて、対象バケットの旧バージョンを含むファイル一式を削除してみました。
AWS CLI の他にjqで整形しているのでインストール済みであることが前提です。

% aws s3api delete-objects --bucket hoge1023bucket --delete "$(aws s3api list-object-versions --bucket hoge1023bucket | jq '.Versions | {Objects: map({Key, VersionId})}')"
{
    "Deleted": [
        {
            "Key": "piyo2/hoge2.txt",
            "VersionId": "PqH4oYssdzK4jXDIbQSqj6dlyqsJAtYr"
        },
        {
            "Key": "piyo2/hoge1.txt",
            "VersionId": "yK0gnnPEFmtycHjEoOojvioMtr9ETczf"
        },
        {
            "Key": "piyo2/hoge2.txt",
            "VersionId": ".R4UlHgnliO_x2wBmwmYghmOuGK0Y1VC"
        },
        {
            "Key": "piyo1/hoge2.txt",
            "VersionId": "pLLoFACAknjFSu1qw0fgjY2_nCE6fVu0"
        },
        {
            "Key": "piyo1/hoge1.txt",
            "VersionId": "bvrQQKErYiGEJhAziKUZb5bYXVfpJdkM"
        }
    ]
}

お、うまく削除出来ていそうです。
バケットを削除してみましょう。

まだバケットが空ではないと表示されてしまいました。
バケットを改めて確認してみると、削除マーカーが残っていました。

削除マーカーも残っているとバケットは削除できないということを知りました。

削除マーカーの考慮も必要

というわけで削除マーカーの考慮も必要なのですが、削除マーカー情報は通常のバージョニングされたファイルとは少し扱いが違っていて別の方法で取得出来ます。
ただし先程と同様のs3api list-object-versionsに結果として含まれています。

% aws s3api list-object-versions --bucket hoge1023bucket
{
    "Versions": [
        {
            "ETag": "\"c4ca4238a0b923820dcc509a6f75849b\"",
            "Size": 1,
            "StorageClass": "STANDARD",
            "Key": "piyo1/hoge1.txt",
            "VersionId": "lJiUOhxWeFKX8MAMDobPu04OUWR8kzLd",
            "IsLatest": false,
            "LastModified": "2022-10-23T01:32:28+00:00",
            "Owner": {
                "DisplayName": "members-30312",
                "ID": "76c32f5081dc4906984229846e5ed7ee77257da3c7405e56f73f39afd6801fb5"
            }
        },
:
        {
            "ETag": "\"c4ca4238a0b923820dcc509a6f75849b\"",
            "Size": 1,
            "StorageClass": "STANDARD",
            "Key": "piyo2/hoge2.txt",
            "VersionId": "7agqkN6ydIGNnO8kFYQm8uOpFWG6hacu",
            "IsLatest": false,
            "LastModified": "2022-10-23T01:32:28+00:00",
            "Owner": {
                "DisplayName": "members-30312",
                "ID": "76c32f5081dc4906984229846e5ed7ee77257da3c7405e56f73f39afd6801fb5"
            }
        }
    ],
    "DeleteMarkers": [
        {
            "Owner": {
                "DisplayName": "members-30312",
                "ID": "76c32f5081dc4906984229846e5ed7ee77257da3c7405e56f73f39afd6801fb5"
            },
            "Key": "piyo1/hoge1.txt",
            "VersionId": "YnyxshcqgaBMkyf6IdUfgbAs.KO9.dVk",
            "IsLatest": true,
            "LastModified": "2022-10-23T01:33:03+00:00"
        },
:
        {
            "Owner": {
                "DisplayName": "members-30312",
                "ID": "76c32f5081dc4906984229846e5ed7ee77257da3c7405e56f73f39afd6801fb5"
            },
            "Key": "piyo2/hoge2.txt",
            "VersionId": "aw5OEPFmSZJTxPfHgX2ujBG66mbE_ksO",
            "IsLatest": true,
            "LastModified": "2022-10-23T01:33:03+00:00"
        }
    ]
}

削除マーカーはVersionsではなくDeleteMarkersに出力されます。
削除マーカーのないバケットの場合はDeleteMarkers自体が出力されないので先程は気が付きませんでした。

取得方法がわかったので、あとは jq で DeleteMarkers もソースとして追加してやるだけで良いです。

% aws s3api delete-objects --bucket hoge1023bucket --delete "$(aws s3api list-object-versions --bucket hoge1023bucket | jq '.Versions + .DeleteMarkers | {Objects: map({Key, VersionId})}')"
{
    "Deleted": [
        {
            "Key": "piyo2/hoge2.txt",
            "VersionId": "aw5OEPFmSZJTxPfHgX2ujBG66mbE_ksO",
            "DeleteMarker": true,
            "DeleteMarkerVersionId": "aw5OEPFmSZJTxPfHgX2ujBG66mbE_ksO"
        },
        {
            "Key": "piyo1/hoge2.txt",
            "VersionId": "ft1dKEVbijA4hypVVTNxfDrbyi4TWmrg",
            "DeleteMarker": true,
            "DeleteMarkerVersionId": "ft1dKEVbijA4hypVVTNxfDrbyi4TWmrg"
        },
        {
            "Key": "piyo2/hoge1.txt",
            "VersionId": "MUZ.MBE7yJJMVbyG3yrcgzMyJQgTwGDw",
            "DeleteMarker": true,
            "DeleteMarkerVersionId": "MUZ.MBE7yJJMVbyG3yrcgzMyJQgTwGDw"
        },
        {
            "Key": "piyo1/hoge1.txt",
            "VersionId": "YnyxshcqgaBMkyf6IdUfgbAs.KO9.dVk",
            "DeleteMarker": true,
            "DeleteMarkerVersionId": "YnyxshcqgaBMkyf6IdUfgbAs.KO9.dVk"
        },
        {
            "Key": "piyo2/hoge2.txt",
            "VersionId": "17lfIe4tmKTzfeL8HaOfL7LjZOAK0wKR"
        },
        {
            "Key": "piyo2/hoge2.txt",
            "VersionId": "7agqkN6ydIGNnO8kFYQm8uOpFWG6hacu"
        },
        {
            "Key": "piyo1/hoge2.txt",
            "VersionId": "zgr1J1XRQ0uI2jCE9RgN88mcLHiH7_AB"
        },
        {
            "Key": "piyo1/hoge1.txt",
            "VersionId": "lJiUOhxWeFKX8MAMDobPu04OUWR8kzLd"
        },
        {
            "Key": "piyo2/hoge1.txt",
            "VersionId": "Qs_xhwb5EPPQsSFBWncaB8ETJDQTJoB."
        }
    ]
}

旧バージョンに加えて削除マーカーも削除されていることが確認出来ますね。
マネジメントコンソール上でも以下のように旧バージョンや削除マーカー含めて全てのファイルが削除されていることが確認出来ました。

さいごに

本日は AWS CLI を使ってバージョニングが有効化された Amazon S3 バケットを空にしてみました。

最初は「空にする」コマンドがありそうだと思ったのですが意外にも用意されておらず苦労しました。
さらに、削除マーカーについても通常のバージョニングされたファイルとは別みたいでそのあたりも実行してみてようやく気が付きました。