Infrastructure as Code with Terraform: A Production-Ready Guide

C
Chief Technology OfficerCodeNex Engineering
November 19, 2025
12 min read
#Terraform#IaC#AWS#DevOps#Automation

Infrastructure as Code with Terraform: A Production-Ready Guide

Infrastructure as Code (IaC) transforms how we provision and manage cloud resources. This guide covers everything you need to know to implement Terraform in production.

Why Infrastructure as Code?

Traditional infrastructure management is:

  • Manual: Point-and-click in AWS Console
  • Error-prone: No version control or review process
  • Undocumented: No single source of truth
  • Difficult to replicate: Hard to create staging environments

IaC solves all of these problems.

Terraform Basics

Core Concepts

Resources: The infrastructure you want to create

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = "WebServer"
    Environment = "production"
  }
}

Providers: Cloud platform integration

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

Variables: Reusable configuration values

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "dev"
}

variable "instance_count" {
  description = "Number of instances"
  type        = number
  default     = 2
}

Production Best Practices

1. State Management

Never commit terraform.tfstate to git!

Use remote state with S3 + DynamoDB locking:

terraform {
  backend "s3" {
    bucket         = "mycompany-terraform-state"
    key            = "prod/infrastructure.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

2. Module Structure

Organize your code with modules:

terraform/
├── modules/
│   ├── vpc/
│   ├── ec2/
│   └── rds/
├── environments/
│   ├── dev/
│   ├── staging/
│   └── prod/
└── global/
    └── s3/

3. Variable Management

Use terraform.tfvars for each environment:

dev.tfvars:

environment      = "dev"
instance_type    = "t3.micro"
instance_count   = 1
enable_backups   = false

prod.tfvars:

environment      = "prod"
instance_type    = "t3.large"
instance_count   = 3
enable_backups   = true

4. Security Best Practices

  • Store secrets in AWS Secrets Manager or SSM Parameter Store
  • Use data sources to reference existing resources
  • Enable resource tagging for cost tracking
  • Implement least-privilege IAM policies
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "prod/database/password"
}

resource "aws_db_instance" "main" {
  password = data.aws_secretsmanager_secret_version.db_password.secret_string
  # ... other configuration
}

5. CI/CD Integration

Example GitHub Actions workflow:

name: Terraform Apply

on:
  push:
    branches: [main]

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2

      - name: Terraform Init
        run: terraform init

      - name: Terraform Plan
        run: terraform plan -var-file="prod.tfvars"

      - name: Terraform Apply
        if: github.ref == 'refs/heads/main'
        run: terraform apply -auto-approve -var-file="prod.tfvars"

Real-World Example: Complete VPC Setup

Here's a production-ready VPC module:

# modules/vpc/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name        = "${var.environment}-vpc"
    Environment = var.environment
  }
}

resource "aws_subnet" "public" {
  count             = length(var.public_subnet_cidrs)
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.public_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  map_public_ip_on_launch = true

  tags = {
    Name = "${var.environment}-public-${count.index + 1}"
    Type = "public"
  }
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "${var.environment}-igw"
  }
}

Common Pitfalls to Avoid

1. Hardcoded Values

Bad:

resource "aws_instance" "web" {
  ami = "ami-0c55b159cbfafe1f0"  # Don't hardcode AMI IDs
}

Good:

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

resource "aws_instance" "web" {
  ami = data.aws_ami.amazon_linux.id
}

2. Not Using Count or For_Each

Create multiple similar resources efficiently:

resource "aws_instance" "web" {
  for_each = toset(var.availability_zones)

  ami               = data.aws_ami.amazon_linux.id
  instance_type     = var.instance_type
  availability_zone = each.value

  tags = {
    Name = "web-${each.value}"
  }
}

3. Ignoring Terraform Plan

Always review terraform plan output before applying. Unexpected changes can cause downtime.

Cost Optimization with Terraform

Use lifecycle rules to prevent accidental resource deletion:

resource "aws_db_instance" "main" {
  # ... configuration

  lifecycle {
    prevent_destroy = true
    ignore_changes  = [password]
  }
}

Migration from Manual Infrastructure

Step-by-Step Process:

  1. Audit existing infrastructure: Document everything you have
  2. Import existing resources: Use terraform import
  3. Write Terraform code: Match current configuration
  4. Validate with plan: Ensure no changes detected
  5. Gradually add new features: Start managing with Terraform

Example import command:

terraform import aws_instance.web i-1234567890abcdef0

Testing Terraform Code

Use terraform validate and terraform fmt:

# Format code
terraform fmt -recursive

# Validate configuration
terraform validate

# Check with tflint
tflint --recursive

Conclusion

Infrastructure as Code with Terraform provides:

  • ✅ Version control for infrastructure
  • ✅ Reproducible environments
  • ✅ Team collaboration
  • ✅ Documentation through code
  • ✅ Audit trail of changes

Start small, iterate, and gradually expand your Terraform usage.

Need help implementing IaC? Download our Terraform starter templates or schedule a consultation.