Terraform

DevOpsInfrastructure as CodeIaCHCLMulti-cloudDeclarativeTool

DevOps Tool

Terraform

Overview

Terraform is an open-source Infrastructure as Code (IaC) tool developed by HashiCorp. It uses HCL (HashiCorp Configuration Language), a declarative configuration language, to manage infrastructure resources across multiple cloud providers and services like AWS, Azure, Google Cloud, and Kubernetes in a unified manner.

Details

Terraform was developed by HashiCorp in 2014 and is currently the most widely used tool in the IaC field (32.8% market share). It uses a proprietary declarative language called HCL (HashiCorp Configuration Language) to define infrastructure as code. With over 1,200 providers available, it can manage everything from AWS, Azure, GCP, and Kubernetes to Datadog and GitHub in a unified manner.

The main characteristic of Terraform is its "declarative" approach. You describe "what you want to create," and Terraform automatically plans and executes the necessary changes by comparing with the current state. The state management feature maintains synchronization between actual infrastructure and code, supporting team collaboration.

The planning feature allows you to preview "what will be changed" before actually applying changes, enabling safe infrastructure operations. Additionally, the rich module ecosystem makes it easy to use reusable infrastructure patterns.

Pros and Cons

Pros

  • Multi-cloud support: Unified management of multiple cloud providers with one tool
  • Rich providers: Support for over 1,200 providers covering a wide range of services
  • Planning feature: Safe operations by previewing changes before application
  • Large community: Abundant modules and documentation
  • State management: Tracks current infrastructure state and manages differences between configuration and reality

Cons

  • Learning curve: HCL learning required, high barrier for beginners
  • State file management: Complex state file management, challenging for team development
  • Provider dependency: Latest features may be unavailable due to provider update delays
  • Rollback limitations: Configuration rollback is not automated

References

Official Resources

Learning Resources

Usage Examples

Basic Resource Definition

# provider.tf - Provider configuration
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  required_version = ">= 1.0"
}

provider "aws" {
  region = var.aws_region
}

# variables.tf - Variable definitions
variable "aws_region" {
  description = "AWS region for resources"
  type        = string
  default     = "us-east-1"
}

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

VPC and Subnet Creation

# vpc.tf - VPC and network configuration
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}"
    Type = "Public"
  }
}

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

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

# Data source - Get AZ list
data "aws_availability_zones" "available" {
  state = "available"
}

EC2 Instance Creation

# ec2.tf - EC2 instance definition
resource "aws_security_group" "web" {
  name_prefix = "${var.environment}-web-"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "${var.environment}-web-sg"
  }
}

resource "aws_instance" "web" {
  count = 2

  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
  subnet_id     = aws_subnet.public[count.index].id
  
  vpc_security_group_ids = [aws_security_group.web.id]
  
  user_data = base64encode(<<-EOF
    #!/bin/bash
    apt update
    apt install -y nginx
    systemctl start nginx
    systemctl enable nginx
    echo "<h1>Web Server ${count.index + 1}</h1>" > /var/www/html/index.html
  EOF
  )

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

# Get latest Ubuntu AMI
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-22.04-amd64-server-*"]
  }
}

Load Balancer Configuration

# alb.tf - Application Load Balancer
resource "aws_lb" "main" {
  name               = "${var.environment}-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = aws_subnet.public[*].id

  tags = {
    Environment = var.environment
  }
}

resource "aws_security_group" "alb" {
  name_prefix = "${var.environment}-alb-"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_lb_target_group" "web" {
  name     = "${var.environment}-web-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id

  health_check {
    enabled             = true
    healthy_threshold   = 2
    unhealthy_threshold = 2
    timeout             = 5
    interval            = 30
    path                = "/"
    matcher             = "200"
  }
}

resource "aws_lb_target_group_attachment" "web" {
  count = length(aws_instance.web)

  target_group_arn = aws_lb_target_group.web.arn
  target_id        = aws_instance.web[count.index].id
  port             = 80
}

resource "aws_lb_listener" "web" {
  load_balancer_arn = aws_lb.main.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.web.arn
  }
}

State Management (Remote Backend)

# backend.tf - S3 backend configuration
terraform {
  backend "s3" {
    bucket         = "my-terraform-state-bucket"
    key            = "infrastructure/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

# S3 bucket and DynamoDB table creation
resource "aws_s3_bucket" "terraform_state" {
  bucket = "my-terraform-state-bucket"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

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

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

Module Creation and Usage

# modules/vpc/main.tf - VPC module
variable "environment" {
  description = "Environment name"
  type        = string
}

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

resource "aws_vpc" "this" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

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

output "vpc_id" {
  description = "ID of the VPC"
  value       = aws_vpc.this.id
}

output "vpc_cidr_block" {
  description = "CIDR block of the VPC"
  value       = aws_vpc.this.cidr_block
}

# main.tf - Module usage
module "vpc" {
  source = "./modules/vpc"

  environment = var.environment
  vpc_cidr    = "10.0.0.0/16"
}

module "production_vpc" {
  source = "./modules/vpc"

  environment = "production"
  vpc_cidr    = "10.1.0.0/16"
}

Basic Terraform Commands

# Initialize (download providers)
terraform init

# Format (code formatting)
terraform fmt

# Validate (syntax check)
terraform validate

# Plan (preview changes)
terraform plan

# Apply (create/update infrastructure)
terraform apply

# Apply specific resources only
terraform apply -target=aws_instance.web

# Destroy (delete resources)
terraform destroy

# Show state
terraform show

# List resources
terraform state list

# Show specific resource details
terraform state show aws_instance.web

# Import (add existing resource to Terraform management)
terraform import aws_instance.web i-1234567890abcdef0

Conditional Logic and Loops

# Conditional example
resource "aws_instance" "conditional" {
  count = var.create_instance ? 1 : 0

  ami           = data.aws_ami.ubuntu.id
  instance_type = var.environment == "production" ? "t3.medium" : "t3.micro"
}

# Resource creation using for_each
variable "users" {
  type = map(object({
    groups = list(string)
  }))
  default = {
    "alice" = {
      groups = ["developers", "admins"]
    }
    "bob" = {
      groups = ["developers"]
    }
  }
}

resource "aws_iam_user" "users" {
  for_each = var.users
  name     = each.key
}

resource "aws_iam_user_group_membership" "users" {
  for_each = var.users

  user   = aws_iam_user.users[each.key].name
  groups = each.value.groups
}

Basic Workflow

1. Initial Setup

# Create new project
mkdir terraform-project && cd terraform-project

# Create configuration file
cat > main.tf << EOF
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

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

# Initialize
terraform init

2. Development Cycle

# 1. Create/edit code
vim resources.tf

# 2. Format
terraform fmt

# 3. Validate
terraform validate

# 4. Review plan
terraform plan

# 5. Apply
terraform apply

# 6. Check state
terraform show

3. Team Development

# Initialize remote backend
terraform init -backend-config=backend.conf

# Workspace management
terraform workspace new production
terraform workspace select development
terraform workspace list

# Check state lock
terraform force-unlock LOCK_ID