# 📘 Terraform Series – Day 12

Secure State Management (S3 + DynamoDB Locking)

### 📝 Abstract

In Terraform, the **state file (**`terraform.tfstate`**)** is the most critical component that connects your configuration with real infrastructure. However, storing it locally can lead to **security risks, data loss, and team conflicts**.

This blog explains how to securely manage Terraform state using **AWS S3 (remote storage)** and **DynamoDB (state locking)**, which is the industry-standard approach for production environments.

### 🎯 Objectives

After completing this blog, you will be able to:

*   Understand Terraform state and its importance
    
*   Know why `.tfstate` should never be pushed to GitHub
    
*   Handle state loss scenarios
    
*   Understand state conflicts in team environments
    
*   Implement **remote backend using S3 + DynamoDB**
    
*   Test state locking in real scenarios
    

> ### 🔷 Step 1: What is Terraform State?

Terraform maintains a file:

```shell
terraform.tfstate
```

### 🧠 This file stores:

*   Real infrastructure details
    
*   Resource IDs and attributes
    
*   Mapping between **Terraform code ↔ AWS resources**
    

> ### 🔷 Step 2: Should You Push `.tfstate` to GitHub?

👉 ❌ **NO — Never do this**

### ⚠️ Why?

Because it contains:

*   Secrets (API keys, credentials)
    
*   Resource IDs
    
*   Internal infrastructure data
    

👉 This can lead to **security breaches**

### ✅ Add to `.gitignore`

```plaintext
*.tfstate
*.tfstate.backup
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/f9ca2da1-694a-452f-b75c-327fe4943969.png align="center")

> ### 🔷 Step 3: What if `.tfstate` is Deleted?

👉 Terraform loses tracking of infrastructure

### ❗ Result:

*   Terraform thinks → **nothing exists**
    
*   Next `terraform apply` → tries to recreate everything ❌
    

### ✅ Solutions:

*   Restore from backup (`.tfstate.backup`)
    
*   Use **remote backend (best practice)**
    

> ### 🔷 Step 4: State Conflict (Very Important)

### 🔹 Scenario:

*   Developer 1 → runs `terraform apply`
    
*   Developer 2 → runs `terraform apply`
    

### ❗ What Happens?

*   Both modify same state file
    
*   File gets overwritten or corrupted
    

👉 This is called **State Conflict**

> ### 🔷 Step 5: Solutions

❌ Local Shared State

*   Not safe
    
*   Not scalable
    

✅ Remote Backend (Best Practice)

Use:

*   **S3 Bucket** → Store state file
    
*   **DynamoDB** → Lock state
    

> ### 🔷 Step 6: Architecture Flow

### 🧠 Working:

1.  Terraform stores state in **S3**
    
2.  Before update → checks **DynamoDB**
    
3.  If no lock → creates `LockID`
    
4.  While locked → ❌ no parallel execution
    
5.  After completion → lock removed
    

> ### 🔷 Step 7: Practical Implementation

### 📁 Step 1: Create Project Folder

```shell
mkdir remote-infra
cd remote-infra
```

### 📄 Step 2: Create Files

```shell
touch provider.tf terraform.tf s3.tf dynamodb.tf
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/95ff2b54-9ad6-45ba-8c97-d8d6ee55591e.png align="center")

### 🔧 Step 3: Provider Configuration

```shell
provider "aws" {
  region = "us-east-2"
}
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/ec14a391-0f11-4aec-a469-7ff1c714146d.png align="center")

### 📦 Step 4: Terraform Block

```plaintext
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/0bf7d1ca-cff2-4e7d-aa40-626e7c823da9.png align="center")

### 🪣 Step 5: Create S3 Bucket

```shell
resource "random_id" "suffix" {
  byte_length = 2
}

resource "aws_s3_bucket" "remote_s3" {
  bucket = "dev-tf-state-${random_id.suffix.hex}"

  tags = {
    Name        = "tf-state-bucket"
    Environment = "dev"
  }
}
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/c32db342-0532-4b1a-9ebc-cac1a9754c06.png align="center")

### 🔐 Step 6: Create DynamoDB Table

```shell
resource "aws_dynamodb_table" "state_lock" {
  name         = "apurv-table"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }

  tags = {
    Name        = "apurv-table"
    Environment = "Dev"
  }
}
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/42fad633-215c-4699-8212-91d291107ab4.png align="center")

### 🔑 Step 7: IAM Permissions

Ensure your AWS user/role has:

*   S3 Full Access
    
*   DynamoDB Full Access
    

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/b7cafcc8-0932-435b-b8d2-e61e47043507.png align="center")

### 🔷 Step 8: Run Terraform

```plaintext
terraform init
terraform validate
terraform plan
terraform apply
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/6d55e7d3-85df-4359-b09d-71a9668ea65d.png align="center")

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/0d19a287-0584-4135-84ca-b61f7ba990be.png align="center")

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/43568c4f-3193-4db9-8507-77b16745cab5.png align="center")

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/e147f25c-965a-4484-ba5e-62fc163fd52d.png align="center")

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/d65b78b0-62b3-430a-bfb1-216f25a9d70e.png align="center")

> ### 🔷 Step 9: Configure Remote Backend

Now go to your **main project folder** and update:

```shell
terraform {
  backend "s3" {
    bucket         = "bucket<name>"
    key            = "terraform.tfstate"
    region         = "us-east-2"
    dynamodb_table = "apurv-table"
  }
}
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/10e60056-70d3-445e-965b-5b907994558a.png align="center")

### 🔄 Reinitialize

```shell
terraform init
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/0e6c643b-ea9f-41f6-ae05-577615cf41c1.png align="center")

> ### 🔷 Step 10: Remove Local State

```shell
rm terraform.tfstate*
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/111892e4-2da2-4229-9f2a-500e22a3b859.png align="center")

### ✅ Verify Remote State

```shell
terraform state list
```

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/8dbcfc21-d389-4fdf-93aa-a5befc0667b6.png align="center")

👉 Resources will still appear  
✔ Because state is now stored in **S3**

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/f468ffcb-cce3-413b-af5b-2db77e0d0519.png align="center")

> ### 🔷 Step 11: Test State Locking

### Terminal 1:

```plaintext
terraform apply
```

### Terminal 2:

```plaintext
terraform apply
```

### ❗ Result:

*   Terminal 2 → ❌ blocked / waits
    
*   Reason → Lock exists in DynamoDB
    

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/7b5f6de2-8a48-4cec-8aa4-bc055307f019.png align="center")

### ✔ After Completion:

*   Lock is removed
    
*   Second execution proceeds
    
*   After testing all the things you can destroy your resources
    

![](https://cdn.hashnode.com/uploads/covers/685cdc0d5ca95e55fac3ab09/e01d21d3-ae83-414a-ac13-684cacdef7d1.png align="center")

### 🚀 Conclusion

*   Terraform state is **critical for infrastructure tracking**
    
*   Never store state locally in production
    
*   Use **S3 for storage + DynamoDB for locking**
    
*   Prevents:
    
    *   Data loss
        
    *   State conflicts
        
    *   Security risks
        

### **👨‍💻 About the Author**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1751797710818/123a7231-3dca-4273-ad68-7bd026f69b95.png?auto=compress,format&format=webp&auto=compress,format&format=webp&auto=compress,format&format=webp&auto=compress,format&format=webp&auto=compress,format&format=webp align="center")

“A complete Terraform series covering everything from fundamentals to advanced real-world infrastructure automation in a DevOps environment.”

### **📬 Let's Stay Connected**

*   📧 **Email**: [**gujjarapurv181@gmail.com**](mailto:gujjarapurv181@gmail.com)
    
*   🐙 **GitHub**: [**github.com/ApurvGujjar07**](http://github.com/ApurvGujjar07)
    
*   💼 **LinkedIn**: [**linkedin.com/in/apurv-gujjar**](http://linkedin.com/in/apurv-gujjar)
