The recurring headline of data breaches caused by misconfigured Amazon S3 buckets has become a cautionary tale for cloud architects and DevOps engineers. In the majority of these incidents, the root cause is a simple but catastrophic error: a bucket accidentally left open to the public. To combat this, AWS introduced S3 Block Public Access, a set of powerful guardrails designed to override bucket policies and Access Control Lists (ACLs) that would otherwise expose sensitive data to the open internet.
For organizations utilizing Infrastructure as Code (IaC), Terraform provides the necessary precision to implement these guardrails consistently across environments. By leveraging the aws_s3_bucket_public_access_block resource, engineers can ensure that security is not a manual checklist item but a coded requirement of the infrastructure deployment pipeline.
The Mechanics of S3 Block Public Access
S3 Block Public Access is not a single toggle but a collection of four independent settings. These settings operate as a layer of protection that sits "above" the bucket policy and ACLs. Even if a developer accidentally attaches a wildcard policy ("Principal": "*") to a bucket, these block settings can intercept and negate that permission.
The four settings are categorized into two main areas: those that govern ACLs and those that govern Bucket Policies.
ACL-Based Protections
Access Control Lists (ACLs) are a legacy method of managing S3 access. While bucket policies are generally preferred for modern architectures, ACLs are still widely present.
- BlockPublicAcls: This setting focuses on prevention. It causes AWS to reject any PUT request that includes a public ACL. If a user or an automated script attempts to upload an object with a public ACL, the request will be denied. This prevents new objects from being uploaded with public access.
- IgnorePublicAcls: This setting focuses on remediation. It instructs S3 to ignore all public ACLs on the bucket and any objects contained within it. If public ACLs were already in place before this setting was enabled, they are simply treated as if they do not exist.
Policy-Based Protections
Bucket policies provide granular, JSON-based control over who can access a bucket. However, a single character error in a JSON policy can open a bucket to the world.
- BlockPublicPolicy: This is a preventative control. It rejects any attempt to change the bucket policy if the proposed policy would grant public access. This effectively blocks the attachment of wildcard policies that would make the bucket public.
- RestrictPublicBuckets: This is a restrictive control. It limits access to buckets that have public policies to only AWS service principals and authorized users within the bucket owner's account.
| Setting | Primary Purpose | Effect on New Requests | Effect on Existing Config |
|---|---|---|---|
| BlockPublicAcls | Prevent public ACLs | Rejects PUT requests with public ACLs | N/A |
| IgnorePublicAcls | Negate public ACLs | Ignores all public ACLs | Overrides existing public ACLs |
| BlockPublicPolicy | Prevent public policies | Rejects policy changes that grant public access | N/A |
| RestrictPublicBuckets | Limit public policy scope | Restricts access to account owners/AWS services | Overrides existing public policies |
Implementing Public Access Block in Terraform
In Terraform, the primary resource for managing these settings is aws_s3_bucket_public_access_block. While the AWS provider defaults to creating private S3 buckets, explicitly defining the aws_s3_bucket_public_access_block is the gold standard for security because it provides granular control and overrides other potential configuration drift.
Minimal Configuration
For a basic implementation, you only need to associate the block resource with a specific bucket ID.
hcl
resource "aws_s3_bucket_public_access_block" "example" {
bucket = "my-bucket"
}
Comprehensive Hardening
To achieve a "maximum security" posture, all four parameters should be set to true. This ensures that no matter how a policy or ACL is manipulated, the bucket remains private.
```hcl
resource "awss3bucketpublicaccessblock" "securebucketblock" {
bucket = awss3bucket.mysecure_bucket.id
blockpublicacls = true
blockpublicpolicy = true
ignorepublicacls = true
restrictpublicbuckets = true
}
```
In this configuration, the block_public_policy setting is particularly critical. It acts as the primary shield against the most common cause of leaks: the accidental application of a public-read bucket policy.
Advanced Scenario: S3 Website Hosting with Controlled Access
There are legitimate use cases where a bucket is configured for website hosting but should not be open to the general public—for example, during a staging phase or for internal corporate portals.
In such scenarios, you can enable the S3 website hosting configuration while simultaneously blocking public access via the aws_s3_bucket_public_access_block resource. This allows you to keep the infrastructure ready (index and error documents defined) while maintaining a strict security perimeter.
To allow specific trusted networks (like an office IP or a VPN subnet) while keeping the general public blocked, you can combine the public access block with a bucket policy containing a CIDR allowlist.
Implementation Example
The following configuration demonstrates how to set up a website-enabled bucket that is blocked from the public but ready for specific IP access.
```hcl
Define trusted IP ranges in locals for maintainability
locals {
allowed_cidr = [
"203.0.113.10/32", # Your office IP
"198.51.100.0/24", # Your VPN subnet
"192.0.2.0/24" # Additional trusted network
]
}
Create the S3 bucket
resource "awss3bucket" "website" {
bucket = "your-static-website-bucket-name"
}
Public Access Block: This blocks the website endpoint from the public
resource "awss3bucketpublicaccessblock" "website" {
bucket = awss3_bucket.website.id
blockpublicacls = true
blockpublicpolicy = true # Key setting that blocks public access
ignorepublicacls = true
restrictpublicbuckets = true
}
Enable website hosting configurations
resource "awss3bucketwebsiteconfiguration" "website" {
bucket = awss3bucket.website.id
index_document {
suffix = "index.html"
}
error_document {
key = "error.html"
}
}
```
Strategic Security Best Practices
Implementing the aws_s3_bucket_public_access_block resource is a significant step, but it should be part of a wider, layered security strategy.
Account-Level vs. Bucket-Level Settings
AWS allows S3 Block Public Access to be configured at both the bucket level and the account level. Account-level settings act as a master switch; they apply to every bucket within that AWS account.
It is a critical architectural recommendation to enable all four Block Public Access settings at both the bucket level and the account level. Account-level settings can override bucket-level configurations, providing a final safety net if a bucket-level resource is accidentally deleted or modified in Terraform.
The Role of Versioning
While Block Public Access prevents current data from being exposed, it does not prevent accidental deletion or corruption. Enabling versioning on your S3 bucket is highly recommended. Versioning ensures that even if an object is accidentally modified or if a configuration error occurs, previous versions of the data remain protected and recoverable.
Adhering to the Principle of Least Privilege
When you do need to grant access to a bucket, avoid using ACLs due to their management complexity. Instead, utilize bucket policies that follow the principle of least privilege. This means granting only the minimum necessary permissions to specific IAM users, roles, or services. For example, instead of granting s3:* permissions, grant only s3:GetObject for a specific prefix.
Monitoring and Auditability
Security is not a "set and forget" task. To maintain a robust security posture, implement the following:
- Logging and Monitoring: Enable S3 server access logging and integrate with AWS CloudTrail. This helps detect unauthorized access attempts and provides an audit trail of who changed a configuration.
- Automation: Use Terraform to ensure consistency. By defining your public access blocks in code, you create a version-controlled history of your security posture.
- Regular Testing: Use scripts or security auditing tools to regularly verify that buckets are not public.
Summary of S3 Management Resources
When working with S3 in Terraform, the aws_s3_bucket_public_access_block is often used in conjunction with several other related resources. Understanding the ecosystem of these resources is key to full bucket lifecycle management.
| Terraform Resource | Function |
|---|---|
aws_s3_bucket |
Creates the core S3 bucket resource. |
aws_s3_bucket_public_access_block |
Manages the four public access block guardrails. |
aws_s3_account_public_access_block |
Manages public access settings at the AWS account level. |
aws_s3_bucket_policy |
Defines detailed JSON permissions for bucket access. |
aws_s3_bucket_acl |
Manages legacy Access Control Lists. |
aws_s3_bucket_website_configuration |
Configures the bucket for static website hosting. |
aws_s3_access_point |
Creates shared access points for large-scale data sets. |
Operational Considerations and Potential Conflicts
Integrating these settings into an existing environment requires caution. Terraform may encounter errors if it detects conflicts between the desired state in the code and the actual state in AWS.
For instance, if you attempt to apply an aws_s3_bucket_public_access_block with block_public_policy = true while a current bucket policy exists that grants public access, the API request may fail or result in a conflict. It is often necessary to remove the permissive policy before applying the block, or to apply them in a specific sequence.
Furthermore, always maintain clear documentation of any exceptions granted. If a specific bucket must be public (e.g., for public assets of a website), this should be documented with the business justification and the date the exception was granted. This documentation is crucial for security audits and troubleshooting when multiple team members are managing the infrastructure.
Conclusion
The implementation of aws_s3_bucket_public_access_block in Terraform is one of the most impactful security measures a cloud engineer can take to protect data. By systematically enabling block_public_acls, ignore_public_acls, block_public_policy, and restrict_public_buckets, you create a multi-layered defense that mitigates the risk of human error and misconfiguration.
The true power of this approach lies in the synergy between bucket-level and account-level blocks. While bucket-level resources provide the granularity needed for specific application requirements, account-level blocks provide the organizational guardrails that ensure no bucket—regardless of who created it—is accidentally exposed. When combined with S3 versioning, strict adherence to the principle of least privilege, and comprehensive monitoring, Terraform allows for the creation of a storage architecture that is not only functional and scalable but inherently secure against the most common vectors of cloud data breaches.
Sources
- https://discuss.hashicorp.com/t/aws-s3-block-all-public-access/27819
- https://github.com/OneUptime/blog/blob/master/posts/2026-02-23-block-public-access-to-s3-buckets-in-terraform/README.md
- https://awsfundamentals.com/terraform/s3/s3-bucket-public-access-block
- https://oneuptime.com/blog/post/2026-02-23-block-public-access-to-s3-buckets-in-terraform/view
- https://nulldog.com/terraform-prevent-public-s3-objects-secure-your-buckets
- https://dev.to/jajera/block-s3-website-with-terraform-keep-ip-access-ready-5ema