Easy Deploy Blog
Infrastructure as Code with Terraform

Infrastructure as Code with Terraform

September 24, 2024
10 min read
Farhaan Patel

Infrastructure as Code with Terraform and Easy Deploy

Infrastructure as Code (IaC) revolutionizes how we manage and provision cloud resources. This guide explores how to leverage Terraform alongside Easy Deploy to create scalable, maintainable infrastructure deployments.

Why Infrastructure as Code?

Benefits of IaC

Getting Started with Terraform

Basic Configuration

Start with a simple Terraform configuration:

# main.tf
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

# variables.tf
variable "aws_region" {
  description = "AWS region for resources"
  type        = string
  default     = "us-west-2"
}

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

Resource Definition

Define your infrastructure resources:

# vpc.tf
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

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

resource "aws_subnet" "public" {
  count             = 2
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index + 1}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]

  map_public_ip_on_launch = true

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

Terraform Modules

Creating Reusable Modules

Structure your modules for reusability:

modules/
├── vpc/
│   ├── main.tf
│   ├── variables.tf
│   └── outputs.tf
├── eks/
│   ├── main.tf
│   ├── variables.tf
│   └── outputs.tf
└── rds/
    ├── main.tf
    ├── variables.tf
    └── outputs.tf

Module Example

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

  tags = merge(
    var.tags,
    {
      Name = var.name
    }
  )
}

# modules/vpc/variables.tf
variable "name" {
  description = "Name of the VPC"
  type        = string
}

variable "cidr_block" {
  description = "CIDR block for VPC"
  type        = string
  default     = "10.0.0.0/16"
}

# modules/vpc/outputs.tf
output "vpc_id" {
  description = "ID of the VPC"
  value       = aws_vpc.this.id
}

State Management

Remote State

Use remote state for team collaboration:

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "us-west-2"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

State Locking

Implement state locking to prevent conflicts:

resource "aws_dynamodb_table" "terraform_locks" {
  name           = "terraform-locks"
  billing_mode   = "PAY_PER_REQUEST"
  hash_key       = "LockID"

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

Integration with Easy Deploy

Automated Provisioning

Integrate Terraform with Easy Deploy workflows:

# .github/workflows/infrastructure.yml
name: Infrastructure Deployment

on:
  push:
    branches: [main]
    paths: ['terraform/**']

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

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

      - name: Terraform Init
        run: terraform init
        working-directory: terraform

      - name: Terraform Plan
        run: terraform plan -out=tfplan
        working-directory: terraform

      - name: Terraform Apply
        run: terraform apply tfplan
        working-directory: terraform

Environment Management

Create environment-specific configurations:

# environments/production/main.tf
module "vpc" {
  source = "../../modules/vpc"

  name       = "production-vpc"
  cidr_block = "10.0.0.0/16"

  tags = {
    Environment = "production"
    Project     = "easy-deploy"
  }
}

module "eks" {
  source = "../../modules/eks"

  cluster_name = "production-cluster"
  vpc_id       = module.vpc.vpc_id
  subnet_ids   = module.vpc.private_subnet_ids

  node_groups = {
    main = {
      desired_capacity = 3
      max_capacity     = 10
      min_capacity     = 1
      instance_types   = ["t3.medium"]
    }
  }
}

Best Practices

Code Organization

terraform/
├── environments/
│   ├── development/
│   ├── staging/
│   └── production/
├── modules/
│   ├── vpc/
│   ├── eks/
│   └── rds/
├── global/
│   └── route53/
└── shared/
    └── iam/

Security Considerations

Testing Infrastructure

# test/vpc_test.go
package test

import (
    "testing"
    "github.com/gruntwork-io/terratest/modules/terraform"
)

func TestVPCModule(t *testing.T) {
    terraformOptions := &terraform.Options{
        TerraformDir: "../modules/vpc",
        Vars: map[string]interface{}{
            "name":       "test-vpc",
            "cidr_block": "10.0.0.0/16",
        },
    }

    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)

    vpcId := terraform.Output(t, terraformOptions, "vpc_id")
    assert.NotEmpty(t, vpcId)
}

Monitoring and Maintenance

Drift Detection

Implement regular drift detection:

#!/bin/bash
# scripts/check-drift.sh

terraform plan -detailed-exitcode
exit_code=$?

if [ $exit_code -eq 2 ]; then
    echo "Infrastructure drift detected!"
    terraform plan
    exit 1
elif [ $exit_code -eq 1 ]; then
    echo "Terraform plan failed!"
    exit 1
else
    echo "No infrastructure drift detected."
fi

Conclusion

Infrastructure as Code with Terraform provides a robust foundation for managing cloud resources at scale. When integrated with Easy Deploy, you can achieve fully automated, reliable infrastructure deployments that support modern DevOps practices.

Start small with basic configurations and gradually adopt more advanced patterns like modules, remote state, and automated testing to build a comprehensive IaC strategy.