Skip to main content

Module 5, State: The Most Important Concept

What is State

Terraform state is a file (terraform.tfstate) that maps your configuration to real-world resources. It is the source of truth for what Terraform manages.

{
"version": 4,
"resources": [
{
"type": "aws_s3_bucket",
"name": "main",
"instances": [{ "attributes": { "id": "my-bucket", "arn": "arn:aws:s3:::my-bucket" } }]
}
]
}

Without state, Terraform would have no idea what already exists. Every terraform plan would show everything as "to be created" even if it exists in AWS.

State Rules

Never:

  • Commit terraform.tfstate to git, it contains secrets in plaintext
  • Edit it manually, you will corrupt it
  • Delete it, you will lose track of all resources
  • Let two people run terraform apply simultaneously, state will corrupt

Always:

  • Add *.tfstate and *.tfstate.backup to .gitignore
  • Use remote state for team environments
  • Enable state locking

State Commands

terraform state list # list all resources in state
terraform state show aws_s3_bucket.main # show one resource in detail
terraform state rm aws_s3_bucket.main # remove from state (NOT from AWS)
terraform import aws_s3_bucket.main my-bucket # import existing AWS resource
terraform state mv aws_s3_bucket.main aws_s3_bucket.artifacts # rename in state
terraform state pull > backup.tfstate # backup remote state locally
tip

terraform state rm is how you stop managing a resource without deleting it. terraform state mv is how you rename a resource in your config without destroying and recreating it in AWS.

Drift Detection

Drift occurs when someone changes infrastructure outside of Terraform:

terraform refresh # sync state with real AWS state
terraform plan # shows drift as changes to be reverted

Example: someone manually adds a tag in the AWS console. terraform plan shows:

~ resource "aws_s3_bucket" "main" {
~ tags = {
- "ManualTag" = "added-in-console"
}
}

Terraform will remove the manual tag on next apply. Never manually change Terraform-managed resources.

Remote State, S3 + DynamoDB

Local state is only for personal projects. Teams must use remote state:

terraform {
backend "s3" {
bucket = "my-company-terraform-state"
key = "finpay/production/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}

Bootstrap (run once before everything else)

# bootstrap/main.tf
resource "aws_s3_bucket" "terraform_state" {
bucket = "my-company-terraform-state"
}

resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration { status = "Enabled" }
}

resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-state-lock"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute { name = "LockID"; type = "S" }
}

Lab 4, Remote State Setup

# Step 1: Create bootstrap resources
cd bootstrap && terraform init && terraform apply

# Step 2: Add backend block to your project's versions.tf
# Step 3: Re-initialise, Terraform migrates local state to S3
terraform init
# Answer "yes" to migrate existing state

Knowledge Check

  1. What is the purpose of terraform.tfstate?
  2. Why should you never commit terraform.tfstate to git?
  3. What is drift and how does Terraform detect it?
  4. What is the difference between terraform state rm and terraform destroy -target?
  5. What does state locking prevent?
  6. You deleted a resource from the console but not from Terraform. What happens on next terraform plan?
  7. A new team member runs terraform apply while you are already running one. What prevents conflict?
Exam Mapping
  • HashiCorp Associate Obj 9: Understand Terraform Cloud capabilities
  • HashiCorp Associate Obj 6c: Describe when to use terraform import
  • AWS DevOps Pro Domain 2: Infrastructure as Code, state management