Terraform Level 1, Task 12: Creating a Public S3 Bucket (The Modern Way)¶
Today's task was a masterclass in how cloud infrastructure and the tools that manage it are constantly evolving. My objective was to create a publicly accessible S3 bucket. While this seems simple, I discovered that recent AWS security enhancements have made the "old" way of doing this with Terraform obsolete, leading to a complex troubleshooting journey.
This document is my detailed, first-person account of that journey. I'll cover the initial failures, the diagnosis of the root cause (default "Block Public Access" settings), and the final, modern, multi-resource solution that is now required to correctly create a public S3 bucket with Terraform.
Table of Contents¶
- The Task
- My Step-by-Step Solution (The One That Worked)
- My Troubleshooting Journey: A Two-Part Failure
- Why Did I Do This? (The "What & Why")
- Deep Dive: The New Way to Create Public S3 Buckets
- Common Pitfalls
- Exploring the Commands I Used
The Task¶
My objective was to use Terraform to create a new, publicly accessible AWS S3 bucket. The requirements were:
1. All code had to be in a main.tf file.
2. The bucket's name had to be datacenter-s3-xxxx.
3. The bucket needed to be created in the us-east-1 region.
4. It had to be made publicly accessible using an ACL.
My Step-by-Step Solution (The One That Worked)¶
The solution required a multi-resource approach to first create the bucket, then disable the default security blocks, and finally apply the public ACL.
Phase 1: Writing the Code¶
In the /home/bob/terraform directory, I created my main.tf file with the following complete and correct code.
provider "aws" {
region = "us-east-1"
# This line, found in provider.tf, was critical for the lab environment
s3_use_path_style = true
}
# 1. Define the bucket itself, without any ACL.
resource "aws_s3_bucket" "datacenter_bucket_resource" {
bucket = "datacenter-s3-3563" # My specific bucket name for this attempt
tags = {
Name = "datacenter-s3-3563"
}
}
# 2. Add a resource to explicitly disable the "Block Public Access" settings.
resource "aws_s3_bucket_public_access_block" "public_access_block" {
bucket = aws_s3_bucket.datacenter_bucket_resource.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
# 3. Add a resource to manage ownership controls, a prerequisite for ACLs.
resource "aws_s3_bucket_ownership_controls" "ownership_controls" {
bucket = aws_s3_bucket.datacenter_bucket_resource.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}
# 4. Finally, apply the public ACL, but only after the other resources are created.
resource "aws_s3_bucket_acl" "bucket_acl" {
depends_on = [
aws_s3_bucket_public_access_block.public_access_block,
aws_s3_bucket_ownership_controls.ownership_controls,
]
bucket = aws_s3_bucket.datacenter_bucket_resource.id
acl = "public-read"
}
Phase 2: The Terraform Workflow¶
From my terminal in the same directory, I executed the standard workflow.
1. Initialize: terraform init
2. Plan: terraform plan (This showed a plan to create 4 new resources).
3. Apply: terraform apply
After I confirmed with yes, Terraform created all four resources in the correct order, and the process completed successfully.
My Troubleshooting Journey: A Two-Part Failure¶
This was a complex problem that I had to solve in two stages.
-
Failure 1: The DNS Lookup Error
- Symptom: My very first
terraform applyfailed with ano such hosterror when trying to connect to a URL likehttp://datacenter-s3-5847.aws:4566/. - Diagnosis: This was a DNS error specific to the lab's AWS simulator. The provider was trying to use "virtual-hosted-style" S3 URLs, which the simulator's network couldn't resolve.
- Solution: I had to add
s3_use_path_style = trueto theprovider "aws"block (which was correctly placed inprovider.tf). This forced Terraform to use "path-style" URLs (http://aws:4566/datacenter-s3-5847/), which the simulator could handle.
- Symptom: My very first
-
Failure 2: The Validation Error
- Symptom: After fixing the DNS issue,
terraform applyran successfully! However, the lab's validation script failed, saying thepublic s3 bucket doesn't exist. - Diagnosis: This was the most confusing part. My code had
acl = "public-read". Why wasn't the bucket public? I learned that recent versions of the AWS provider align with new AWS security defaults. All new buckets have "Block all public access" enabled by default. This new setting overrides the oldaclargument, so my bucket was being created but remained private. The validation script saw it wasn't public and reported that the "public bucket" didn't exist. - Solution: The fix was to adopt the new, modern Terraform approach: use the
aws_s3_bucket_public_access_blockresource to explicitly turn off these protections, and then use the separateaws_s3_bucket_aclresource to apply thepublic-readsetting.
- Symptom: After fixing the DNS issue,
Why Did I Do This? (The "What & Why")¶
- S3 (Simple Storage Service): This is AWS's object storage service, designed for storing and retrieving any amount of data. A bucket is the root-level container for your objects (files).
- S3 Public Access: Making an S3 bucket public is a powerful feature, often used for hosting static websites, but it's also a significant security consideration. Because of past security incidents, AWS has made it intentionally harder to make buckets public. The "Block all public access" feature is a global safety switch that is now on by default.
- ACL (Access Control List): This is one of the methods for controlling access to S3 buckets. The public-read ACL is a "canned" policy that grants read-only access to anyone on the internet.
Deep Dive: The New Way to Create Public S3 Buckets¶
My final, successful main.tf file demonstrates the modern, four-part process required by recent AWS provider versions.
aws_s3_bucket: First, you create the bucket itself, with no permission settings. This is just the container.aws_s3_bucket_public_access_block: This is the master switch. You must create this resource and set all four arguments (block_public_acls, etc.) tofalse. This tells AWS, "I know what I'm doing; I intend to make this bucket public."aws_s3_bucket_ownership_controls: This is another new requirement. It controls how ownership of uploaded objects is handled.BucketOwnerPreferredis the standard setting required to allow ACLs to function.aws_s3_bucket_acl: Only after the first three resources are in place can you apply thepublic-readACL. Thedepends_onblock is a crucial addition that explicitly tells Terraform the correct order of operations, preventing race conditions during creation.
Common Pitfalls¶
- Using Only the acl Argument: As I discovered, on modern provider versions, this is no longer sufficient and will result in a private bucket, even if Terraform doesn't return an error.
- Forgetting s3_use_path_style: In a lab environment, forgetting this provider setting can lead to confusing DNS errors that have nothing to do with the resource configuration itself.
- Bucket Name Already Exists: S3 bucket names must be globally unique across all AWS accounts. If I had tried to use a common name, my apply command would have failed with a BucketAlreadyExists error.
Exploring the Commands I Used¶
- terraform init: Prepared my working directory by downloading the aws provider plugin.
- terraform plan: Showed me a "dry run" plan. My final, successful plan correctly showed 4 resources to be created.
- terraform apply: Executed the plan and created the public S3 bucket after I confirmed with yes.