Day 4 — Terraform State Management & Remote Backend

Today’s learning was all about one of the most important parts of Terraform that many beginners skip at first: state management.
When you start building real AWS infrastructure, understanding the state file and remote backends becomes absolutely essential.
This blog breaks down the concepts in the cleanest and simplest way possible.
🔁 How Terraform Updates Infrastructure

Terraform always tries to make your actual AWS infrastructure match your configuration files.
Here’s the flow every time you run terraform apply:
1️⃣ Reads your Terraform configuration
2️⃣ Reads the state file
3️⃣ Compares the difference
4️⃣ Makes only the required updates
This is why Terraform state is so important — without it, Terraform wouldn’t know what exists.
📌 If it's the first time, how does Terraform read state when there is no state file?
👉 On the very first run (first terraform apply), there is no state file.
So what does Terraform do?
✅ 1. Reads your configuration files (.tf files)
It sees what resources you want to create.
❌ 2. State file doesn’t exist yet
Terraform checks for terraform.tfstate.
Since this is the first run, it finds nothing. So Terraform assumes:
“No resources exist yet in real AWS.”
🔄 3. Compares desired vs actual
Desired state = your configuration
Actual state = empty (because no state file)
Terraform concludes:
“All resources need to be created.”
🚀 4. Terraform creates the resources
It creates your VPC, S3 bucket, EC2 instance, etc.
📝 5. Terraform generates the state file
Once resources are created, Terraform stores their details in terraform.tfstate:
resource IDs
metadata
dependencies
attributes
configuration
Now Terraform knows what exists next time.
🔍 Example
First terraform apply:
No state file
Terraform thinks: “AWS has nothing.”
Creates everything
Writes new
terraform.tfstate
Second terraform apply:
Reads configuration
Reads the existing state file
Sees what already exists
Creates only new resources or updates changes
📄 Terraform State File
Terraform stores all information about your deployed resources inside:
terraform.tfstate
The state file is a JSON file that contains:
Resource metadata and current configuration
Attributes (CIDR blocks, bucket names, etc.)
Provider details
Resource dependencies
Sensitive data
🛡️ State File Best Practices
To avoid disasters:
✔ Never edit the state file manually
✔ Don’t store it locally for real projects
✔ Use remote state storage (S3)
✔ Enable state locking
✔ Back it up with versioning
✔ Use separate state per environment
✔ Restrict IAM access
✔ Ensure encryption is always ON
☁️ Why Use Remote Backend?
Storing state on S3 gives you:
🔹 Team collaboration
🔹 Secure storage
🔹 Automatic locking
🔹 Versioning & recovery
🔹 High durability
🟦AWS Remote Backend Components
S3 Bucket – stores the Terraform state
S3 Native Locking (Terraform 1.10+) – prevents concurrent changes
IAM policies – control access
No DynamoDB tables. No extra AWS services. Everything happens just with S3.
🔒 S3 Native State Locking
Starting with Terraform 1.10 (released in 2024), you no longer need DynamoDB for state locking. Terraform now supports S3 native state locking using Amazon S3's Conditional Writes feature.
How It Works
Terraform tries to create a
.tflockfile in S3If it exists → lock denied
If none exists → apply begins
After apply, Terraform deletes lock file (delete marker due to versioning)
Previous Method (DynamoDB):
Required separate DynamoDB table creation
Additional AWS service to monitor and maintain
More complex IAM permissions
Extra cost for DynamoDB read/write operations
DynamoDB state locking is now discouraged and may be deprecated in future Terraform versions
🏗️ Create an S3 Bucket with Versioning and Encryption Enabled
Before using S3 as a remote backend, you must create a secure bucket to store your Terraform state file.
This bucket should always have versioning and encryption enabled because the state file contains sensitive information.
Example Terraform code:
resource "aws_s3_bucket" "tf_state" {
bucket = "my-terraform-state-bucket-12345"
}
resource "aws_s3_bucket_versioning" "versioning" {
bucket = aws_s3_bucket.tf_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" {
bucket = aws_s3_bucket.tf_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
What this does & Why these settings matter:
Creates an S3 bucket
Enables versioning so every state update is backed up
Enables AES256 encryption for security
Initialize Your Backend Using this State File
Once the bucket is ready, you configure Terraform to use it as the backend.
Create a backend.tf file:
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket-12345"
key = "dev/terraform.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true
}
}
Key Parameters:
bucket: S3 bucket name for state storagekey: Path within the bucket where state file will be storedregion: AWS region for the S3 bucketuse_lockfile: Enable S3 native state locking (set totrue)encrypt: Enable server-side encryption for the state file
⚠️Important: S3 versioning MUST be enabled for S3 native state locking to work properly.
If you keep the bucket-creation file and the backend configuration file in the same folder, you should comment out the backend block before running Terraform. First, create the S3 bucket by applying only the bucket file. Once the bucket is successfully created, uncomment the backend configuration and run terraform init again to enable the remote backend and migrate the state.
Then initialize the backend:
terraform init
Terraform will:
Connect to your S3 bucket
Create the remote state file
Enable state locking (native S3 locking)
Ask to migrate your local state (choose yes if prompted)
Objective:
Create an S3 bucket for Terraform remote state, enable versioning + encryption, and then configure Terraform to use S3 as backend.
✅ 1. Create S3 Bucket using Terraform (Local Backend First)
📌 s3.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "tf_state" {
bucket = "rajeshreddy-tf-state-bucket"
}
resource "aws_s3_bucket_versioning" "versioning" {
bucket = aws_s3_bucket.tf_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "encryption" {
bucket = aws_s3_bucket.tf_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
✅ 2. Initialize Terraform
terraform init
Output:
Terraform has been successfully initialized!
✅ 3. Create the S3 Bucket
terraform apply
Enter: yes
Output:
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Your remote backend S3 bucket is now created successfully.

✅ 4. Configure Remote Backend in Terraform
terraform {
backend "s3" {
bucket = "rajeshreddy-tf-state-bucket"
key = "global/s3/terraform.tfstate"
region = "us-east-1"
}
}
✅ 5. Reinitialize Terraform to Migrate State
terraform init
Terraform will ask:
Do you want to copy existing state to the new backend?
Enter a value: yes
Enter yes.
Output:
Successfully configured the backend "s3"!
✅ 6. Validate with Terraform Plan
terraform plan
Output:
No changes. Your infrastructure matches the configuration.
This confirms:
Backend is working
State is stored in S3
Terraform is reading it correctly

How to Test State Locking
To verify that S3 native state locking is working:
Terminal 1: Run
terraform applyTerminal 2: While the first is running, try
terraform planorterraform applyExpected Result: The second command should fail with an error like:
Error: Error acquiring the state lock Error message: operation error S3: PutObject, https response error StatusCode: 412 Lock Info: ID: <lock-id> Path: <bucket>/<key> Operation: OperationTypeApply Who: <user>@<hostname>Check S3 Bucket: During the operation, you'll see a
.tflockfile temporarily in your S3 bucketAfter Completion: The lock file will be automatically deleted (delete marker with versioning)
🧰 Useful State Commands
terraform state list # list resources
terraform state show res # show resource details
terraform state rm res # remove from state
terraform state mv a b # move resource in state
terraform state pull # view raw state
Reference Video
This video demonstrates the exact workflow covered in the article. Thanks to piyush sachdeva




