Skip to content

Terraform Level 1, Task 28: Modifying Infrastructure with Terraform (S3 Versioning)

Today's task was a powerful lesson in the true purpose of Terraform. So far, I've been using it to create new resources. This task taught me how to use Terraform to manage and update resources that are already defined in my code. My objective was to take an existing S3 bucket definition and enable versioning on it, a critical feature for data protection.

This was a fantastic demonstration of "Infrastructure as Code" as a "living" document, not just a one-time script. I learned how Terraform uses its state file to understand what it's already managing and how terraform plan can intelligently figure out what to change instead of trying to create a new, duplicate resource. This document is my very detailed, first-person guide to that entire process.

Table of Contents


The Task

My objective was to use Terraform to modify an existing S3 bucket definition in my main.tf file. The requirements were: 1. The target S3 bucket was named datacenter-s3-25994. 2. I needed to enable versioning for this bucket. 3. All this had to be done by updating the main.tf file.


My Step-by-Step Solution

The process involved a simple one-line code change, followed by the plan and apply workflow.

Phase 1: Editing the Code

  1. I connected to the iac-server and navigated to the /home/bob/terraform directory.
  2. I opened the existing main.tf file, which already contained the definition for the S3 bucket.
  3. I located the aws_s3_bucket resource block and added the new versioning block inside it.

Before (in main.tf):

resource "aws_s3_bucket" "s3_ran_bucket" {
  bucket = "datacenter-s3-25994"
  acl    = "private"

  tags = {
    Name = "datacenter-s3-25994"
  }
}

After (in main.tf):

resource "aws_s3_bucket" "s3_ran_bucket" {
  bucket = "datacenter-s3-25994"
  acl    = "private"

  # This is the new block I added to enable versioning
  versioning {
    enabled = true
  }

  tags = {
    Name = "datacenter-s3-25994"
  }
}

Phase 2: The Terraform Workflow

From my terminal, I executed the core commands. 1. Initialize: terraform init. 2. Plan: terraform plan. This was the most insightful step. The plan did not say "1 to add." Instead, it showed ~ 1 to change. This ~ symbol means an in-place update. It confirmed that Terraform knew the bucket already existed (or was about to be created) and just needed to be modified. 3. Apply: terraform apply. After I confirmed with yes, Terraform connected to AWS and applied the versioning change to the bucket. The success message Apply complete! Resources: 0 added, 1 changed, 0 destroyed. confirmed the task was done.


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

- Managing Change (The Core of IaC): This task demonstrated the real power of Infrastructure as Code. The code in my main.tf file is not just a one-time script; it is the single source of truth for my desired infrastructure. When I need to change my infrastructure (like enabling a feature on a bucket), I don't log into the AWS console and click buttons. I change the code. - Terraform State (terraform.tfstate): How did Terraform know not to create a new bucket? Because of the state file (terraform.tfstate). This is a JSON file that Terraform maintains in the same directory. It's a database that maps the resources in my code (like aws_s3_bucket.s3_ran_bucket) to the real-world resources in my cloud account (like the datacenter-s3-25994 bucket). - How plan Works: When I run terraform plan, Terraform does a three-way comparison: 1. The Code (Desired State): What I want (e.g., a bucket with versioning enabled). 2. The State File (Recorded State): What Terraform thinks exists (e.g., a bucket with no versioning). 3. The Real World (Actual State): What AWS reports is actually provisioned. Terraform saw a difference between my desired state (code) and the recorded state, so it created a plan to update the resource. - S3 Bucket Versioning: This is a critical data protection feature. When versioning is enabled, S3 never permanently deletes a file on an overwrite or delete operation. - On Overwrite: If I upload a new file with the same name, S3 keeps the old version and marks the new one as the "current" version. - On Delete: If I delete a file, S3 doesn't actually delete it. It just inserts a "delete marker." This provides an invaluable safety net, allowing me to recover data from accidental deletions or to roll back to a previous version of a file.


Deep Dive: A Line-by-Line Explanation of the Change

The change was just one small block, but it triggered a specific API call to modify the bucket's properties.

[Image of an S3 bucket with versioning enabled]

# This is the resource block I edited.
resource "aws_s3_bucket" "s3_ran_bucket" {
  bucket = "datacenter-s3-25994"
  acl    = "private"

  # This is the new block I added.
  # 'versioning' is an argument block within the 'aws_s3_bucket' resource.
  # By adding this block, I am telling Terraform to manage the
  # versioning configuration of the bucket.
  versioning {
    # Setting 'enabled = true' is the declarative way of saying
    # "I want the desired state of this bucket to have versioning enabled."
    enabled = true
  }

  tags = {
    Name = "datacenter-s3-25994"
  }
}
When I ran terraform apply, Terraform saw this new versioning block and made an API call to AWS equivalent to s3:PutBucketVersioning, setting the status to "Enabled".


Common Pitfalls

- Confusing resource and data: If the bucket was created outside of this Terraform configuration, I would have had to use a data "aws_s3_bucket" block to look it up. But since the bucket was defined in my code, I was correctly modifying the existing resource block. - Cost Implications: Enabling versioning is a best practice, but it's important to remember that it has cost implications. Since I am now storing every single version of every file, my S3 storage costs can increase. It's common to pair versioning with lifecycle_rule blocks to automatically delete old versions after a certain number of days. - Manually Changing the Bucket: The biggest sin in an IaC workflow is to log into the AWS console and enable versioning by clicking the button. The next time I run terraform plan, Terraform would detect this "drift" and show that the real-world state does not match my code (which still says no versioning). This can cause confusion. The code must always be the single source of truth.


Exploring the Essential Terraform Commands

- terraform init: Initializes the directory, downloading providers. - terraform plan: The most important command for this task. It showed me a ~ (change) symbol instead of a + (create) symbol, which confirmed my understanding that Terraform would update the existing resource. - terraform apply: Executed the plan and performed the modification to the S3 bucket. - terraform state list: A useful command to see all the resources Terraform is currently managing in its state file (e.g., aws_s3_bucket.s3_ran_bucket). - terraform state show 'aws_s3_bucket.s3_ran_bucket': A very useful command to see all the attributes of a resource as they are recorded in the state file. I could have used this after my change to see the versioning block reflected in the state.