DEV Community

Sourav kumar
Sourav kumar

Posted on

Day- 08 /100 - Deploying Multi-Environment Infrastructure with Terraform

Introduction

In real-world DevOps practices, managing multiple environments (development, staging, and production) efficiently is crucial. Terraform, an Infrastructure as Code (IaC) tool, allows us to automate cloud infrastructure provisioning. In this blog, we will build a multi-environment setup (Dev, Stage, and Prod) using Terraform modules, while also implementing best practices like state management with S3 and DynamoDB.

Project Overview

We will create three environments (Dev, Stage, and Prod) with different configurations for:

  1. EC2 Instances (varying counts per environment)
  2. S3 Buckets (varying counts per environment)
  3. VPC with Public & Private Subnets
  4. Security Groups
  5. RDS Database
  6. DynamoDB for state locking
  7. S3 for Terraform state storage

πŸ“ Folder Structure

terraform-project/
│── modules/
β”‚   β”œβ”€β”€ ec2/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ outputs.tf
β”‚   β”œβ”€β”€ s3/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ outputs.tf
β”‚   β”œβ”€β”€ vpc/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ outputs.tf
β”‚   β”œβ”€β”€ security-groups/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ outputs.tf
β”‚   β”œβ”€β”€ rds/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ outputs.tf
β”‚   β”œβ”€β”€ dynamodb/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ outputs.tf
│── environments/
β”‚   β”œβ”€β”€ dev/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ backend.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ terraform.tfvars
β”‚   β”œβ”€β”€ stage/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ backend.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ terraform.tfvars
β”‚   β”œβ”€β”€ prod/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ backend.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ terraform.tfvars
│── global/
β”‚   β”œβ”€β”€ s3-backend/
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ outputs.tf
│── provider.tf
│── variables.tf
│── terraform.tfvars
│── outputs.tf
│── README.md
Enter fullscreen mode Exit fullscreen mode

1️⃣ Configure Terraform Backend (State Management)

File: global/s3-backend/main.tf

resource "aws_s3_bucket" "terraform_state" {
  bucket = "mycompany-terraform-state"
  acl    = "private"
  versioning {
    enabled = true
  }
  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_dynamodb_table" "terraform_lock" {
  name         = "terraform-lock"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}
Enter fullscreen mode Exit fullscreen mode

2️⃣ Backend Configuration for Each Environment

File: environments/dev/backend.tf

terraform {
  backend "s3" {
    bucket         = "mycompany-terraform-state"
    key            = "dev/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-lock"
    encrypt        = true
  }
}
Enter fullscreen mode Exit fullscreen mode

3️⃣ VPC Module

File: modules/vpc/main.tf

resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr
}

resource "aws_subnet" "public" {
  count = length(var.public_subnets)

  vpc_id                  = aws_vpc.main.id
  cidr_block              = var.public_subnets[count.index]
  map_public_ip_on_launch = true
}

resource "aws_subnet" "private" {
  count = length(var.private_subnets)

  vpc_id     = aws_vpc.main.id
  cidr_block = var.private_subnets[count.index]
}
Enter fullscreen mode Exit fullscreen mode

4️⃣ EC2 Module

File: modules/ec2/main.tf

resource "aws_instance" "app" {
  count = var.instance_count

  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = var.subnet_id

  tags = {
    Name = "EC2-${var.environment}"
  }
}
Enter fullscreen mode Exit fullscreen mode

5️⃣ RDS Module

File: modules/rds/main.tf

resource "aws_db_instance" "database" {
  allocated_storage    = var.db_storage
  engine              = "mysql"
  instance_class      = var.db_instance_class
  db_name             = var.db_name
  username           = var.db_username
  password           = var.db_password
  publicly_accessible = false
  skip_final_snapshot = true
}
Enter fullscreen mode Exit fullscreen mode

6️⃣ Dev Environment Implementation

Each environment (dev, stage, prod) will have its own terraform.tfvars file inside its respective folder.
File: environments/dev/main.tf

environments/dev/terraform.tfvars

# VPC
vpc_cidr        = "10.0.0.0/16"
public_subnets  = ["10.0.1.0/24"]
private_subnets = ["10.0.2.0/24"]

# EC2
instance_count  = 2
ami_id          = "ami-12345678"
instance_type   = "t2.micro"

# RDS
db_storage         = 20
db_instance_class  = "db.t3.micro"
db_name            = "devdb"
db_username        = "admin"
db_password        = "password123"
Enter fullscreen mode Exit fullscreen mode

environments/stage/terraform.tfvars

# VPC
vpc_cidr        = "10.1.0.0/16"
public_subnets  = ["10.1.1.0/24"]
private_subnets = ["10.1.2.0/24"]

# EC2
instance_count  = 3
ami_id          = "ami-87654321"
instance_type   = "t3.medium"

# RDS
db_storage         = 50
db_instance_class  = "db.t3.medium"
db_name            = "stagedb"
db_username        = "admin"
db_password        = "securepassword456"
Enter fullscreen mode Exit fullscreen mode

environments/prod/terraform.tfvars

# VPC
vpc_cidr        = "10.2.0.0/16"
public_subnets  = ["10.2.1.0/24"]
private_subnets = ["10.2.2.0/24"]

# EC2
instance_count  = 5
ami_id          = "ami-11223344"
instance_type   = "t3.large"

# RDS
db_storage         = 100
db_instance_class  = "db.t3.large"
db_name            = "proddb"
db_username        = "admin"
db_password        = "supersecurepassword789"
Enter fullscreen mode Exit fullscreen mode

Now, environments/dev/main.tf (or stage/main.tf and prod/main.tf) will only reference variables, making it more reusable.

module "vpc" {
  source          = "../../modules/vpc"
  vpc_cidr        = var.vpc_cidr
  public_subnets  = var.public_subnets
  private_subnets = var.private_subnets
}

module "ec2" {
  source         = "../../modules/ec2"
  instance_count = var.instance_count
  ami_id         = var.ami_id
  instance_type  = var.instance_type
  subnet_id      = module.vpc.public_subnets[0]
  environment    = "dev"
}

module "rds" {
  source           = "../../modules/rds"
  db_storage       = var.db_storage
  db_instance_class = var.db_instance_class
  db_name         = var.db_name
  db_username     = var.db_username
  db_password     = var.db_password
}
Enter fullscreen mode Exit fullscreen mode

8️⃣ Apply Terraform
Step 1: Initialize Backend

terraform init
Enter fullscreen mode Exit fullscreen mode

Step 2: Plan

terraform plan -var-file="terraform.tfvars"
Enter fullscreen mode Exit fullscreen mode

Step 3: Apply

terraform apply -var-file="terraform.tfvars" -auto-approve
Enter fullscreen mode Exit fullscreen mode

Steps to Run the Terraform Project in All 3 Environments (Dev, Stage, Prod)

Here we will understand how to set up, initialize, and deploy the Terraform project for each environment: Dev, Stage, and Prod.

1️⃣ Prerequisites
Before running Terraform, ensure you have the following installed:

  1. Terraform CLI (β‰₯ v1.0) β†’ Install from Terraform Download
  2. AWS CLI (β‰₯ v2.0) β†’ Install from AWS CLI
  3. AWS Credentials Configured (~/.aws/credentials)
aws configure
Enter fullscreen mode Exit fullscreen mode

S3 Bucket for Remote Backend (Created in global/s3-backend/)

2️⃣ Initialize Backend (S3 & DynamoDB)
Since we are using remote backend for Terraform state, we first create the S3 bucket and DynamoDB table.

🌍 Navigate to global/s3-backend/ and Apply

cd global/s3-backend/
terraform init
terraform apply -auto-approve
Enter fullscreen mode Exit fullscreen mode

This creates an S3 bucket for storing Terraform state and a DynamoDB table for state locking.

3️⃣ Deploy a Specific Environment (Dev, Stage, Prod)
Each environment (dev, stage, prod) has its own directory. You must navigate to the specific environment before running Terraform commands.

▢️ Deploy Dev Environment

cd environments/dev/
terraform init
terraform plan -var-file="terraform.tfvars"
terraform apply -var-file="terraform.tfvars" -auto-approve
Enter fullscreen mode Exit fullscreen mode

βœ… This provisions the Dev environment.

▢️ Deploy Stage Environment

cd environments/stage/
terraform init
terraform plan -var-file="terraform.tfvars"
terraform apply -var-file="terraform.tfvars" -auto-approve
Enter fullscreen mode Exit fullscreen mode

βœ… This provisions the Stage environment.

▢️ Deploy Prod Environment

cd environments/prod/
terraform init
terraform plan -var-file="terraform.tfvars"
terraform apply -var-file="terraform.tfvars" -auto-approve
Enter fullscreen mode Exit fullscreen mode

βœ… This provisions the Prod environment.

4️⃣ Verify Deployments
After running Terraform, verify that resources have been created or not either through console or through aws cli commands.

5️⃣ Destroy Resources (When Needed)
To destroy an environment, navigate to its directory and run:

terraform destroy -var-file="terraform.tfvars" -auto-approve
Enter fullscreen mode Exit fullscreen mode

Example:
cd environments/dev/
terraform destroy -var-file="terraform.tfvars" -auto-approve
πŸ”΄ ⚠️ WARNING: This permanently deletes all resources in the environment!

Summary

Summary

Top comments (0)