バージョニングを有効化する
--versioning-configurationにStatus=Enabledを指定する。
$ aws s3api put-bucket-versioning --bucket example-bucket --versioning-configuration Status=Enabled
成功しても出力はない。
有効化の直後は設定の伝播に時間がかかる。
伝播中は、有効化後に作成・更新したオブジェクトへのGETリクエストがHTTP 404 NoSuchKeyになる場合がある。
AWSのドキュメントでは、有効化から15分待ってから書き込みや削除に進むよう推奨している。
レプリケーションやObject Lockもバージョニングの有効化が前提である。
バージョニングの状態を確認する
get-bucket-versioningで現在の状態を確認する。
$ aws s3api get-bucket-versioning --bucket example-bucket
{
"Status": "Enabled"
}
一度もバージョニングを設定していないバケットでは、レスポンス自体が空になる。
$ aws s3api get-bucket-versioning --bucket example-bucket
$
エラーではなく終了コードは0のため、出力の有無で判定する。
--query Status --output textで値だけを取り出す場合、未設定のバケットでは文字列Noneが返る。
$ aws s3api get-bucket-versioning --bucket example-bucket --query Status --output text
None
空文字列ではなくNoneが返るため、スクリプトで判定する際はNoneとの比較で書ける。
バージョンの一覧を確認する
バージョニングが有効なバケットでは、同じキーへ上書きするたびに新しいVersionIdが振られる。list-object-versionsでキーごとの世代を確認する。
$ aws s3api list-object-versions --bucket example-bucket
{
"Versions": [
{
"ETag": "\"e30260020baeb0398ff07b37dd33ed16\"",
"ChecksumAlgorithm": [
"CRC64NVME"
],
"ChecksumType": "FULL_OBJECT",
"Size": 3,
"StorageClass": "STANDARD",
"Key": "example.txt",
"VersionId": "U5HvIlzkuLTVpDS9Xrk6m0lBFnrzGo9c",
"IsLatest": true,
"LastModified": "2026-08-01T03:05:16+00:00",
"Owner": {
"ID": "4997ce1a32cd1c916c78d870d4f4ddea30a6280177e5566c52014cdbb8841dfd"
}
},
{
"ETag": "\"4f98f59e877ecb84ff75ef0fab45bac5\"",
"ChecksumAlgorithm": [
"CRC64NVME"
],
"ChecksumType": "FULL_OBJECT",
"Size": 3,
"StorageClass": "STANDARD",
"Key": "example.txt",
"VersionId": "CHEtfwwY.F4LUL0sTcfMEKFxTc.90HBq",
"IsLatest": false,
"LastModified": "2026-08-01T03:05:15+00:00",
"Owner": {
"ID": "4997ce1a32cd1c916c78d870d4f4ddea30a6280177e5566c52014cdbb8841dfd"
}
}
],
"RequestCharged": null,
"Prefix": ""
}
IsLatestがtrueのバージョンがaws s3 cpやget-objectで取得される現行バージョンである。
出力が長いため、必要な項目だけをテーブル形式で表示すると読みやすい。
$ aws s3api list-object-versions --bucket example-bucket --query 'Versions[].[Key,VersionId,IsLatest,LastModified]' --output table
-------------------------------------------------------------------------------------------
| ListObjectVersions |
+-------------+------------------------------------+--------+-----------------------------+
| example.txt| U5HvIlzkuLTVpDS9Xrk6m0lBFnrzGo9c | True | 2026-08-01T03:05:16+00:00 |
| example.txt| CHEtfwwY.F4LUL0sTcfMEKFxTc.90HBq | False | 2026-08-01T03:05:15+00:00 |
+-------------+------------------------------------+--------+-----------------------------+
特定のキーだけを確認する場合は--prefixを指定する。
$ aws s3api list-object-versions --bucket example-bucket --prefix example.txt
過去のバージョンを取得する
get-objectに--version-idを指定すると、過去のバージョンをダウンロードできる。
$ aws s3api get-object --bucket example-bucket --key example.txt --version-id CHEtfwwY.F4LUL0sTcfMEKFxTc.90HBq old.txt
{
"AcceptRanges": "bytes",
"LastModified": "2026-08-01T03:05:15+00:00",
"ContentLength": 3,
"ETag": "\"4f98f59e877ecb84ff75ef0fab45bac5\"",
"ChecksumCRC64NVME": "FfdqN7gXyuY=",
"ChecksumType": "FULL_OBJECT",
"VersionId": "CHEtfwwY.F4LUL0sTcfMEKFxTc.90HBq",
"ContentType": "text/plain",
"ServerSideEncryption": "AES256",
"Metadata": {}
}
aws s3 cpには--version-idがないため、バージョンを指定する場合はs3api get-objectを使う。
過去のバージョンを現行に戻す
誤った上書きから復旧する場合は、過去のバージョンを同じキーへコピーする。--copy-sourceにバケット名とキーを渡し、?versionId=で戻したいバージョンを指定する。
$ aws s3api copy-object --bucket example-bucket --key example.txt \
--copy-source "example-bucket/example.txt?versionId=CHEtfwwY.F4LUL0sTcfMEKFxTc.90HBq"
{
"CopySourceVersionId": "CHEtfwwY.F4LUL0sTcfMEKFxTc.90HBq",
"VersionId": "w8Rcri.XWN3IH7v7U0_ip5b6Y29WYd0Y",
"ServerSideEncryption": "AES256",
"CopyObjectResult": {
"ETag": "\"4f98f59e877ecb84ff75ef0fab45bac5\"",
"LastModified": "2026-08-01T03:36:44+00:00",
"ChecksumType": "FULL_OBJECT",
"ChecksumCRC64NVME": "FfdqN7gXyuY="
}
}
コピーの結果は新しいバージョンとして追加され、コピー元のバージョンもそのまま残る。
古いバージョンを削除して戻すのではないため、復旧に失敗しても元の状態を保てる。
削除マーカーから復元する
バージョニングが有効なバケットでaws s3 rmを実行しても、実体は削除されない。
削除マーカーが最新バージョンとして追加され、通常の一覧やダウンロードから見えなくなるだけである。
$ aws s3 rm s3://example-bucket/example.txt
delete: s3://example-bucket/example.txt
$ aws s3 ls s3://example-bucket/
$
list-object-versionsで確認すると、削除マーカーがDeleteMarkersとして現れる(ETagなどは省略)。
{
"Versions": [
{
"Key": "example.txt",
"VersionId": "U5HvIlzkuLTVpDS9Xrk6m0lBFnrzGo9c",
"IsLatest": false,
"LastModified": "2026-08-01T03:05:16+00:00"
},
{
"Key": "example.txt",
"VersionId": "CHEtfwwY.F4LUL0sTcfMEKFxTc.90HBq",
"IsLatest": false,
"LastModified": "2026-08-01T03:05:15+00:00"
}
],
"DeleteMarkers": [
{
"Owner": {
"ID": "4997ce1a32cd1c916c78d870d4f4ddea30a6280177e5566c52014cdbb8841dfd"
},
"Key": "example.txt",
"VersionId": "ZIwg7QBTeA5IHnKDtyWEnYP3Bz4RhBNr",
"IsLatest": true,
"LastModified": "2026-08-01T03:05:17+00:00"
}
],
"RequestCharged": null,
"Prefix": ""
}
削除マーカーのVersionIdを指定してdelete-objectを実行すると、直前の状態に戻る。
$ aws s3api delete-object --bucket example-bucket --key example.txt --version-id ZIwg7QBTeA5IHnKDtyWEnYP3Bz4RhBNr
{
"DeleteMarker": true,
"VersionId": "ZIwg7QBTeA5IHnKDtyWEnYP3Bz4RhBNr"
}
$ aws s3 ls s3://example-bucket/
2026-08-01 12:05:16 3 example.txt
なお--version-idに実体のバージョンを指定した場合、そのバージョンは完全に削除される。
復元目的で実行する際は、削除マーカーのVersionIdを指定しているかを確認する。
バケット全体を特定時点に戻したい場合は、 【AWS】s3-pit-restoreでS3を特定時点にまとめて復元する のようなツールを使うと、キーごとの復元作業をまとめて実行できる。
バージョニングを停止する
Status=Suspendedを指定するとバージョニングを停止できる。
$ aws s3api put-bucket-versioning --bucket example-bucket --versioning-configuration Status=Suspended
$ aws s3api get-bucket-versioning --bucket example-bucket
{
"Status": "Suspended"
}
一度有効にしたバケットを未設定の状態には戻せない。
選べる値はEnabledとSuspendedのみである。
停止後もすでに保存されたバージョンは残り続ける。
停止中に追加したオブジェクトはVersionIdがnullになり、次の上書きでnullバージョンが置き換えられる。
停止した状態で同じキーへ2回上書きしてみる。
$ aws s3 cp example.txt s3://example-bucket/example.txt
upload: ./example.txt to s3://example-bucket/example.txt
$ aws s3 cp example.txt s3://example-bucket/example.txt
upload: ./example.txt to s3://example-bucket/example.txt
$ aws s3api list-object-versions --bucket example-bucket --query 'Versions[].[Key,VersionId,IsLatest]' --output table
--------------------------------------------------------------
| ListObjectVersions |
+--------------+------------------------------------+--------+
| example.txt | null | True |
| example.txt | U5HvIlzkuLTVpDS9Xrk6m0lBFnrzGo9c | False |
| example.txt | CHEtfwwY.F4LUL0sTcfMEKFxTc.90HBq | False |
+--------------+------------------------------------+--------+
2回上書きしてもnullバージョンは1つしか残らない。
停止中の上書きでは世代が増えないため、過去のバージョンは残るが新しい変更は保護されない。
ストレージコストを抑える目的であれば、停止よりも後述のライフサイクルルールでの世代管理が適している。
すべてのバケットの状態をまとめて確認する
list-bucketsと組み合わせると、アカウント内のバケットのバージョニング状態を一覧できる。
$ for bucket in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
versioning=$(aws s3api get-bucket-versioning --bucket "$bucket" --query Status --output text 2>/dev/null)
echo "$bucket ${versioning:-Denied}"
done
example-bucket Enabled
example-log-bucket None
example-static-bucket Suspended
example-deploy-bucket Enabled
未設定のバケットはNoneと表示される。2>/dev/nullで捨てているのは権限不足などのエラーである。
エラー時は変数が空になるためDeniedと表示される。
zshでスクリプトを書く場合、statusは読み取り専用の変数のため代入に失敗する。
上記のように別の変数名を使う。
MFA Deleteを有効にする
MFA Deleteを有効にすると、バージョンの完全削除とバージョニング状態の変更にMFAの認証コードが必要になる。
有効化はバケットを作成したAWSアカウントのルートユーザーのみが実行でき、マネジメントコンソールからは設定できない。
$ aws s3api put-bucket-versioning --bucket example-bucket \
--versioning-configuration Status=Enabled,MFADelete=Enabled \
--mfa "arn:aws:iam::123456789012:mfa/root-account-mfa-device 123456"
--mfaにはMFAデバイスのシリアル番号と6桁のコードをスペース区切りで渡す。
仮想MFAデバイスの場合、シリアル番号はデバイスのARNである。
IAMユーザーの認証情報で実行するとAccessDeniedになる。
aws: [ERROR]: An error occurred (AccessDenied) when calling the PutBucketVersioning operation: Mfa Authentication must be used for this request
MFA Deleteを有効にしたバケットではライフサイクルルールを利用できないため、世代の自動削除と併用できない。
古いバージョンをライフサイクルで削除する
バージョニングを有効にすると、上書きのたびに古い世代が残り、ストレージコストが増え続ける。
各バージョンは差分ではなくオブジェクト全体を保持するため、3世代あれば3オブジェクト分の料金がかかる。
ライフサイクルルールで非現行バージョンの保持期間を決めておく。
{
"Rules": [
{
"ID": "expire-noncurrent-versions",
"Filter": {},
"Status": "Enabled",
"NoncurrentVersionExpiration": {
"NoncurrentDays": 30
},
"Expiration": {
"ExpiredObjectDeleteMarker": true
}
}
]
}
$ aws s3api put-bucket-lifecycle-configuration --bucket example-bucket --lifecycle-configuration file://lifecycle.json
NoncurrentVersionExpirationは現行でなくなってからの経過日数で古い世代を削除する。ExpiredObjectDeleteMarkerは、対応する実体がなくなった削除マーカーを掃除する設定である。
設定内容はget-bucket-lifecycle-configurationで確認できる。
$ aws s3api get-bucket-lifecycle-configuration --bucket example-bucket
{
"TransitionDefaultMinimumObjectSize": "all_storage_classes_128K",
"Rules": [
{
"Expiration": {
"ExpiredObjectDeleteMarker": true
},
"ID": "expire-noncurrent-versions",
"Filter": {},
"Status": "Enabled",
"NoncurrentVersionExpiration": {
"NoncurrentDays": 30
}
}
]
}
日数ではなく世代数で残したい場合はNewerNoncurrentVersionsを併用する。
1から100の範囲で指定でき、新しい順に指定した数の非現行バージョンを保持したうえで、それより古い世代をNoncurrentDaysの経過後に削除する。
"NoncurrentVersionExpiration": {
"NoncurrentDays": 30,
"NewerNoncurrentVersions": 3
}
すでにオブジェクトの有効期限ルールを設定しているバケットでバージョニングを有効にする場合は注意が必要である。
有効期限ルールは現行バージョンに削除マーカーを付けるだけになるため、非現行バージョンの有効期限ルールを追加しなければ古い世代が残り続ける。
バージョニングを有効にしたバケットを削除する
バージョンが残っているバケットは削除できない。aws s3 rm --recursiveで見かけ上は空にしても、BucketNotEmptyのエラーになる。
$ aws s3 rm s3://example-bucket/ --recursive
delete: s3://example-bucket/example.txt
$ aws s3 rb s3://example-bucket
remove_bucket failed: s3://example-bucket 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.
list-object-versionsの結果をdelete-objectsに渡し、バージョンと削除マーカーをまとめて削除する。
$ aws s3api delete-objects --bucket example-bucket \
--delete "$(aws s3api list-object-versions --bucket example-bucket \
--output json --query '{Objects: [Versions, DeleteMarkers][].{Key: Key, VersionId: VersionId}}')"
{
"Deleted": [
{
"Key": "example.txt",
"VersionId": "CHEtfwwY.F4LUL0sTcfMEKFxTc.90HBq"
},
{
"Key": "example.txt",
"VersionId": "U5HvIlzkuLTVpDS9Xrk6m0lBFnrzGo9c"
},
{
"Key": "example.txt",
"VersionId": "null",
"DeleteMarker": true,
"DeleteMarkerVersionId": "null"
}
]
}
[Versions, DeleteMarkers][]で両方の配列を平坦化し、KeyとVersionIdだけを取り出している。
3件目のVersionIdがnullなのは、バージョニングを停止した状態で削除したためである。
delete-objectsは1回のリクエストで最大1000件までのため、オブジェクトが多いバケットでは分割して実行する。| [:1000]で先頭1000件に絞り込み、結果が空になるまで繰り返す。
$ while true; do
objects=$(aws s3api list-object-versions --bucket example-bucket \
--query '{Objects: [Versions, DeleteMarkers][] | [:1000].{Key: Key, VersionId: VersionId}}')
echo "$objects" | grep -q VersionId || break
aws s3api delete-objects --bucket example-bucket --delete "$objects" > /dev/null
done
パイプを挟まず[Versions, DeleteMarkers][][:1000]と書くと、平坦化した配列ではなく各要素にスライスが適用され、結果が空になる。| [:1000]のように書いて、平坦化の結果に対してスライスする。
バージョンをすべて削除するとバケットを削除できる。
$ aws s3 rb s3://example-bucket
remove_bucket: example-bucket
必要な権限
コマンドの実行にはそれぞれ次の権限が必要である。
| コマンド | 必要なアクション |
|---|---|
| put-bucket-versioning | s3:PutBucketVersioning |
| get-bucket-versioning | s3:GetBucketVersioning |
| list-object-versions | s3:ListBucketVersions |
| get-object –version-id | s3:GetObjectVersion |
| copy-object | s3:GetObjectVersion, s3:PutObject |
| delete-object –version-id | s3:DeleteObjectVersion |
| delete-objects | s3:DeleteObjectVersion |
| put-bucket-lifecycle-configuration | s3:PutLifecycleConfiguration |
| get-bucket-lifecycle-configuration | s3:GetLifecycleConfiguration |
| list-buckets | s3:ListAllMyBuckets |
| s3 rb | s3:DeleteBucket |
AmazonS3ReadOnlyAccessはs3:Get*とs3:List*を含むため、状態や世代の確認だけであればマネージドポリシーで足りる。
参考
- put-bucket-versioning - AWS CLI Command Reference
- get-bucket-versioning - AWS CLI Command Reference
- Retaining multiple versions of objects with S3 Versioning - Amazon S3
- Configuring MFA delete - Amazon S3
- 【AWS】s3-pit-restoreでS3を特定時点にまとめて復元する
- 【AWS CLI】aws s3api get-bucket-policyでS3のバケットポリシーを確認する
