How to delete non empty s3 bucket with terraform?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
Terraform will not normally delete an S3 bucket if it still contains objects, because S3 itself requires the bucket to be empty first. The standard Terraform solution is force_destroy = true on the bucket resource. That tells the AWS provider to remove the bucket contents as part of destroy, but it should be used carefully because it turns a normally protective failure into a destructive action.
The basic Terraform setting
For an S3 bucket managed by the AWS provider, the usual pattern looks like this:
With force_destroy = true, Terraform is allowed to empty the bucket before removing it. Without that setting, terraform destroy typically fails if objects are still present.
This is the cleanest answer because the behavior stays inside the provider instead of relying on shell provisioners or manual AWS CLI cleanup.
Why it matters in real environments
S3 buckets are often long-lived and may contain:
- uploaded application files
- logs
- generated reports
- versioned objects and delete markers
That means "destroy the infrastructure" can unexpectedly become "erase live data." So force_destroy is appropriate only when bucket contents are disposable or the deletion has been explicitly approved.
Example with versioning awareness
A common pattern is to enable versioning but still allow destruction in non-production environments:
The important operational point is that versioning can make a bucket look empty in the console while old versions or delete markers still exist. The provider has to handle those objects too during destroy, which is one reason large bucket cleanup can take time.
What not to do by default
You will sometimes see examples that attach a local-exec provisioner and call aws s3 rm --recursive during destroy. That can work, but it is usually the wrong default because:
- it depends on the AWS CLI being installed
- it mixes imperative cleanup into declarative infrastructure
- it is harder to reason about and test than provider-native behavior
If the provider supports the behavior directly, prefer the provider setting.
Extra blockers you still need to think about
Even with force_destroy = true, bucket deletion may still be blocked by surrounding S3 features or policy choices, for example:
- restrictive bucket policies
- object lock retention or legal holds
- insufficient IAM permissions
- replication or lifecycle behavior still in flight
So force_destroy is "allow Terraform to empty the bucket," not "guarantee deletion no matter what."
Use it differently in dev and prod
In development or ephemeral test stacks, force_destroy = true is often reasonable. In production, many teams intentionally leave it disabled so that a destroy fails loudly instead of silently deleting important objects.
That can be expressed as an environment-specific variable:
This makes the destructive behavior opt-in instead of accidental.
Common Pitfalls
The biggest mistake is enabling force_destroy on a bucket that contains valuable production data. Terraform will treat that data as part of the resource lifecycle.
Another issue is assuming the bucket is empty just because no current objects are visible. Versioned buckets may still contain older versions or delete markers.
People also use shell provisioners for cleanup when the provider setting is simpler and safer to maintain.
Finally, bucket deletion can still fail if IAM permissions, object-lock settings, or policies prevent object removal. force_destroy does not override S3 security controls.
Summary
- The normal Terraform answer is
force_destroy = trueonaws_s3_bucket. - That allows the provider to remove bucket contents before deleting the bucket.
- It is appropriate for disposable or explicitly approved data, especially in dev environments.
- It should be used cautiously for versioned or production buckets.
- If deletion still fails, check permissions, object lock, policies, and versioned contents.
Related reading
- How to delete untagged images from AWS ECR Container Registry
- How to deploy a React NodeJS Express application to AWS?
- How to deploy TURN servercoturn inside Kubernetes
- How to describe AWS Lambda function test events in CloudFormation template?
- how to delete tiller from kubernetes cluster
- How to deploy a bunch of yaml files?
- How to describe Security Groups for a VPC?
- How To Design a Distributed Logging System in Kubernetes?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.