S3 でプリフィックスを指定して定期的に Latest version 以外のバージョンを削除する

バージョニングが有効な特定の S3 のバケットにおいて、プリフィックスを指定して定期的に latest 以外のバージョンを削除するという要件があったので試してみました。

IAM ロール作成

S3 の権限が必要なので次のような IAM ポリシーを作成し、IAM ロールにアタッチしてください。 (他に AWSLambdaBasicExecutionRole も合わせてアタッチしてください)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:DeleteObjectVersion",
                "s3:ListBucketVersions"
            ],
            "Resource": [
                "arn:aws:s3:::バケット名",
                "arn:aws:s3:::バケット名/*"
            ]
        }
    ]
}

Lambda の作成

Python 3.7 で新規に Lambda を作成し、さきほどの IAM ロールをアタッチします。

実際の Python のコードはこちらです。

import boto3

BUCKET = 'バケット名'
PREFIX = 'プリフィックス'

s3 = boto3.client('s3')

def lambda_handler(event, context):
    keys = get_all_versions(
        bucket=BUCKET,
        prefix=PREFIX
    )
    
    for key in keys:
        response = s3.delete_object(
            Bucket=BUCKET,
            Key=key['key'],
            VersionId=key['version_id']
        )

def get_all_versions(bucket: str, prefix: str, next_key: str=None, next_version_id: str=None, keys: []=[]):
    if next_key is not None and next_version_id is not None:
        response = s3.list_object_versions(
            Bucket=bucket,
            Prefix=prefix,
            KeyMarker=next_key,
            VersionIdMarker=next_version_id
        )
    else:
        response = s3.list_object_versions(
            Bucket=bucket,
            Prefix=prefix
        )

    if 'Versions' in response:
        for content in response['Versions']:
            if not content['IsLatest']:
                keys.append({'key': content['Key'], 'version_id': content['VersionId']})
    
    if 'DeleteMarkers' in response:
        for content in response['DeleteMarkers']:
            keys.append({'key': content['Key'], 'version_id': content['VersionId']})

    if response['IsTruncated'] and 'NextKeyMarker' in response and 'NextVersionIdMarker' in response:
        get_all_versions(
            bucket=bucket,
            prefix=prefix,
            next_key=response['NextKeyMarker'],
            next_version_id=response['NextVersionIdMarker'],
            keys=keys
        )

    return keys

デフォルトだと Timeout が 3 秒なので、タイムアウトしないように 1 分くらいに変更しておくと良いと思います。

この Lambda を実行すると Latest version 以外のバージョンは全て削除されます。

定期的に実行できるようにする

定期的に Lambda を実行したいので CloudWatch Events に Rule を作成します。

毎日 AM 4:00 GMT に設定したい場合は

f:id:enomotodev:20190926183922p:plain

このように設定し、Targets の function には先ほど作成した Lambda を指定してください。

まとめ

Lambda + CloudWatch Events の組み合わせを使うと、cron のようなことができるのでとても便利でした。

今回はバージョンを削除するという要件だったため S3 のライフサイクルは使用できませんでしたが、機会があったらこの辺も実際に試していきたいと思います!

参考