Table of Contents
In this article, I will help you restore Delete marker objects in S3 buckets.
How will we do it? What if there are thousands of objects inside the bucket?
The situation that I encountered Delete marker objects
We use CloudFormation combined with a Lambda function to create S3 buckets and empty folders inside these buckets.
CFN will be responsible for creating buckets and Lambda will create folders.
Although we are calling them folders, S3 only understands them as objects, any file is also an object and a “folder” that we call is also an object.
And each object has its own key ID and S3 manages objects according to this ID.
The situation that I encountered was that when adding a new folder, Lambda (which seems not to be completely accurate) would recreate “folder” objects with the same key ID.
As a result, the existing folders will be deleted and I can only see them if I turn on the Show versions button.


At this point, the objects inside the folder and even the folder itself will be marked as Delete marker.
The condition for my situation is that the S3 bucket has Bucket Versioning enabled.

How to restore Delete marker objects in S3 bucket?
In theory, AWS says that if you want to restore a deleted object in an S3 bucket that uses Versioning, you just need to delete the object whose Type is the Delete marker corresponding to the object you need to restore.
After you delete the Delete marker, the object will be displayed with the most recent old version that was saved.
Like the LICENSE file in the demo image above, you just need to select and delete the object above, with the Type column being Delete marker.
But the question is: what if the bucket has thousands or even hundreds of thousands of objects?
Will you click to delete each object like that?
Restore Delete marker objects in S3 bucket with Python script
Now, you might be thinking of something that can automate the process of recovering thousands of Delete marker objects.
Here is a Python script I wrote that can help you find and recover Delete marker objects in a specific S3 bucket.
Prerequisites for using this script:
- Python 3, of course, you will need to have Python installed on your computer to be able to run this script. You can download Python here for Windows and install it.
- AWS CLI, because this script will interact with the S3 bucket through the boto3 library, so it will need IAM and AWS CLI permissions to work.
You can see how to install AWS CLI and configure it on this page.
import boto3
import logging
import os
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def get_s3_client(profile_name=None):
"""Get S3 client with a specific AWS profile."""
session = boto3.Session(profile_name=profile_name)
return session.client('s3')
def list_all_versions(bucket_name, s3_client):
"""List all versions of all objects in the bucket."""
logging.info(f"Listing all object versions in bucket: {bucket_name}")
paginator = s3_client.get_paginator('list_object_versions')
all_versions = []
for page in paginator.paginate(Bucket=bucket_name):
all_versions.extend(page.get('Versions', []))
logging.info(f"Found {len(all_versions)} object versions.")
return all_versions
def download_object(bucket_name, key, version_id, download_path, s3_client):
"""Download a specific version of an object."""
logging.info(f"Downloading {key} version {version_id} to {download_path}")
response = s3_client.get_object(Bucket=bucket_name, Key=key, VersionId=version_id)
with open(download_path, 'wb') as file:
file.write(response['Body'].read())
logging.info(f"Downloaded {key} version {version_id}")
def backup_bucket_to_local(bucket_name, backup_dir, s3_client):
"""Backup all object versions from the bucket to a local directory."""
logging.info(f"Backing up bucket {bucket_name} to local directory {backup_dir}")
if not os.path.exists(backup_dir):
os.makedirs(backup_dir)
# List all versions of all objects in the bucket
all_versions = list_all_versions(bucket_name, s3_client)
for version in all_versions:
key = version['Key']
version_id = version['VersionId']
# Avoid handling keys that end with '/'
if key.endswith('/'):
logging.warning(f"Skipping directory key: {key}")
continue
# Construct a path for the downloaded file
file_path = os.path.join(backup_dir, key)
# Ensure the directory exists
os.makedirs(os.path.dirname(file_path), exist_ok=True)
# Download the object
download_object(bucket_name, key, version_id, file_path, s3_client)
def delete_delete_marker(bucket_name, key, version_id, s3_client):
"""Delete a delete marker from the bucket."""
logging.info(f"Deleting delete marker for key: {key}, version ID: {version_id}")
s3_client.delete_object(Bucket=bucket_name, Key=key, VersionId=version_id)
logging.info(f"Deleted delete marker for {key}, version ID: {version_id}")
def list_delete_markers(bucket_name, s3_client):
"""List all delete markers in the bucket."""
logging.info(f"Listing delete markers in bucket: {bucket_name}")
paginator = s3_client.get_paginator('list_object_versions')
delete_markers = []
for page in paginator.paginate(Bucket=bucket_name):
delete_markers.extend(page.get('DeleteMarkers', []))
logging.info(f"Found {len(delete_markers)} delete markers.")
return delete_markers
def restore_objects_from_delete_markers(bucket_name, profile_name):
"""Restore objects from delete markers by deleting those delete markers."""
s3_client = get_s3_client(profile_name)
# Backup bucket to a local directory
backup_dir = f"{bucket_name}-backup"
backup_bucket_to_local(bucket_name, backup_dir, s3_client)
# Restore objects from delete markers
delete_markers = list_delete_markers(bucket_name, s3_client)
if not delete_markers:
logging.info("No delete markers found.")
return
for marker in delete_markers:
key = marker['Key']
version_id = marker['VersionId']
delete_delete_marker(bucket_name, key, version_id, s3_client)
logging.info("All delete markers have been deleted.")
if __name__ == "__main__":
bucket_name = 'daniel-devopslite-test' # Replace with your bucket name
profile_name = 'default' # Replace with your AWS profile name
restore_objects_from_delete_markers(bucket_name, profile_name)
will briefly explain the functions in the above Python script.
get_s3_client(profile_name=None): Creates an S3 client using the specified AWS profile. This client is used to interact with the S3 service.list_all_versions(bucket_name, s3_client): Retrieves and logs all versions of all objects in the specified S3 bucket using a paginator. This handles pagination if there are many versions.download_object(bucket_name, key, version_id, download_path, s3_client): Downloads a specific version of an object from S3 and saves it to a local file.backup_bucket_to_local(bucket_name, backup_dir, s3_client): Backs up all object versions from the S3 bucket to a specified local directory. It creates the directory structure and downloads each object, avoiding any keys that represent directories.delete_delete_marker(bucket_name, key, version_id, s3_client): Deletes a delete marker for a specified object, which effectively restores the object by removing the marker.list_delete_markers(bucket_name, s3_client): Lists all delete markers in the specified bucket using a paginator.restore_objects_from_delete_markers(bucket_name, profile_name): This function performs two main tasks:- Backups the entire bucket to a local directory.
- Restores objects from delete markers by deleting those markers.
if __name__ == "__main__": Executes therestore_objects_from_delete_markersfunction with specified bucket and profile names when the script is run directly.
In short this script provides a way to backup an S3 bucket to a local directory, handle all object versions, and restore objects by removing delete markers. It uses the boto3 library to interact with AWS S3, handles errors through logging, and creates the necessary local directory structure to match the S3 bucket’s content.
After you download this script to your computer, remember to replace the bucket name and AWS profile in these two lines in the script.
bucket_name = 'daniel-devopslite-test' # Replace with your bucket name
profile_name = 'default' # Replace with your AWS profile nameLog in to your AWS account with AWS CLI and then execute the following command to run the script:
python3 restore-s3.pyOn your terminal window (either CMD or PowerShell) will display log like image below.

When the script finishes executing, you will see it create a backup folder for the S3 bucket at the same level as the script.

And then you can check on S3 bucket, the script has restored the Delete marker objects in S3 bucket successfully. You can see the objects without enabling Show versions.

Conclusion
This article is both a small experience of mine and an experience that I want to share with you.
If one day, you no longer see your folders/files in your S3 bucket, stay calm.
Most likely, this script will help you restore the Delete marker objects in your S3 bucket. Good luck!