# Day06: Terraform Project Structure

## Complete Hands-On Guide

**Transform your single** [`main.tf`](http://main.tf) into a production-ready, multi-file Terraform project. This follows HashiCorp best practices for organizing AWS infrastructure code across multiple files.

## Why Multiple Files Matter

**Single file → Multiple files evolution** - Started with everything in [`main.tf`](http://main.tf) (Day 4) to learn basics, now improving for real-world use.

**Key Benefits:**

* **Single Responsibility** - Each file has one clear purpose
    
* **Team Collaboration** - Multiple developers work simultaneously
    
* **Better Git History** - Focused, reviewable changes
    
* **Scalable Foundation** - Ready for modules (coming later)
    

---

## Complete Project Structure

```plaintext
day-06-terraform-project/
├── main.tf                 # All resource definitions (S3, VPC, EC2)
├── variables.tf            # Input variable declarations
├── locals.tf               # Local computed values
├── outputs.tf              # Output values
├── providers.tf            # AWS provider configuration
├── versions.tf             # Terraform & provider versions
├── backend.tf              # S3 remote state backend
├── terraform.tfvars        # Environment variables (NEVER commit!)
├── terraform.tfvars.example # GitHub-safe variable template
├──.gitignore              # Exclude sensitive files
└── README.md               # Project documentation
```

**Root directory = Root Module** - Terraform auto-detects all `.tf` files.

---

## File-by-File Implementation Guide

### 1\. [**main.tf**](http://main.tf) - Infrastructure Resources

**Purpose:** Define all AWS resources (S3, VPC, EC2, etc.)

```plaintext

// create S3 bucket
resource "aws_s3_bucket" "first_bucket" {
  bucket = "anjali-gupta-bucket-76"
#   region = "eu-north-1"
region = var.region

  tags = {
    Name        = "My bucket"
    Environment = var.environment
  }
}

// create one sample resource
resource "aws_vpc" "sample" {
    cidr_block = "10.0.1.0/24"
    # region = "eu-north-1"
    region = var.region
    tags = {
      Environment=var.environment
    #   Name= "${var.environment}-VPC"
        Name= local.vpc_name
    } 
}


  
// create one ec2 instance
resource "aws_instance" "example" {
  ami           = "resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"  
  instance_type = "t2.micro"
#   region = "eu-north-1"
    region = var.region

  tags = {
    Environment=var.environment
    
    # Name = "${var.environment}-Instance"
    Name = local.instance_name
  }
}
```

**When to split further:** [`s3.tf`](http://s3.tf), [`vpc.tf`](http://vpc.tf), [`ec2.tf`](http://ec2.tf) for 100+ resources.

### 2\. [**variables.tf**](http://variables.tf) - Input Configuration

**Purpose:** Declare all configurable parameters with types and descriptions.

```plaintext

// variable named it environment
variable "environment" {
  type        = string
  default     = "dev"
}


variable "region" {
  type        = string
  default     = "eu-north-1"
}
```

### 3\. [**locals.tf**](http://locals.tf) - Reusable Computed Values

**Purpose:** Complex expressions used across resources.

```plaintext

// locals
# locals {
#     // can use like this also 
#  env = var.environment
#  vpc_name = "${local.env}-VPC"
#  instance_name = "${local.env}-Instance"
# }


locals {
  vpc_name = "${var.environment}-VPC"
  instance_name = "${var.environment}-Instance"
}
```

### 4\. [**outputs.tf**](http://outputs.tf) - Resource Information

**Purpose:** Expose key values for other modules/CLI.

```plaintext

output "instance_id" {
    value = aws_instance.example.id
}

output "vpc_id" {
    value = aws_vpc.sample.id
}
```

### 5\. [**providers.tf**](http://providers.tf) - AWS Provider Setup

**Purpose:** Provider configuration and aliases.

```plaintext
 terraform {
    required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
 }
 
# Configure the AWS Provider
provider "aws" {
  region = "eu-north-1"
}
```

### 6\. [**versions.tf**](http://versions.tf) - Version Constraints

**Purpose:** Ensure reproducible builds.

```plaintext
terraform {
  required_version = ">= 1.5.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
```

### 7\. [**backend.tf**](http://backend.tf) - Remote State Management

**Purpose:** S3 backend with DynamoDB locking.

```plaintext
terraform {

    backend "s3" {
        bucket = "anjali-gupta-bucket-76"
        key    = "terraform.tfstate"
        region = "eu-north-1"
        use_lockfile = true
    }
}
```

---

## Git Security Checklist (.gitignore)

**Critical:** Never commit sensitive data to GitHub.

```plaintext
.terraform*
*.tfstate
*.tfstate.backup    
.terraform.lock.hcl
crash.log
*.log
terraform.tfvars
*.tfvars.json
.terraform/
```

### terraform.tfvars.example (GitHub Safe)

```plaintext
aws_region     = "us-east-1"
environment    = "dev"
bucket_name    = "my-app-bucket-123"
vpc_cidr       = "10.0.0.0/16"
ami_id         = "ami-0abcdef1234567890"
instance_type  = "t3.micro"
```

**Workflow:**

1. `cp terraform.tfvars.example terraform.tfvars`
    
2. Edit sensitive values in `terraform.tfvars`
    
3. **Commit only** `.example` file
    

**Demo sequence:**

1. **Cut backend block** → [`backend.tf`](http://backend.tf)
    
2. **Cut provider block** → [`providers.tf`](http://providers.tf)
    
3. **Cut variable declarations** → [`variables.tf`](http://variables.tf)
    
4. **Cut locals block** → [`locals.tf`](http://locals.tf)
    
5. **Cut outputs** → [`outputs.tf`](http://outputs.tf)
    
6. **Create** `.gitignore` + `terraform.tfvars.example`
    

**Result:** Clean, green VSCode - Terraform auto-detects all files!

## Advanced Multi-Environment Structure

```plaintext
terraform-project/
│
├── README.md                  # Project documentation
├── .gitignore                 # Git ignore file for Terraform
├── .terraform-version         # Terraform version specification
│
└── environments/              # Environment specific configurations
    │
    ├── dev/
    │   ├── main.tf            # Main configuration for dev
    │   ├── variables.tf       # Variable declarations
    │   ├── terraform.tfvars   # Variable values
    │   ├── outputs.tf         # Output values
    │   └── backend.tf         # Backend configuration
    │
    └── staging/
        ├── main.tf
        ├── variables.tf
        ├── terraform.tfvars
        ├── outputs.tf
        └── backend.tf
```

### **Module Based Approach:**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1764429849842/a27854b1-7bd5-4ff3-9186-98e6ab02b024.png align="center")

**Two strategies:**

1. **Separate** [**main.tf**](http://main.tf) **per environment** - Different resources
    
2. **Shared** [**main.tf**](http://main.tf) **\+ environment tfvars** - Same infra, different values
    

---

## Quick Start Commands

```bash
# Initialize (downloads providers)
terraform init

# Plan (shows what will change)
terraform plan

# Apply (creates infrastructure)
terraform apply

# Destroy (removes everything)
terraform destroy
```

---

## Key Takeaways

* **Root Module** = Root directory with `.tf` files
    
* **Flexible naming** - [`outputs.tf`](http://outputs.tf) or [`output.tf`](http://output.tf) both work
    
* **Modules later** - For complex projects with 100+ resources
    
* **S3 Backend** = Team collaboration essential
    
* **Gitignore** = Security first, never commit state files
    
* **tfvars.example** = Shareable template for GitHub
    

### Video:

* %[https://youtu.be/QMsJholPkDY?si=jBwQ8ZL70g_PwfBJ] 
    

**#30daysTerraformwithAWS**
