Terraform
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
- Terraform Official Site - HashiCorp official site
- Terraform Documentation - Official documentation
- Terraform Registry - Provider and module search
- Terraform GitHub - Source code
Learning Resources
- Learn Terraform - Official tutorials
- Terraform Best Practices - Google Cloud best practices
- Terraform AWS Examples - AWS module collection
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