Lab 00: State Operations
Overview
In this lab, you'll work with Terraform state directly. You'll learn to inspect state, move resources, and use the declarative moved and removed blocks for safe refactoring.
Time: 2-3 hours
Cost: $0.00 (S3 buckets only)
Difficulty: Intermediate
Learning Objectives
By the end of this lab, you will be able to:
- Inspect resources using
terraform state listandterraform state show - Move/rename resources using
terraform state mv - Remove resources from state using
terraform state rm - Use
movedblocks for declarative refactoring - Use
removedblocks to cleanly remove resources from state
Prerequisites
- Completed Week 00 and Week 01 labs
- Terraform >= 1.9.0
- AWS credentials configured
- Existing S3 state bucket from Week 00
Part 1: Setup - Create Resources to Manage (20 min)
First, let's create some infrastructure that we'll manipulate throughout this lab.
1.1 Create Working Directory
cd week-02/lab-00
mkdir -p student-work
cd student-work1.2 Create Initial Configuration
Create main.tf:
terraform {
required_version = ">= 1.9.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# Use your existing state bucket from Week 00
backend "s3" {
bucket = "YOUR-STATE-BUCKET-NAME"
key = "week-02/lab-00/terraform.tfstate"
region = "us-east-1"
use_lockfile = true
}
}
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
Course = "terraform-course"
Week = "02"
Lab = "00"
AutoTeardown = "24h"
}
}
}Create variables.tf:
variable "student_name" {
description = "Your name or identifier (used in resource names)"
type = string
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}Create buckets.tf:
# We'll create several buckets to practice state operations
resource "aws_s3_bucket" "data" {
bucket = "tf-state-lab-${var.student_name}-data-${var.environment}"
}
resource "aws_s3_bucket" "logs" {
bucket = "tf-state-lab-${var.student_name}-logs-${var.environment}"
}
resource "aws_s3_bucket" "backup" {
bucket = "tf-state-lab-${var.student_name}-backup-${var.environment}"
}Create outputs.tf:
output "data_bucket_name" {
description = "Name of the data bucket"
value = aws_s3_bucket.data.id
}
output "logs_bucket_name" {
description = "Name of the logs bucket"
value = aws_s3_bucket.logs.id
}
output "backup_bucket_name" {
description = "Name of the backup bucket"
value = aws_s3_bucket.backup.id
}Create terraform.tfvars:
student_name = "YOUR-NAME-HERE"
environment = "dev"1.3 Deploy Initial Infrastructure
terraform init
terraform fmt
terraform validate
terraform plan
terraform applyCheckpoint: You should have 3 S3 buckets created.
Part 2: Inspecting State (20 min)
Now let's explore what's in our state file.
2.1 List All Resources
terraform state listExpected output:
aws_s3_bucket.backup
aws_s3_bucket.data
aws_s3_bucket.logs
2.2 Show Resource Details
Pick a resource and inspect it:
terraform state show aws_s3_bucket.dataQuestions to answer (write answers in your notes):
- What is the bucket's ARN?
- What region is it in?
- What is the
idattribute? - Are there any tags beyond the default tags?
2.3 Pull Full State (Advanced)
You can dump the entire state as JSON:
terraform state pull | head -50Or use jq for specific queries:
terraform state pull | jq '.resources[] | .type + "." + .name'Part 3: Moving Resources in State (30 min)
Scenario
Your team has decided to rename resources to follow a new naming convention: bucket_data instead of data.
3.1 The Wrong Way (Don't Do This!)
If you just rename the resource in your .tf file without updating state, Terraform will:
- Plan to destroy the old resource
- Plan to create a new resource
This would delete your data! Let's see this (but not apply it):
In buckets.tf, rename aws_s3_bucket.data to aws_s3_bucket.bucket_data. Then run:
terraform planObserve: Terraform wants to destroy and recreate. This is dangerous!
Revert your change - put the resource name back to data.
3.2 The Right Way: state mv (Dry Run First!)
First, always do a dry run:
terraform state mv -dry-run aws_s3_bucket.data aws_s3_bucket.bucket_dataIf that looks correct, do the actual move:
terraform state mv aws_s3_bucket.data aws_s3_bucket.bucket_data3.3 Update Configuration to Match
Now update buckets.tf to match the new state:
# Changed from: resource "aws_s3_bucket" "data"
resource "aws_s3_bucket" "bucket_data" {
bucket = "tf-state-lab-${var.student_name}-data-${var.environment}"
}Update outputs.tf as well:
output "data_bucket_name" {
description = "Name of the data bucket"
value = aws_s3_bucket.bucket_data.id # Changed reference
}3.4 Verify No Changes
terraform planExpected: "No changes. Your infrastructure matches the configuration."
๐ You successfully renamed a resource without destroying it!
Part 4: Using Moved Blocks (Better Way!) (30 min)
The moved block is the modern, declarative way to refactor. It's safer because it's version controlled and works across team members.
4.1 Set Up Another Rename
Let's rename aws_s3_bucket.logs to aws_s3_bucket.bucket_logs using a moved block.
Add to buckets.tf:
# Declare the move
moved {
from = aws_s3_bucket.logs
to = aws_s3_bucket.bucket_logs
}
# Update the resource name
resource "aws_s3_bucket" "bucket_logs" {
bucket = "tf-state-lab-${var.student_name}-logs-${var.environment}"
}Update outputs.tf:
output "logs_bucket_name" {
description = "Name of the logs bucket"
value = aws_s3_bucket.bucket_logs.id # Changed reference
}4.2 Plan and Apply
terraform planObserve: The plan shows the resource will be moved, not destroyed/recreated.
terraform apply4.3 Verify
terraform state listYou should see aws_s3_bucket.bucket_logs instead of aws_s3_bucket.logs.
4.4 Clean Up the Moved Block
After applying, you can remove the moved block from your configuration. It's served its purpose.
Part 5: Using Removed Blocks (30 min)
Scenario
You decide the backup bucket is no longer needed by Terraform (maybe another team will manage it), but you don't want to destroy the actual bucket.
5.1 The Wrong Way
If you just delete the resource from your .tf file, Terraform will plan to destroy the bucket!
5.2 The Right Way: removed Block
Add to buckets.tf:
removed {
from = aws_s3_bucket.backup
lifecycle {
destroy = false # Keep the infrastructure, just remove from state
}
}Delete the original resource block for aws_s3_bucket.backup and its output.
5.3 Apply
terraform planObserve: The plan shows the resource will be "forgotten" but not destroyed.
terraform apply5.4 Verify
terraform state listThe backup bucket is no longer in state, but check the AWS console - the bucket still exists!
5.5 (Optional) Destroy with Removed Block
If you wanted to actually destroy the resource while using a removed block, you would set:
removed {
from = aws_s3_bucket.backup
lifecycle {
destroy = true # Actually destroy the resource
}
}Part 6: State rm Command (15 min)
Let's practice the imperative approach to removing from state.
6.1 Create a Temporary Resource
Add to buckets.tf:
resource "aws_s3_bucket" "temp" {
bucket = "tf-state-lab-${var.student_name}-temp-${var.environment}"
}terraform apply6.2 Remove from State
terraform state rm aws_s3_bucket.temp6.3 Observe the Result
terraform state list # temp bucket is gone from state
terraform plan # Terraform wants to CREATE the resource (it exists but TF doesn't know)6.4 Clean Up
The bucket still exists in AWS! Options:
Option A: Delete manually in AWS Console, then remove the resource from your .tf file
Option B: Run terraform apply to bring it back under management, then terraform destroy
For this lab, let's go with Option A - delete it from the console and remove the resource block.
Part 7: Cleanup (10 min)
7.1 Final State Check
terraform state listYou should have:
aws_s3_bucket.bucket_dataaws_s3_bucket.bucket_logs
7.2 Destroy
terraform destroySubmission
Create a PR with:
- Your completed
student-work/directory - A
NOTES.mdfile answering:- What's the difference between
terraform state mvand amovedblock? - When would you use
terraform state rmvs aremovedblock? - What happens to infrastructure when you remove it from state?
- Describe a real scenario where you'd need to use
movedblocks.
- What's the difference between
Common Mistakes
- Forgetting to update config after
state mv- State and config must match - Using
state rmwhen you meandestroy-state rmdoesn't delete infrastructure! - Not using dry-run - Always use
-dry-runbeforestate mv - Leaving
movedblocks forever - Remove them after they've been applied everywhere
Stretch Goals
If you finish early:
- Module Refactor: Try moving a resource INTO a module using
movedblocks - State Pull/Push: Experiment with
terraform state pullandterraform state push(carefully!) - Multiple Moves: Chain multiple
movedblocks in one apply