76

I have tried both s3cmd:

$ s3cmd -r -f -v del s3://my-versioned-bucket/

And the AWS CLI:

$ aws s3 rm s3://my-versioned-bucket/ --recursive

But both of these commands simply add DELETE markers to S3. The command for removing a bucket also doesn't work (from the AWS CLI):

$ aws s3 rb s3://my-versioned-bucket/ --force
Cleaning up. Please wait...
Completed 1 part(s) with ... file(s) remaining
remove_bucket failed: s3://my-versioned-bucket/ A client error (BucketNotEmpty) occurred when calling the DeleteBucket operation: The bucket you tried to delete is not empty. You must delete all versions in the bucket.

Ok... how? There's no information in their documentation for this. S3Cmd says it's a 'fully-featured' S3 command-line tool, but it makes no reference to versions other than its own. Is there any way to do this without using the web interface, which will take forever and requires me to keep my laptop on?

NobleUplift
  • 4,577
  • 6
  • 37
  • 76

21 Answers21

82

I ran into the same limitation of the AWS CLI. I found the easiest solution to be to use Python and boto3:

#!/usr/bin/env python

BUCKET = 'your-bucket-here'

import boto3

s3 = boto3.resource('s3')
bucket = s3.Bucket(BUCKET)
bucket.object_versions.delete()

# if you want to delete the now-empty bucket as well, uncomment this line:
#bucket.delete()

A previous version of this answer used boto but that solution had performance issues with large numbers of keys as Chuckles pointed out.

slm
  • 12,534
  • 12
  • 87
  • 106
Abe Voelker
  • 25,576
  • 13
  • 76
  • 95
36

Using boto3 it's even easier than with the proposed boto solution to delete all object versions in an S3 bucket:

#!/usr/bin/env python
import boto3

s3 = boto3.resource('s3')
bucket = s3.Bucket('your-bucket-name')
bucket.object_versions.all().delete()

Works fine also for very large amounts of object versions, although it might take some time in that case.

Dunedan
  • 5,560
  • 6
  • 29
  • 38
31

You can delete all the objects in the versioned s3 bucket. But I don't know how to delete specific objects.

$ aws s3api delete-objects \
      --bucket <value> \
      --delete "$(aws s3api list-object-versions \
      --bucket <value> | \
      jq '{Objects: [.Versions[] | {Key:.Key, VersionId : .VersionId}], Quiet: false}')"

Alternatively without jq:

$ aws s3api delete-objects \
    --bucket ${bucket_name} \
    --delete "$(aws s3api list-object-versions \
    --bucket "${bucket_name}" \
    --output=json \
    --query='{Objects: Versions[].{Key:Key,VersionId:VersionId}}')"
slm
  • 12,534
  • 12
  • 87
  • 106
Cheers
  • 351
  • 3
  • 7
  • 1
    Any way of getting around the 1000+ objects limitation? – Matías Feb 28 '18 at 23:43
  • Nice one, but as mentioned in other answers Versions might not be enough and you might need to delete DeleteMarkers too. – Federico Aug 30 '19 at 09:24
  • 2
    Correct, DeleteMarkers need to be deleted as well. (having just tried this) – Bruce Edge Jul 21 '20 at 22:45
  • @BruceEdge could you update the answer to reflect deletion of deletemarkers aswell? Thank you – DJ_Stuffy_K Dec 09 '20 at 21:52
  • 1
    @DJ_Stuffy_K I addressed this later by creating a policy that deleted versions more than 1 day old. Now, when I want to delete a versioned bucket, I apply the policy, then disable versioning. Then the policy takes care of all the old versions. (There wasn't an easy way to delete the markers in a single command) – Bruce Edge Dec 11 '20 at 16:00
  • @BruceEdge doesn't answer 2 from https://stackoverflow.com/a/61123579/629287 take care of deleting markers in a single command? – Chris B Dec 15 '20 at 19:09
  • @chris, yes, it appears to.I don't remember if I tried that or not. Looks worth a try. – Bruce Edge Dec 16 '20 at 22:51
13

One way to do it is iterate through the versions and delete them. A bit tricky on the CLI, but as you mentioned Java, that would be more straightforward:

AmazonS3Client s3 = new AmazonS3Client();
String bucketName = "deleteversions-"+UUID.randomUUID();

//Creates Bucket
s3.createBucket(bucketName);

//Enable Versioning
BucketVersioningConfiguration configuration = new BucketVersioningConfiguration(ENABLED);
s3.setBucketVersioningConfiguration(new SetBucketVersioningConfigurationRequest(bucketName, configuration ));

//Puts versions
s3.putObject(bucketName, "some-key",new ByteArrayInputStream("some-bytes".getBytes()), null);
s3.putObject(bucketName, "some-key",new ByteArrayInputStream("other-bytes".getBytes()), null);

//Removes all versions
for ( S3VersionSummary version : S3Versions.inBucket(s3, bucketName) ) {
    String key = version.getKey();
    String versionId = version.getVersionId();          
    s3.deleteVersion(bucketName, key, versionId);
}

//Removes the bucket
s3.deleteBucket(bucketName);
System.out.println("Done!");

You can also batch delete calls for efficiency if needed.

Julio Faerman
  • 12,021
  • 9
  • 52
  • 72
  • 1
    I was actually going to try getting a multithreaded program running to delete each of the 'folders' in my S3 bucket (there are four of them), and if needed split the threads for the larger folders into `first/1`, ..., `first/9`, etc. ... but a coworker and I just ended up deleting all the versions using the web interface and Cyberduck. Thanks for the help when I needed it though! – NobleUplift Apr 30 '15 at 15:40
  • The important stuff to remember when writing a bulk-delete script * you may have to use a region-specific endpoint for deleting versions (check bucket location) * you have to delete all Versions and DeleteMarkers – Jan Grz Apr 20 '21 at 09:05
12

Here is a one liner you can just cut and paste into the command line to delete all versions and delete markers (it requires aws tools, replace yourbucket-name-backup with your bucket name)

echo '#!/bin/bash' > deleteBucketScript.sh \
&& aws --output text s3api list-object-versions --bucket $BUCKET_TO_PERGE \
| grep -E "^VERSIONS" |\
awk '{print "aws s3api delete-object --bucket $BUCKET_TO_PERGE --key "$4" --version-id "$8";"}' >> \
deleteBucketScript.sh && . deleteBucketScript.sh; rm -f deleteBucketScript.sh; echo '#!/bin/bash' > \
deleteBucketScript.sh && aws --output text s3api list-object-versions --bucket $BUCKET_TO_PERGE \
| grep -E "^DELETEMARKERS" | grep -v "null" \
| awk '{print "aws s3api delete-object --bucket $BUCKET_TO_PERGE --key "$3" --version-id "$5";"}' >> \
deleteBucketScript.sh && . deleteBucketScript.sh; rm -f deleteBucketScript.sh;

then you could use:

aws s3 rb s3://bucket-name --force

Vihar Manchala
  • 328
  • 3
  • 13
Nitin
  • 7,219
  • 3
  • 15
  • 29
  • 1
    My co-worker set up life cycle rules that will slowly delete the versioning buckets over the next several days. Your answer is ironic in that it spells the end of S3 versioning in our system forever. – NobleUplift Sep 19 '16 at 22:31
  • 1
    From what I can see, this is the only actual CLI-based answer that caters for buckets of any size that actually works. – volvox May 13 '18 at 20:33
  • 2
    Upvoting for the correctness of the answer but this code is really hard to read and understand. – Alex Harvey Dec 13 '19 at 00:12
10

This two bash lines are enough for me to enable the bucket deletion !

1: Delete objects aws s3api delete-objects --bucket ${buckettoempty} --delete "$(aws s3api list-object-versions --bucket ${buckettoempty} --query='{Objects: Versions[].{Key:Key,VersionId:VersionId}}')"

2: Delete markers aws s3api delete-objects --bucket ${buckettoempty} --delete "$(aws s3api list-object-versions --bucket ${buckettoempty} --query='{Objects: DeleteMarkers[].{Key:Key,VersionId:VersionId}}')"

Alexandre Hamon
  • 556
  • 6
  • 7
8

For those using multiple profiles via ~/.aws/config

import boto3

PROFILE = "my_profile"
BUCKET = "my_bucket"

session = boto3.Session(profile_name = PROFILE)
s3 = session.resource('s3')
bucket = s3.Bucket(BUCKET)
bucket.object_versions.delete()
Vishal Gori
  • 417
  • 4
  • 3
  • Thanks for your pythonic answer. Works for millions of objects (though you have to leave it open for a long time). And since python is interactive, it feels like a CLI answer ;) – Aaron R. Nov 20 '19 at 15:31
  • You can pass profile as an option using cli as well - `aws s3api delete-objects --profile --bucket --delete "$(aws s3api list-object-versions --bucket | jq -M '{Objects: [.["Versions","DeleteMarkers"][]|select(.Key == "key-value")| {Key:.Key, VersionId : .VersionId}], Quiet: false}')"` – Vishal Gori Jun 24 '20 at 16:07
  • can I specify prefix level as well? – DJ_Stuffy_K Dec 09 '20 at 21:55
  • @VishalGori Is there a way to print what is being deleted similar to what aws s3 rm would ? I'm running this pyscript but don't know whether the program is running or stuck. – DJ_Stuffy_K Dec 09 '20 at 22:16
6
  1. For deleting specify object(s), using jq filter.
  2. You may need cleanup the 'DeleteMarkers' not just 'Versions'.
  3. Using $() instead of ``, you may embed variables for bucket-name and key-value.
aws s3api delete-objects --bucket bucket-name --delete "$(aws s3api list-object-versions --bucket bucket-name | jq -M '{Objects: [.["Versions","DeleteMarkers"][]|select(.Key == "key-value")| {Key:.Key, VersionId : .VersionId}], Quiet: false}')"
Koen.
  • 21,087
  • 6
  • 75
  • 76
Tiger peng
  • 429
  • 5
  • 5
  • 1
    This oneliner (as well as the one above http://stackoverflow.com/a/31086407/465684 ) look and work nice, but they do not cater for the situation when there are more than 1000 objects to delete (hard limitation of the s3api delete-objects call). – Andrei Neculau Sep 06 '16 at 10:27
  • @tiger-peng Got the error "An error occurred (MalformedXML) when calling the DeleteObjects operation: The XML you provided was not well-formed or did not validate against our published schema". Any idea what's going on? – Marcello de Sales Feb 07 '18 at 18:16
  • @MarcellodeSales, if you can share what command you used, I may try to check it out. – Tiger peng Mar 16 '18 at 17:46
  • I'm using the solution below with Python... I gave up on the aws cli – Marcello de Sales Mar 19 '18 at 21:08
6

I ran into issues with Abe's solution as the list_buckets generator is used to create a massive list called all_keys and I spent an hour without it ever completing. This tweak seems to work better for me, I had close to a million objects in my bucket and counting!

import boto

s3 = boto.connect_s3()
bucket = s3.get_bucket("your-bucket-name-here")

chunk_counter = 0 #this is simply a nice to have
keys = []
for key in bucket.list_versions():
    keys.append(key)
    if len(keys) > 1000:
        bucket.delete_keys(keys)
        chunk_counter += 1
        keys = []
        print("Another 1000 done.... {n} chunks so far".format(n=chunk_counter))

#bucket.delete() #as per usual uncomment if you're sure!

Hopefully this helps anyone else encountering this S3 nightmare!

Community
  • 1
  • 1
chuckwired
  • 137
  • 1
  • 6
  • 1
    Thank you for the example this helped me out, one thing this is missing a final bucket.delete_keys(keys) after the for loop to catch any stragglers based on your batching logic here. – Sean Feb 05 '18 at 19:58
  • does it work at a prefix level? – DJ_Stuffy_K Dec 09 '20 at 21:53
5

If you want pure CLI approach (with jq):

aws s3api list-object-versions \
          --bucket $bucket \
          --region $region \
          --query "Versions[].Key"  \
          --output json | jq 'unique' | jq -r '.[]' | while read key; do
   echo "deleting versions of $key"
   aws s3api list-object-versions \
          --bucket $bucket \
          --region $region \
          --prefix $key \
          --query "Versions[].VersionId"  \
          --output json | jq 'unique' | jq -r '.[]' | while read version; do
     echo "deleting $version"
     aws s3api delete-object \
          --bucket $bucket \
          --key $key \
          --version-id $version \
          --region $region
   done
done          
Bob Ritchie
  • 51
  • 1
  • 1
4

Even though technically it's not AWS CLI, I'd recommend using AWS Tools for Powershell for this task. Then you can use the simple command as below:

Remove-S3Bucket -BucketName {bucket-name} -DeleteBucketContent -Force -Region {region}

As stated in the documentation, DeleteBucketContent flag does the following:

"If set, all remaining objects and/or object versions in the bucket are deleted proir (sic) to the bucket itself being deleted"

Reference: https://docs.aws.amazon.com/powershell/latest/reference/

Volkan Paksoy
  • 5,991
  • 5
  • 23
  • 38
  • 1
    This solution actually worked far better than the others. Namely, because we have Powershell tools which work on cross-platform Powershell now, this was the fastest solution to get my bucket deleted without much effort. This will receive my bounty once I can award it later tonight. – Thomas Ward Nov 17 '20 at 16:26
  • @ThomasWard Thank you for the bounty. I appreciate it. – Volkan Paksoy Nov 22 '20 at 07:51
  • 1
    Well, your answer was the only one that not only included the mechanism to delete the bucket but a straightforward integrated-into-the-command way to purge all content and versions as well. None of the other solutions worked - even the aws s3api calls didn't work they hardexploded on "Must delete all versions" and emptying the bucket didn't help, so this was the only solution that really worked for me here. – Thomas Ward Nov 23 '20 at 02:53
4

If you have to delete/empty large S3 buckets, it becomes quite inefficient (and expensive) to delete every single object and version. It's often more convenient to let AWS expire all objects and versions.

aws s3api put-bucket-lifecycle-configuration \
  --lifecycle-configuration '{"Rules":[{
      "ID":"empty-bucket",
      "Status":"Enabled",
      "Prefix":"",
      "Expiration":{"Days":1},
      "NoncurrentVersionExpiration":{"NoncurrentDays":1}
    }]}' \
  --bucket YOUR-BUCKET

Then you just have to wait 1 day and the bucket can be deleted with:

aws s3api delete-bucket --bucket YOUR-BUCKET
muhqu
  • 10,881
  • 6
  • 25
  • 28
3

Looks like as of now, there is an Empty button in the AWS S3 console. Empty Button

Just select your bucket and click on it. It will ask you to confirm your decision by typing permanently delete Note, this will not delete the bucket itself.

Alexander Pogrebnyak
  • 42,921
  • 9
  • 97
  • 117
  • I believe this doesn't work on expired buckets. Please test this on a small bucket before doing this. Otherwise you will spend hours on this without realizing the problem. – javatarz Apr 16 '21 at 04:11
2

You can do this from the AWS Console using Lifecycle Rules.

Open the bucket in question. Click the Management tab at the top. Make sure the Lifecycle Sub Tab is selected. Click + Add lifecycle rule

On Step 1 (Name and scope) enter a rule name (e.g. removeall) Click Next to Step 2 (Transitions) Leave this as is and click Next.

You are now on the 3. Expiration step. Check the checkboxes for both Current Version and Previous Versions. Click the checkbox for "Expire current version of object" and enter the number 1 for "After _____ days from object creation Click the checkbox for "Permanently delete previous versions" and enter the number 1 for "After _____ days from becoming a previous version"

click the checkbox for "Clean up incomplete multipart uploads" and enter the number 1 for "After ____ days from start of upload" Click Next Review what you just did.
Click Save

Come back in a day and see how it is doing.

enter image description here

ojhurst
  • 322
  • 1
  • 4
  • 9
1

This bash script found here: https://gist.github.com/weavenet/f40b09847ac17dd99d16

worked as is for me.

I saved script as: delete_all_versions.sh and then simply ran:

./delete_all_versions.sh my_foobar_bucket

and that worked without a flaw.

Did not need python or boto or anything.

Mamun
  • 1,512
  • 2
  • 13
  • 26
0

By far the easiest method I've found is to use this CLI tool, s3wipe. It's provided as a docker container so you can use it like so:

$ docker run -it --rm slmingol/s3wipe --help
usage: s3wipe [-h] --path PATH [--id ID] [--key KEY] [--dryrun] [--quiet]
              [--batchsize BATCHSIZE] [--maxqueue MAXQUEUE]
              [--maxthreads MAXTHREADS] [--delbucket] [--region REGION]

Recursively delete all keys in an S3 path

optional arguments:
  -h, --help               show this help message and exit
  --path PATH              S3 path to delete (e.g. s3://bucket/path)
  --id ID                  Your AWS access key ID
  --key KEY                Your AWS secret access key
  --dryrun                 Don't delete. Print what we would have deleted
  --quiet                  Suprress all non-error output
  --batchsize BATCHSIZE    # of keys to batch delete (default 100)
  --maxqueue MAXQUEUE      Max size of deletion queue (default 10k)
  --maxthreads MAXTHREADS  Max number of threads (default 100)
  --delbucket              If S3 path is a bucket path, delete the bucket also
  --region REGION          Region of target S3 bucket. Default vaue `us-
                           east-1`

Example

Here's an example where I'm deleting all the versioned objects in a bucket and then deleting the bucket:

$ docker run -it --rm slmingol/s3wipe \
   --id $(aws configure get default.aws_access_key_id) \
   --key $(aws configure get default.aws_secret_access_key) \
   --path s3://bw-tf-backends-aws-example-logs \
   --delbucket
[2019-02-20@03:39:16] INFO: Deleting from bucket: bw-tf-backends-aws-example-logs, path: None
[2019-02-20@03:39:16] INFO: Getting subdirs to feed to list threads
[2019-02-20@03:39:18] INFO: Done deleting keys
[2019-02-20@03:39:18] INFO: Bucket is empty.  Attempting to remove bucket

How it works

There's a bit to unpack here but the above is doing the following:

  • docker run -it --rm mikelorant/s3wipe - runs s3wipe container interactively and deletes it after each execution
  • --id & --key - passing our access key and access id in
  • aws configure get default.aws_access_key_id - retrieves our key id
  • aws configure get default.aws_secret_access_key - retrieves our key secret
  • --path s3://bw-tf-backends-aws-example-logs - bucket that we want to delete
  • --delbucket - deletes bucket once emptied

References

slm
  • 12,534
  • 12
  • 87
  • 106
  • The idea looked promising, but I'm getting the following error: `ERROR: S3ResponseError: 400 Bad Request` for some reason. Also, the `aws configure get default.aws_access_key_id` is not working so I have to manually pass the credentials. – Slava Fomin II Nov 21 '19 at 14:03
0

https://gist.github.com/wknapik/191619bfa650b8572115cd07197f3baf

#!/usr/bin/env bash

set -eEo pipefail
shopt -s inherit_errexit >/dev/null 2>&1 || true

if [[ ! "$#" -eq 2 || "$1" != --bucket ]]; then
    echo -e "USAGE: $(basename "$0") --bucket <bucket>"
    exit 2
fi

# $@ := bucket_name
empty_bucket() {
    local -r bucket="${1:?}"
    for object_type in Versions DeleteMarkers; do
        local opt=() next_token=""
        while [[ "$next_token" != null ]]; do
            page="$(aws s3api list-object-versions --bucket "$bucket" --output json --max-items 1000 "${opt[@]}" \
                        --query="[{Objects: ${object_type}[].{Key:Key, VersionId:VersionId}}, NextToken]")"
            objects="$(jq -r '.[0]' <<<"$page")"
            next_token="$(jq -r '.[1]' <<<"$page")"
            case "$(jq -r .Objects <<<"$objects")" in
                '[]'|null) break;;
                *) opt=(--starting-token "$next_token")
                   aws s3api delete-objects --bucket "$bucket" --delete "$objects";;
            esac
        done
    done
}

empty_bucket "${2#s3://}"

E.g. empty_bucket.sh --bucket foo

This will delete all object versions and delete markers in a bucket in batches of 1000. Afterwards, the bucket can be deleted with aws s3 rb s3://foo.

Requires bash, awscli and jq.

0

This works for me. Maybe running later versions of something and above > 1000 items. been running a couple of million files now. However its still not finished after half a day and no means to validate in AWS GUI =/

# Set bucket name to clearout
BUCKET = 'bucket-to-clear'

import boto3
s3 = boto3.resource('s3')
bucket = s3.Bucket(BUCKET)

max_len         = 1000      # max 1000 items at one req
chunk_counter   = 0         # just to keep track
keys            = []        # collect to delete

# clear files
def clearout():
    global bucket
    global chunk_counter
    global keys
    result = bucket.delete_objects(Delete=dict(Objects=keys))

    if result["ResponseMetadata"]["HTTPStatusCode"] != 200:
        print("Issue with response")
        print(result)

    chunk_counter += 1
    keys = []
    print(". {n} chunks so far".format(n=chunk_counter))
    return

# start
for key in bucket.object_versions.all():
    item = {'Key': key.object_key, 'VersionId': key.id}
    keys.append(item)
    if len(keys) >= max_len:
        clearout()

# make sure last files are cleared as well
if len(keys) > 0:
    clearout()

print("")
print("Done, {n} items deleted".format(n=chunk_counter*max_len))
#bucket.delete() #as per usual uncomment if you're sure!
roady
  • 557
  • 1
  • 5
  • 18
0

To add to python solutions provided here: if you are getting boto.exception.S3ResponseError: S3ResponseError: 400 Bad Request error, try creating ~/.boto file with the following data:

[Credentials]
aws_access_key_id = aws_access_key_id
aws_secret_access_key = aws_secret_access_key
[s3]
host=s3.eu-central-1.amazonaws.com
aws_access_key_id = aws_access_key_id
aws_secret_access_key = aws_secret_access_key

Helped me to delete bucket in Frankfurt region.

Original answer: https://stackoverflow.com/a/41200567/2586441

0

You can use aws-cli to delete s3 bucket

aws s3 rb s3://your-bucket-name

If aws cli is not installed in your computer you can your following commands: For Linux or ubuntu:

sudo apt-get install aws-cli

Then check it is installed or not by:

aws --version

Now configure it by providing aws-access-credentials

aws configure

Then give the access key and secret access key and your region

Zakaria
  • 211
  • 2
  • 6
-1

I found the other answers either incomplete or requiring external dependencies to be installed (like boto), so here is one that is inspired by those but goes a little deeper.

As documented in Working with Delete Markers, before a versioned bucket can be removed, all its versions must be completely deleted, which is a 2-step process:

  1. "delete" all version objects in the bucket, which marks them as deleted but does not actually delete them
  2. complete the deletion by deleting all the deletion marker objects

Here is the pure CLI solution that worked for me (inspired by the other answers):

#!/usr/bin/env bash

bucket_name=...

del_s3_bucket_obj()
{
    local bucket_name=$1
    local obj_type=$2
    local query="{Objects: $obj_type[].{Key:Key,VersionId:VersionId}}"
    local s3_objects=$(aws s3api list-object-versions --bucket ${bucket_name} --output=json --query="$query")
    if ! (echo $s3_objects | grep -q '"Objects": null'); then
        aws s3api delete-objects --bucket "${bucket_name}" --delete "$s3_objects"
    fi
}

del_s3_bucket_obj ${bucket_name} 'Versions'
del_s3_bucket_obj ${bucket_name} 'DeleteMarkers'

Once this is done, the following will work:

aws s3 rb "s3://${bucket_name}"

Not sure how it will fare with 1000+ objects though, if anyone can report that would be awesome.

Oliver
  • 21,908
  • 4
  • 55
  • 73