How to Remove Delete Markers from Multiple Objects on Amazon S3 at once

Use this to restore the files inside the specific folder. I've used aws cli commands in my script. Provide input as: sh scriptname.sh bucketname path/to/a/folder

**Script:**
#!/bin/bash
#please provide the bucketname and path to destination folder to restore
# Remove all versions and delete markers for each object
 aws s3api list-object-versions --bucket $1 --prefix $2 --output text | 
 grep "DELETEMARKERS" | while read obj
   do
        KEY=$( echo $obj| awk '{print $3}')
        VERSION_ID=$( echo $obj | awk '{print $5}')
        echo $KEY
        echo $VERSION_ID
        aws s3api delete-object --bucket $1 --key $KEY --version-id $VERSION_ID

   done

Edit: put $VERSION_ID in correct position in the script


Here's a sample Python implementation:

import boto3
import botocore

BUCKET_NAME = 'BUCKET_NAME'
s3 = boto3.resource('s3')


def main():
    bucket = s3.Bucket(BUCKET_NAME)
    versions = bucket.object_versions

    for version in versions.all():
        if is_delete_marker(version):
             version.delete()


def is_delete_marker(version):
    try:
        # note head() is faster than get()
        version.head()
        return False
    except botocore.exceptions.ClientError as e:
        if 'x-amz-delete-marker' in e.response['ResponseMetadata']['HTTPHeaders']:
            return True
        # an older version of the key but not a DeleteMarker
        elif '404' == e.response['Error']['Code']:
            return False


if __name__ == '__main__':
    main()

For some context for this answer see: https://docs.aws.amazon.com/AmazonS3/latest/dev/DeleteMarker.html

If you try to get an object and its current version is a delete marker, Amazon S3 responds with:

  • A 404 (Object not found) error
  • A response header, x-amz-delete-marker: true

The response header tells you that the object accessed was a delete marker. This response header never returns false; if the value is false, Amazon S3 does not include this response header in the response.

The only way to list delete markers (and other versions of an object) is by using the versions subresource in a GET Bucket versions request. A simple GET does not retrieve delete marker objects.

Unfortunately, despite what is written in https://github.com/boto/botocore/issues/674, checking if ObjectVersion.size is None is not a reliable way to determine if a version is a delete marker as it will also be true for previously deleted versions of folder keys.

Currently, boto3 is missing a straightforward way to determine if an ObjectVersion is a DeleteMarker. See https://github.com/boto/boto3/issues/1769

However, ObjectVersion.head() and .Get() operations will throw an exception on an ObjectVersion that is a DeleteMarker. Catching this exception is likely the only reliable way of determining if an ObjectVersion is a DeleteMarker.


I just wrote a program (using boto) to solve the same problem:

from boto.s3 import deletemarker
from boto.s3.connection import S3Connection
from boto.s3.key import Key

def restore_bucket(bucket_name): 
    bucket = conn.get_bucket(bucket_name)
    for version in bucket.list_versions():
        if isinstance(version, deletemarker.DeleteMarker) and version.is_latest:
            bucket.delete_key(version.name, version_id=version.version_id)

If you need to restore folders within the versioned buckets, the rest of the program I wrote can be found here.