- AWS
S3バージョニングの有効化と復元 削除マーカーの消し方CLI
S3のバージョニングとは、同じキーへの上書きや削除のたびに以前の中身を「バージョン」として残しておく機能です。 有効にしておくと、上書きしたファイルを前の内容に戻したり、削除したファイルを「削除マーカー」を消すことで復元したりできます。
この記事では、有効化 → 上書き → 過去バージョンの取得 → 巻き戻し → 削除 → 復元の流れを AWS CLI と boto3 で順に実行し、各段階の list-object-versions の出力を載せます。削除マーカーの一括除去、バージョン付きバケットを空にするスクリプト、エラー文と原因の対応表もまとめました。
確認環境:Ubuntu 24.04、AWS CLI 1.46.1、Python 3.11.15、boto3 1.43.103、jq 1.7。AWS の API をローカルで再現するツール(moto 5.2.3)で実行しています。掲載するコマンドは、実際の AWS でそのまま使える形(エンドポイント指定なし)で書いています。バージョン ID は S3 が自動で割り振る文字列で、記事中の値は確認環境で発行されたものです。
S3バージョニングの早見表
| やりたいこと | CLI コマンド |
|---|---|
| バージョニングを有効にする | aws s3api put-bucket-versioning --bucket B --versioning-configuration Status=Enabled |
| 今の状態を確認する | aws s3api get-bucket-versioning --bucket B |
| 全バージョンと削除マーカーを一覧する | aws s3api list-object-versions --bucket B --prefix KEY |
| 過去バージョンをダウンロードする | aws s3api get-object --bucket B --key KEY --version-id ID 保存先 |
| 過去バージョンを最新に戻す(巻き戻し) | aws s3api copy-object --bucket B --key KEY --copy-source "B/KEY?versionId=ID" |
| 削除したファイルを復元する | 削除マーカーを aws s3api delete-object --bucket B --key KEY --version-id マーカーのID で消す |
| 特定のバージョンを完全に消す | aws s3api delete-object --bucket B --key KEY --version-id ID |
| バージョニングを止める | aws s3api put-bucket-versioning --bucket B --versioning-configuration Status=Suspended |
| 削除のしかた | 起きること | 元に戻せるか |
|---|---|---|
| バージョン ID を指定しない削除 | 削除マーカーが最新になり、見えなくなる。中身は残る | 戻せる(マーカーを消す) |
| バージョン ID を指定した削除 | その版が完全に消える | 戻せない |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「S3バージョニングの有効化と復元 削除マーカーの消し方CLI」
バージョニングを有効にする(put-bucket-versioning)
バケットの状態は「未設定」「Enabled(有効)」「Suspended(停止)」の3つです。新しく作ったバケットは未設定で、get-bucket-versioning は何も表示しません。
aws s3api get-bucket-versioning --bucket example-versioned-bucket
(出力なし)
有効にして、もう一度確認します。
aws s3api put-bucket-versioning --bucket example-versioned-bucket \
--versioning-configuration Status=Enabled
aws s3api get-bucket-versioning --bucket example-versioned-bucket
{
"Status": "Enabled"
}
一度有効にしたバケットは、未設定の状態には戻せません。 あとから選べるのは Suspended(停止)だけです。有効化より前からあったオブジェクトは、バージョン ID が null の版としてそのまま残ります。
上書きして、バージョン一覧を見る(list-object-versions)
同じキー report.txt に、内容を変えて3回アップロードします。
echo "version 1" > report.txt
aws s3api put-object --bucket example-versioned-bucket --key report.txt --body report.txt
{
"ETag": "\"81127ad129dd2249f5ab0667ca0aeb84\"",
"ChecksumCRC32": "zHmTaw==",
"VersionId": "4b320889-9fff-4990-b5eb-ac2d1ab4d823"
}
有効なバケットでは、アップロードのたびに VersionId が返ります。aws s3 cp でも同じようにバージョンが増えます。
echo "version 2 (long)" > report.txt
aws s3 cp report.txt s3://example-versioned-bucket/report.txt
echo "version 3" > report.txt
aws s3 cp report.txt s3://example-versioned-bucket/report.txt
一覧を取ります。そのままだと ETag や所有者なども出て長くなるので、--query で見たい項目に絞っています。以降の一覧はすべてこのコマンドの出力です。
aws s3api list-object-versions --bucket example-versioned-bucket --prefix report.txt \
--query '{Versions:Versions[].{VersionId:VersionId,IsLatest:IsLatest,Size:Size},DeleteMarkers:DeleteMarkers[].{VersionId:VersionId,IsLatest:IsLatest}}'
{
"Versions": [
{
"VersionId": "cb22e3ac-18bc-4704-9906-1a0b5761b06a",
"IsLatest": true,
"Size": 10
},
{
"VersionId": "e26de999-da11-4ee9-93a4-cc6c045c8a41",
"IsLatest": false,
"Size": 17
},
{
"VersionId": "4b320889-9fff-4990-b5eb-ac2d1ab4d823",
"IsLatest": false,
"Size": 10
}
],
"DeleteMarkers": null
}
| 項目 | 意味 |
|---|---|
Versions |
中身を持つバージョンの一覧。新しい順に並ぶ |
VersionId |
版を指定するための ID |
IsLatest |
true が今の最新。バージョン指定なしの GET で返るのはこの版 |
DeleteMarkers |
削除マーカーの一覧。1つもなければ null |
過去バージョンを取得する(get-object --version-id)
1回目にアップロードした版(一覧のいちばん下)をダウンロードします。
aws s3api get-object --bucket example-versioned-bucket --key report.txt \
--version-id 4b320889-9fff-4990-b5eb-ac2d1ab4d823 report_v1.txt \
--query '{VersionId:VersionId,ContentLength:ContentLength}'
cat report_v1.txt
{
"VersionId": "4b320889-9fff-4990-b5eb-ac2d1ab4d823",
"ContentLength": 10
}
version 1
この時点ではバケットの中は何も変わっていません。中身を確かめてから戻すかどうかを決められます。
上書きを巻き戻す(copy-object で過去版を最新にする)
過去バージョンを最新にするには、その版を同じキーへコピーします。 --copy-source の末尾に ?versionId= でコピー元の版を指定します。
aws s3api copy-object --bucket example-versioned-bucket --key report.txt \
--copy-source "example-versioned-bucket/report.txt?versionId=4b320889-9fff-4990-b5eb-ac2d1ab4d823" \
--query '{CopySourceVersionId:CopySourceVersionId,VersionId:VersionId}'
aws s3 cp s3://example-versioned-bucket/report.txt -
{
"CopySourceVersionId": "4b320889-9fff-4990-b5eb-ac2d1ab4d823",
"VersionId": "28de0657-00ba-4c78-912a-507d01e709ae"
}
version 1
一覧を見ると、コピーで作られた新しい版(28de0657…)が最新になり、これまでの3つの版もすべて残っています。
{
"Versions": [
{
"VersionId": "28de0657-00ba-4c78-912a-507d01e709ae",
"IsLatest": true,
"Size": 10
},
{
"VersionId": "cb22e3ac-18bc-4704-9906-1a0b5761b06a",
"IsLatest": false,
"Size": 10
},
{
"VersionId": "e26de999-da11-4ee9-93a4-cc6c045c8a41",
"IsLatest": false,
"Size": 17
},
{
"VersionId": "4b320889-9fff-4990-b5eb-ac2d1ab4d823",
"IsLatest": false,
"Size": 10
}
],
"DeleteMarkers": null
}
巻き戻しの方法は2つあります。
| 方法 | 操作 | 履歴 | 向いている場面 |
|---|---|---|---|
| コピーで戻す | copy-object で過去版を同じキーへコピー |
すべて残る(1つ増える) | 通常はこちら。あとで「戻したこと」も取り消せる |
| 新しい版を消して戻す | 最新の版を delete-object --version-id で消す |
消した版は残らない | 誤ってアップロードした版そのものを残したくないとき |
2つ目の方法は、記事後半の「バージョン指定ありの削除」で実行結果を載せます。
削除すると削除マーカーが付く(バージョン指定なしの削除)
バージョン ID を付けずに削除します。
aws s3api delete-object --bucket example-versioned-bucket --key report.txt
{
"DeleteMarker": true,
"VersionId": "1d5c534e-ce9c-445f-bff3-19e843af08de"
}
"DeleteMarker": true が返りました。中身は消えておらず、「削除された」という目印(削除マーカー)が最新版として1つ追加された状態です。 一覧はこうなります。
{
"Versions": [
{
"VersionId": "28de0657-00ba-4c78-912a-507d01e709ae",
"IsLatest": false,
"Size": 10
},
{
"VersionId": "cb22e3ac-18bc-4704-9906-1a0b5761b06a",
"IsLatest": false,
"Size": 10
},
{
"VersionId": "e26de999-da11-4ee9-93a4-cc6c045c8a41",
"IsLatest": false,
"Size": 17
},
{
"VersionId": "4b320889-9fff-4990-b5eb-ac2d1ab4d823",
"IsLatest": false,
"Size": 10
}
],
"DeleteMarkers": [
{
"VersionId": "1d5c534e-ce9c-445f-bff3-19e843af08de",
"IsLatest": true
}
]
}
Versions の4つはすべて IsLatest: false になり、DeleteMarkers に IsLatest: true のマーカーが入りました。この状態で普通に GET すると、オブジェクトが無いときと同じ応答になります。aws s3 ls にも表示されません。
aws s3api get-object --bucket example-versioned-bucket --key report.txt out.txt
An error occurred (NoSuchKey) when calling the GetObject operation: The specified key does not exist.
削除マーカーはキー名とバージョン ID だけを持ち、データは持ちません。公式ドキュメントでは、保存サイズはキー名の長さ分として数えられると説明されています。
削除マーカーを消して復元する(delete-object --version-id)
復元は、最新になっている削除マーカーを、バージョン ID を指定して削除するだけです。 マーカーの ID は --query で取り出せます。
DM=$(aws s3api list-object-versions --bucket example-versioned-bucket --prefix report.txt \
--query 'DeleteMarkers[?IsLatest].VersionId' --output text)
echo "$DM"
aws s3api delete-object --bucket example-versioned-bucket --key report.txt --version-id "$DM"
1d5c534e-ce9c-445f-bff3-19e843af08de
{
"VersionId": "1d5c534e-ce9c-445f-bff3-19e843af08de"
}
一覧を見ると、マーカーが無くなり、直前の版が最新に戻っています。
{
"Versions": [
{
"VersionId": "28de0657-00ba-4c78-912a-507d01e709ae",
"IsLatest": true,
"Size": 10
},
…(省略:残り3つは IsLatest が false のまま)…
],
"DeleteMarkers": null
}
aws s3 cp s3://example-versioned-bucket/report.txt -
version 1
--prefix report.txt は前方一致なので、report.txt.bak のようなキーがあると一緒に拾います。1つのキーだけを確実に指定したいときは、--query でキー名も条件に加えます。
aws s3api list-object-versions --bucket example-versioned-bucket --prefix report.txt \
--query 'DeleteMarkers[?Key==`report.txt` && IsLatest].VersionId' --output text
バージョン指定ありの削除は「完全な削除」
同じ delete-object でも、--version-id に中身を持つ版を指定すると、その版が完全に消えます。削除マーカーは付きません。
aws s3api delete-object --bucket example-versioned-bucket --key report.txt \
--version-id e26de999-da11-4ee9-93a4-cc6c045c8a41
{
"VersionId": "e26de999-da11-4ee9-93a4-cc6c045c8a41"
}
一覧から2回目の版(Size 17)が無くなりました。
{
"Versions": [
{
"VersionId": "28de0657-00ba-4c78-912a-507d01e709ae",
"IsLatest": true,
"Size": 10
},
{
"VersionId": "cb22e3ac-18bc-4704-9906-1a0b5761b06a",
"IsLatest": false,
"Size": 10
},
{
"VersionId": "4b320889-9fff-4990-b5eb-ac2d1ab4d823",
"IsLatest": false,
"Size": 10
}
],
"DeleteMarkers": null
}
続けて、最新の版(28de0657…)をバージョン指定で消すと、1つ前の版が最新に繰り上がります。 これが「新しい版を消して巻き戻す」方法です。
aws s3api delete-object --bucket example-versioned-bucket --key report.txt \
--version-id 28de0657-00ba-4c78-912a-507d01e709ae
aws s3 cp s3://example-versioned-bucket/report.txt -
{
"VersionId": "28de0657-00ba-4c78-912a-507d01e709ae"
}
version 3
{
"Versions": [
{
"VersionId": "cb22e3ac-18bc-4704-9906-1a0b5761b06a",
"IsLatest": true,
"Size": 10
},
{
"VersionId": "4b320889-9fff-4990-b5eb-ac2d1ab4d823",
"IsLatest": false,
"Size": 10
}
],
"DeleteMarkers": null
}
2つの削除の違いを整理します。
| バージョン指定なし | バージョン指定あり | |
|---|---|---|
| コマンド | delete-object --key KEY |
delete-object --key KEY --version-id ID |
| 応答 | "DeleteMarker": true と、マーカーの VersionId |
消した版の VersionId |
| 中身 | 残る | その版だけ完全に消える |
| 一覧の変化 | DeleteMarkers に1件増える |
Versions(またはマーカー)から1件減る |
| 取り消し | マーカーを消せば戻る | 取り消せない |
aws s3 rm での操作 |
こちらの動作になる | aws s3 rm では指定できない(s3api を使う) |
未設定・Enabled・Suspended の挙動比較表
| 操作 | 未設定 | Enabled(有効) | Suspended(停止) |
|---|---|---|---|
get-bucket-versioning の出力 |
出力なし | "Status": "Enabled" |
"Status": "Suspended" |
| PUT(同じキーに上書き) | 上書きされ、以前の中身は残らない | 新しいバージョン ID の版が増え、以前の版は残る | バージョン ID が null の版として保存。null の版が既にあればそれを上書きする |
| 有効だった頃の版 | ― | ― | そのまま残る |
| DELETE(バージョン指定なし) | 完全に消える | 削除マーカーが付く。中身は残る | バージョン ID null の削除マーカーが付く。null の版があればその中身は消える |
| DELETE(バージョン指定あり) | ― | その版を完全に消す | その版を完全に消す |
一覧での VersionId |
null |
版ごとの ID | 停止後に入れた版は null |
未設定と Enabled の列はこの記事の確認環境で実行した結果、Suspended の列は公式ドキュメントの説明に基づいています。未設定のバケットで同じキーに2回 PUT して一覧を取ると、次のように版は1つだけで、ID は null になります。
aws s3api list-object-versions --bucket example-plain-bucket \
--query 'Versions[].[Key,VersionId,IsLatest]' --output text
a.txt null True
Suspended で気を付けたいのは、停止中の削除は null の版の中身を消すという点です。有効だった頃に作られた版は残りますが、停止後にアップロードした内容は、バージョン指定なしの削除で戻せなくなります。
boto3 で同じ流れを実行する
有効化から復元、バージョン指定の削除までを1本にまとめたスクリプトです。
import boto3
from botocore.exceptions import ClientError
s3 = boto3.client("s3", region_name="ap-northeast-1")
BUCKET = "example-versioned-bucket"
KEY = "report.txt"
def show(title):
"""バージョンと削除マーカーを新しい順に表示する"""
res = s3.list_object_versions(Bucket=BUCKET, Prefix=KEY)
print(f"--- {title}")
for v in res.get("Versions", []):
print(" Version ", v["VersionId"][:8], "IsLatest =", v["IsLatest"])
for m in res.get("DeleteMarkers", []):
print(" DeleteMarker", m["VersionId"][:8], "IsLatest =", m["IsLatest"])
return res
# 1) 有効化
s3.put_bucket_versioning(
Bucket=BUCKET, VersioningConfiguration={"Status": "Enabled"}
)
print("Status:", s3.get_bucket_versioning(Bucket=BUCKET).get("Status"))
# 2) 同じキーに3回アップロード
ids = []
for i in (1, 2, 3):
r = s3.put_object(Bucket=BUCKET, Key=KEY, Body=f"version {i}\n".encode())
ids.append(r["VersionId"])
show("3回アップロードした直後")
# 3) 過去バージョンの中身を読む
body = s3.get_object(Bucket=BUCKET, Key=KEY, VersionId=ids[0])["Body"].read()
print("1つ目の版の中身:", body.decode().strip())
# 4) 過去バージョンを最新に戻す(コピーして新しい版を作る)
r = s3.copy_object(
Bucket=BUCKET, Key=KEY,
CopySource={"Bucket": BUCKET, "Key": KEY, "VersionId": ids[0]},
)
print("戻したあとの最新:", s3.get_object(Bucket=BUCKET, Key=KEY)["Body"].read().decode().strip())
# 5) バージョン指定なしで削除 → 削除マーカーが付く
r = s3.delete_object(Bucket=BUCKET, Key=KEY)
print("DeleteMarker:", r.get("DeleteMarker"), "/ VersionId:", r["VersionId"][:8])
res = show("バージョン指定なしで削除した直後")
try:
s3.get_object(Bucket=BUCKET, Key=KEY)
except ClientError as e:
print("GET:", e.response["Error"]["Code"])
# 6) 削除マーカーを消して復元
marker = [m for m in res["DeleteMarkers"] if m["IsLatest"]][0]
s3.delete_object(Bucket=BUCKET, Key=KEY, VersionId=marker["VersionId"])
show("削除マーカーを消した直後")
print("復元後の中身:", s3.get_object(Bucket=BUCKET, Key=KEY)["Body"].read().decode().strip())
# 7) バージョンIDを指定した削除は、その版を完全に消す
s3.delete_object(Bucket=BUCKET, Key=KEY, VersionId=ids[1])
show("2つ目の版をバージョン指定で削除した直後")
try:
s3.get_object(Bucket=BUCKET, Key=KEY, VersionId=ids[1])
except ClientError as e:
print("GET:", e.response["Error"]["Code"], "/", e.response["Error"]["Message"])
実行結果です(ID は先頭8文字だけ表示しています)。
Status: Enabled
--- 3回アップロードした直後
Version 8babfd1c IsLatest = True
Version ba972eb9 IsLatest = False
Version 665a3069 IsLatest = False
1つ目の版の中身: version 1
戻したあとの最新: version 1
DeleteMarker: True / VersionId: 991e0ece
--- バージョン指定なしで削除した直後
Version 3e34b41b IsLatest = False
Version 8babfd1c IsLatest = False
Version ba972eb9 IsLatest = False
Version 665a3069 IsLatest = False
DeleteMarker 991e0ece IsLatest = True
GET: NoSuchKey
--- 削除マーカーを消した直後
Version 3e34b41b IsLatest = True
Version 8babfd1c IsLatest = False
Version ba972eb9 IsLatest = False
Version 665a3069 IsLatest = False
復元後の中身: version 1
--- 2つ目の版をバージョン指定で削除した直後
Version 3e34b41b IsLatest = True
Version 8babfd1c IsLatest = False
Version 665a3069 IsLatest = False
GET: NoSuchVersion / The specified version does not exist.
CLI との書き方の違いは、コピー元の指定です。CLI は "バケット/キー?versionId=ID" という文字列、boto3 は CopySource={"Bucket": …, "Key": …, "VersionId": …} という辞書で渡します。
削除マーカーを一括で消して復元するスクリプト
フォルダごと削除してしまったときは、キーごとに1件ずつ戻すより、まとめて処理するほうが確実です。docs/ 以下の3ファイルを aws s3 rm --recursive で消した状態から始めます。
aws s3 ls s3://example-versioned-bucket/ --recursive
2026-10-05 15:07:30 10 img/logo.png
2026-10-05 15:07:27 10 report.txt
--query だけで書く(1,000件まで)
list-object-versions の出力を、delete-objects がそのまま受け取れる形(Objects の配列)に --query で整えます。
aws s3api list-object-versions --bucket example-versioned-bucket --prefix docs/ \
--query '{Objects: DeleteMarkers[?IsLatest].{Key:Key,VersionId:VersionId}}' \
--output json > markers.json
cat markers.json
{
"Objects": [
{
"Key": "docs/a.txt",
"VersionId": "546fdbc9-50bd-43fc-ae65-2e7974f0ea42"
}
]
}
(上の出力は、説明用に docs/a.txt だけを削除した状態で取ったものです。)消す前に markers.json を目で確認できるのがこの方法の良いところです。問題なければ渡します。
aws s3api delete-objects --bucket example-versioned-bucket --delete file://markers.json
{
"Deleted": [
{
"Key": "docs/a.txt",
"VersionId": "546fdbc9-50bd-43fc-ae65-2e7974f0ea42"
}
]
}
IsLatest で絞っているのは、最新になっているマーカーだけを消せば復元できるからです。同じキーを「削除 → 再アップロード → 削除」と繰り返すと過去のマーカーも残りますが、それらは復元には関係しません。
jq で1,000件ずつに分ける(件数が多いとき)
delete-objects に1回で渡せるのは1,000件までです。件数が多いときは jq で分割します。restore_markers.sh として保存します。
#!/bin/bash
# 使い方: ./restore_markers.sh バケット名 プレフィックス
BUCKET="$1"
PREFIX="$2"
aws s3api list-object-versions --bucket "$BUCKET" --prefix "$PREFIX" --output json \
| jq -c '[.DeleteMarkers[]? | select(.IsLatest) | {Key, VersionId}]
| select(length > 0) | _nwise(1000) | {Objects: .}' \
| while read -r batch; do
aws s3api delete-objects --bucket "$BUCKET" --delete "$batch" \
--query 'Deleted[].Key' --output text
done
echo "done"
bash restore_markers.sh example-versioned-bucket docs/
aws s3 ls s3://example-versioned-bucket/ --recursive
docs/a.txt docs/b.txt docs/c.txt
done
2026-10-05 15:07:28 10 docs/a.txt
2026-10-05 15:07:28 10 docs/b.txt
2026-10-05 15:07:29 10 docs/c.txt
2026-10-05 15:07:30 10 img/logo.png
2026-10-05 15:07:27 10 report.txt
3つのファイルが一覧に戻りました。select(length > 0) は、対象が0件のときに空の配列を delete-objects に渡さないための行です。これが無いと、0件のときに次のエラーで止まります。
An error occurred (MalformedXML) when calling the DeleteObjects operation: The XML you provided was not well-formed or did not validate against our published schema
対象が無い状態でもう一度実行すると、done だけが表示されます。
バージョン付きバケットを空にするスクリプト
バージョニングが有効なバケットは、aws s3 rm --recursive で全部消したつもりでも、削除マーカーが付くだけで全バージョンが残っています。そのためバケットの削除はエラーになります。
aws s3 rm s3://example-versioned-bucket/ --recursive --quiet
aws s3api delete-bucket --bucket example-versioned-bucket
An error occurred (BucketNotEmpty) when calling the DeleteBucket operation: The bucket you tried to delete is not empty
空にするには、すべてのバージョンとすべての削除マーカーを、バージョン ID を指定して消します。 empty_bucket.sh として保存します。この操作は取り消せないので、バケット名をよく確かめてから実行してください。
#!/bin/bash
# 使い方: ./empty_bucket.sh バケット名
BUCKET="$1"
aws s3api list-object-versions --bucket "$BUCKET" --output json \
| jq -c '[(.Versions[]?, .DeleteMarkers[]?) | {Key, VersionId}]
| select(length > 0) | _nwise(1000) | {Objects: .}' \
| while read -r batch; do
aws s3api delete-objects --bucket "$BUCKET" --delete "$batch" \
--query 'length(Deleted)' --output text
done
aws s3api list-object-versions --bucket "$BUCKET" \
--query '{Versions: length(Versions || `[]`), DeleteMarkers: length(DeleteMarkers || `[]`)}'
bash empty_bucket.sh example-versioned-bucket
aws s3api delete-bucket --bucket example-versioned-bucket
10
{
"Versions": 0,
"DeleteMarkers": 0
}
5つのバージョンと5つの削除マーカー、合わせて10件が消え、そのあとの delete-bucket はエラーなく完了しました。
boto3 版(一括復元と、空にする処理)
import boto3
BUCKET = "example-versioned-bucket"
s3 = boto3.client("s3", region_name="ap-northeast-1")
def remove_delete_markers(bucket, prefix=""):
"""最新が削除マーカーになっているキーを、マーカーを消して復元する"""
restored = []
pages = s3.get_paginator("list_object_versions").paginate(Bucket=bucket, Prefix=prefix)
for page in pages:
targets = [
{"Key": m["Key"], "VersionId": m["VersionId"]}
for m in page.get("DeleteMarkers", [])
if m["IsLatest"]
]
if targets: # 1ページは最大1000件なので、そのまま delete_objects に渡せる
res = s3.delete_objects(Bucket=bucket, Delete={"Objects": targets})
restored += [d["Key"] for d in res.get("Deleted", [])]
return restored
def empty_bucket(bucket):
"""全バージョンと全削除マーカーを消して、バケットを空にする"""
boto3.resource("s3", region_name="ap-northeast-1").Bucket(bucket).object_versions.delete()
def count(bucket):
res = s3.list_object_versions(Bucket=bucket)
return len(res.get("Versions", [])), len(res.get("DeleteMarkers", []))
print("復元したキー:", remove_delete_markers(BUCKET, "docs/"))
print("現在の一覧:", [o["Key"] for o in s3.list_objects_v2(Bucket=BUCKET).get("Contents", [])])
print("空にする前 (Versions, DeleteMarkers):", count(BUCKET))
empty_bucket(BUCKET)
print("空にした後 (Versions, DeleteMarkers):", count(BUCKET))
s3.delete_bucket(Bucket=BUCKET)
print("バケットを削除しました")
復元したキー: ['docs/a.txt', 'docs/b.txt', 'docs/c.txt']
現在の一覧: ['docs/a.txt', 'docs/b.txt', 'docs/c.txt', 'img/logo.png', 'report.txt']
空にする前 (Versions, DeleteMarkers): (5, 0)
空にした後 (Versions, DeleteMarkers): (0, 0)
バケットを削除しました
boto3 の resource には object_versions.delete() があり、全バージョンと全マーカーの削除を1行で書けます。
エラー文と原因の対応表
| エラー文 | 出る場面 | 対応 |
|---|---|---|
NoSuchKey … The specified key does not exist. |
最新が削除マーカーのキーを、バージョン指定なしで GET した | list-object-versions でマーカーを確認し、消して復元する |
NoSuchVersion … The specified version does not exist. |
既に消したバージョン ID を指定して GET した | 一覧から今ある VersionId を取り直す |
(405) … Method Not Allowed |
削除マーカーのバージョン ID を指定して取得しようとした | マーカーは中身を持たない。Versions 側の ID を指定する |
BucketNotEmpty |
バージョンやマーカーが残ったままバケットを削除した | 全バージョンと全マーカーを消してから削除する |
MalformedXML(DeleteObjects) |
Objects が空の配列だった |
対象が0件のときは delete-objects を呼ばない |
NoSuchVersion は、先ほど完全に消した2回目の版を指定すると再現できます。
aws s3api get-object --bucket example-versioned-bucket --key report.txt \
--version-id e26de999-da11-4ee9-93a4-cc6c045c8a41 out.txt
An error occurred (NoSuchVersion) when calling the GetObject operation: The specified version does not exist.
削除マーカーの ID を指定した head-object は 405 になります。
aws s3api head-object --bucket example-versioned-bucket --key report.txt --version-id "$DM"
An error occurred (405) when calling the HeadObject operation: Method Not Allowed
公式ドキュメントでは、削除マーカーへのアクセスについて次のように説明されています。
| リクエスト | 応答 |
|---|---|
| バージョン ID なしの GET / HEAD(最新が削除マーカー) | 404 Not Found。応答ヘッダーに x-amz-delete-marker: true |
| 削除マーカーのバージョン ID を指定した GET / HEAD | 405 Method Not Allowed。応答ヘッダーに x-amz-delete-marker: true |
| 削除マーカーのバージョン ID を指定した DELETE | マーカーが消える(復元の操作) |
削除マーカーに対してできる操作は DELETE だけ、と覚えておくと迷いません。
よくある質問
Q. バージョニングを有効にする前に消したファイルは復元できますか? A. 復元できません。バージョンが残るのは、有効にしたあとの上書きと削除だけです。大切なバケットは、使い始める時点で有効にしておくと安心です。
Q. 削除マーカーを消すのと、過去バージョンをコピーするのは、どちらで復元すればよいですか?
A. 削除したものを戻すなら、削除マーカーを消すのが手軽です(履歴が削除前の状態に戻ります)。上書きしたものを前の内容に戻すなら、copy-object で過去バージョンをコピーします。
Q. バージョンが増え続けると容量はどうなりますか?
A. すべてのバージョンが保存容量として数えられます。ライフサイクル設定の NoncurrentVersionExpiration で「最新でなくなってから N 日たった版を消す」ルールを入れておくと、自動で整理できます。
Q. 有効にしたバージョニングを元の未設定に戻せますか? A. 戻せません。選べるのは Suspended(停止)です。停止しても、それまでに作られたバージョンは残り、バージョン ID を指定すれば取得できます。
まとめ
- 有効化は
put-bucket-versioning --versioning-configuration Status=Enabled。一度有効にすると未設定には戻らず、止めるときは Suspended にする list-object-versionsのVersions・DeleteMarkers・IsLatestを見れば、今どの版が最新かが分かる- 上書きの巻き戻しは
copy-objectの--copy-sourceに?versionId=を付けて同じキーへコピー。履歴はすべて残る - バージョン指定なしの削除は削除マーカーが付くだけ。マーカーを
delete-object --version-idで消すと復元できる - バージョン指定ありの削除は完全な削除で、取り消せない
- 一括復元は
IsLatestが true の削除マーカーだけをdelete-objectsに渡す。0件のときは呼ばない - バケットを空にするには、全バージョンと全削除マーカーを消す。boto3 なら
object_versions.delete()の1行
参考・出典
- Amazon S3 ユーザーガイド:Retaining multiple versions of objects with S3 Versioning
- Amazon S3 ユーザーガイド:Working with delete markers
- Amazon S3 ユーザーガイド:Adding objects to versioning-suspended buckets
- Amazon S3 ユーザーガイド:Deleting objects from versioning-suspended buckets
- Amazon S3 API リファレンス:DeleteObjects
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「S3バージョニングの有効化と復元 削除マーカーの消し方CLI」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。