【小ネタ】 Lambda 関数を利用して、バージョニングが有効化された Amazon S3 バケットを空にしてみた

【小ネタ】 Lambda 関数を利用して、バージョニングが有効化された Amazon S3 バケットを空にしてみた

2025.08.04

はじめに

テクニカルサポートの 片方 です。
今回は小ネタとして、Lambda 関数 (Python3.13) を利用して、バージョニングが有効化された Amazon S3 バケットを空にする方法をご紹介します。
なお、AWS CLI コマンドを利用する場合は、弊社ブログをご参考ください。

https://dev.classmethod.jp/articles/aws-cli-remove-files-versioning-s3-bucket/

実装してみた

実行ロール

※ 信頼関係

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

※ アタッチするポリシー例

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucketVersions",
        "s3:ListBucket",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:DeleteBucket"
      ],
      "Resource": [
        "arn:aws:s3:::YOUR_BUCKET_NAME",
        "arn:aws:s3:::YOUR_BUCKET_NAME/*"
      ]
    }
  ]
}

※ 適宜修正してください。

Lambda関数

今回は Python3.13 で実装しました。

import boto3
import os

s3 = boto3.client('s3')
bucket_name = os.environ['BUCKET_NAME']

def lambda_handler(event, context):
    print(f"Deleting all versions and delete markers in bucket: {bucket_name}")

    paginator = s3.get_paginator('list_object_versions')
    page_iterator = paginator.paginate(Bucket=bucket_name)

    delete_markers = []
    versions = []

    for page in page_iterator:
        if 'Versions' in page:
            for version in page['Versions']:
                versions.append({
                    'Key': version['Key'],
                    'VersionId': version['VersionId']
                })
        if 'DeleteMarkers' in page:
            for marker in page['DeleteMarkers']:
                delete_markers.append({
                    'Key': marker['Key'],
                    'VersionId': marker['VersionId']
                })

    # Combine all versions and delete markers
    objects_to_delete = versions + delete_markers

    # Delete in batches of 1000 (S3 limit)
    for i in range(0, len(objects_to_delete), 1000):
        batch = objects_to_delete[i:i+1000]
        response = s3.delete_objects(
            Bucket=bucket_name,
            Delete={'Objects': batch}
        )
        print(f"Deleted {len(batch)} objects")

    return {
        'statusCode': 200,
        'body': f"Deleted {len(objects_to_delete)} object versions from {bucket_name}"
    }
  • 環境変数
    BUCKET_NAME : YOUR_BUCKET_NAME

※ 適宜修正してください。

補足

併せて S3 バケットも削除したい場合は以下をご参考ください。

import boto3
import os

s3 = boto3.client('s3')
bucket_name = os.environ['BUCKET_NAME']

def lambda_handler(event, context):
    print(f"Starting full cleanup and deletion of bucket: {bucket_name}")

    paginator = s3.get_paginator('list_object_versions')
    page_iterator = paginator.paginate(Bucket=bucket_name)

    delete_markers = []
    versions = []

    for page in page_iterator:
        if 'Versions' in page:
            for version in page['Versions']:
                versions.append({
                    'Key': version['Key'],
                    'VersionId': version['VersionId']
                })
        if 'DeleteMarkers' in page:
            for marker in page['DeleteMarkers']:
                delete_markers.append({
                    'Key': marker['Key'],
                    'VersionId': marker['VersionId']
                })

    objects_to_delete = versions + delete_markers

    # Delete in batches of 1000
    for i in range(0, len(objects_to_delete), 1000):
        batch = objects_to_delete[i:i+1000]
        response = s3.delete_objects(
            Bucket=bucket_name,
            Delete={'Objects': batch}
        )
        print(f"Deleted {len(batch)} objects")

    # 確認のため再チェック(念のため)
    remaining_objects = list(s3.get_paginator('list_object_versions').paginate(Bucket=bucket_name))
    if any('Versions' in page or 'DeleteMarkers' in page for page in remaining_objects):
        return {
            'statusCode': 500,
            'body': f"Bucket {bucket_name} is not empty after deletion attempt."
        }

    # バケットを削除
    try:
        s3.delete_bucket(Bucket=bucket_name)
        print(f"Bucket {bucket_name} deleted successfully.")
        return {
            'statusCode': 200,
            'body': f"Bucket {bucket_name} and all object versions deleted."
        }
    except Exception as e:
        print(f"Error deleting bucket: {str(e)}")
        return {
            'statusCode': 500,
            'body': f"Error deleting bucket: {str(e)}"
        }

検証してみた

バージョニングが有効化されていて、既に複数のオブジェクトが存在する Amazon S3 バケットを空にしてみます。

1000
1001

テストします。
1002

成功しました。

1003

まとめ

Lambda には 実行時間制限(最大15分) があり、オブジェクト数が多いと、1 回の実行では終わらない場合があります。
タイムアウト値を引き上げることや、もしオブジェクト数が非常に多い場合は、Step Functions や バッチ処理との併用を検討してください。
本ブログが誰かの参考になれば幸いです。

参考資料

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。

この記事をシェアする

facebookのロゴhatenaのロゴtwitterのロゴ

© Classmethod, Inc. All rights reserved.