Skip to content

Terraform Level 1, Task 13: Creating a Secure, Private S3 Bucket

Today's task was a crucial lesson in cloud security. After learning the complex process of creating a public S3 bucket in a previous task, this objective was the opposite: create a private S3 bucket, which is the standard and recommended configuration for most use cases.

This exercise was fantastic because it taught me about the "secure by default" posture of modern cloud services and how Terraform providers align with these best practices. I learned that creating a private bucket is now incredibly simple, as I just need to let the defaults do their job. This document is my very detailed, first-person guide to the entire process, including a deep dive into both the provider.tf and main.tf files.

Table of Contents


The Task

My objective was to use Terraform to create a new, private 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 nautilus-s3-10156. 3. The bucket needed to be created in the us-east-1 region. 4. It must block all public access.


My Step-by-Step Solution

The solution was refreshingly simple compared to creating a public bucket, as I was able to rely on secure defaults.

Phase 1: Writing the Code

In the /home/bob/terraform directory, I created my main.tf file. I did not need to edit the existing provider.tf. I wrote the following minimal and declarative code.

# The main.tf file for creating the private bucket.
# Notice the absence of any 'acl' or public access block resources.

resource "aws_s3_bucket" "nautilus_s3_bucket_resource" {
  bucket = "nautilus-s3-10156"

  tags = {
    Name = "nautilus-s3-10156"
  }
}

Phase 2: The Terraform Workflow

From my terminal in the same directory, I executed the three core commands.

  1. Initialize: terraform init
  2. Plan: terraform plan. The output showed that it would create one aws_s3_bucket resource.
  3. Apply: terraform apply. After I confirmed with yes, Terraform created the S3 bucket in my AWS account. The success message confirmed the task was done.

Why Did I Do This? (The "What & Why")

- S3 (Simple Storage Service): This is AWS's object storage service, like a limitless hard drive in the cloud. A bucket is the root-level container for your files. - The Golden Rule of S3: Private by Default: This is the core concept of this task. In response to security concerns, AWS made a significant change in recent years. Now, when you create a new S3 bucket, it is completely private by default. All four "Block Public Access" settings are turned ON. This means no one can access the bucket's contents from the internet unless explicitly granted permission through other means (like IAM policies or signed URLs). - Simplicity through Secure Defaults: The beauty of this task is what I didn't have to do. - I did not need to use the old acl = "private" argument. - I did not need to create an aws_s3_bucket_public_access_block resource to turn on the blocks, because they are already on by default. By simply creating the bucket with no special permission settings, I was embracing the secure default, which is exactly what the task required.


Deep Dive: A Line-by-Line Explanation of the Terraform Files

This task involved two files. Understanding both was key to understanding the full picture of the lab environment.

The provider.tf File (The Lab Environment Setup)

This file defines how Terraform connects to the "cloud," which in this lab is a local simulator.

# The terraform block is for core settings, like pinning provider versions.
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.91.0"
    }
  }
}

# The provider block configures the connection for a specific provider.
provider "aws" {
  region = "us-east-1"
  # These next lines are NOT standard for real AWS accounts. They are
  # for the lab's local AWS simulator (like LocalStack).
  skip_credentials_validation = true
  skip_requesting_account_id  = true
  s3_use_path_style           = true # This was the fix for a previous DNS error.

  # The 'endpoints' block is the biggest clue that this is a simulator.
  # It redirects all AWS API calls to a local service instead of the
  # real AWS internet endpoints.
  endpoints {
    s3 = "http://aws:4566"
    # ... and many other services
  }
}

The main.tf File (My Solution)

This file defines the actual infrastructure I wanted to build. It's very simple because I am relying on the default settings.

# This is the resource block that defines my S3 bucket.
# "aws_s3_bucket" is the Resource TYPE, telling Terraform I want a bucket.
# "nautilus_s3_bucket_resource" is the local NAME I use to refer to this
# bucket within my Terraform code.
resource "aws_s3_bucket" "nautilus_s3_bucket_resource" {

  # The 'bucket' argument is required and sets the globally unique name for the bucket.
  bucket = "nautilus-s3-10156"

  # Standard tagging to give the bucket a recognizable name in the AWS Console.
  tags = {
    Name = "nautilus-s3-10156"
  }
}

Common Pitfalls

  • Over-complicating the Code: The most common mistake would be to try and re-use the complex, multi-resource code from the "public bucket" task. For a private bucket, all those extra aws_s3_bucket_public_access_block and aws_s3_bucket_acl resources are unnecessary.

  • Bucket Name Already Exists: S3 bucket names must be globally unique across all AWS accounts. If I had tried to use a common name that was already taken, my apply command would have failed with a BucketAlreadyExists error.

  • Forgetting terraform init: As always, trying to run plan or apply in a new project without first running init will fail because the necessary provider plugins won't be downloaded.

Exploring the Commands I Used The workflow was the standard, three-step Terraform process:

  • terraform init: Prepared my working directory by downloading the aws provider plugin defined in provider.tf.

  • terraform plan: Showed me a "dry run" plan, confirming that it would create one aws_s3_bucket resource using all the default security settings.

  • terraform apply: Executed the plan and created the private S3 bucket in the simulated AWS environment after I confirmed with yes.