Infrastructure as Code with Terraform
Duration: 140 min
Terraform is an Infrastructure as Code (IaC) tool that enables defining, versioning, and managing infrastructure declaratively. This module covers Terraform fundamentals, AWS providers, modules, state management, and best practices.
Terraform Fundamentals
Terraform uses HCL (HashiCorp Configuration Language) to define infrastructure:
# Configure the AWS provider
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
# Define a resource
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-unique-bucket-name"
tags = {
Name = "My Bucket"
Environment = "production"
}
}
# Output values
output "bucket_name" {
value = aws_s3_bucket.my_bucket.id
description = "The name of the S3 bucket"
}Terraform Workflow
# Initialize Terraform (download providers, setup backend)
terraform init
# Validate configuration syntax
terraform validate
# Format code to standard style
terraform fmt -recursive
# Plan changes (preview what will be created/modified)
terraform plan -out=tfplan
# Apply changes (create/modify resources)
terraform apply tfplan
# Destroy resources
terraform destroy
# Show current state
terraform show
# List resources in state
terraform state list
# Show specific resource details
terraform state show aws_s3_bucket.my_bucketVariables and Outputs
# variables.tf
variable "environment" {
type = string
description = "Environment name"
default = "development"
}
variable "instance_count" {
type = number
description = "Number of instances"
default = 1
}
variable "tags" {
type = map(string)
description = "Common tags for all resources"
default = {
Project = "MyApp"
Team = "DevOps"
}
}
# main.tf
resource "aws_instance" "app" {
count = var.instance_count
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = merge(
var.tags,
{
Name = "app-${count.index + 1}"
}
)
}
# outputs.tf
output "instance_ids" {
value = aws_instance.app[*].id
description = "IDs of created instances"
}
output "instance_ips" {
value = aws_instance.app[*].private_ip
description = "Private IPs of instances"
}Terraform Modules
Modules organize and reuse infrastructure code:
# modules/vpc/main.tf
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
enable_dns_hostnames = true
tags = {
Name = var.name
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidr
availability_zone = var.availability_zone
tags = {
Name = "${var.name}-public"
}
}
# modules/vpc/variables.tf
variable "name" {
type = string
}
variable "cidr_block" {
type = string
}
variable "public_subnet_cidr" {
type = string
}
variable "availability_zone" {
type = string
}
# modules/vpc/outputs.tf
output "vpc_id" {
value = aws_vpc.main.id
}
output "subnet_id" {
value = aws_subnet.public.id
}
# main.tf - Using the module
module "vpc" {
source = "./modules/vpc"
name = "production"
cidr_block = "10.0.0.0/16"
public_subnet_cidr = "10.0.1.0/24"
availability_zone = "us-east-1a"
}State Management
Terraform state tracks resource metadata. Store state remotely for team collaboration:
# backend.tf
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}Setup remote state:
# Create S3 bucket for state
aws s3api create-bucket \
--bucket my-terraform-state \
--region us-east-1
# Enable versioning
aws s3api put-bucket-versioning \
--bucket my-terraform-state \
--versioning-configuration Status=Enabled
# Enable encryption
aws s3api put-bucket-encryption \
--bucket my-terraform-state \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}]
}'
# Create DynamoDB table for state locking
aws dynamodb create-table \
--table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUESTWorkspaces
Workspaces manage multiple environments with same configuration:
# Create workspace
terraform workspace new production
# List workspaces
terraform workspace list
# Switch workspace
terraform workspace select production
# Use workspace in configuration
resource "aws_instance" "app" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = terraform.workspace == "production" ? "t3.large" : "t3.micro"
tags = {
Environment = terraform.workspace
}
}AWS Resources with Terraform
EC2 Instance
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
user_data = base64encode(<<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl start nginx
EOF
)
tags = {
Name = "web-server"
}
}RDS Database
resource "aws_db_instance" "main" {
identifier = "mydb"
engine = "postgres"
engine_version = "15.3"
instance_class = "db.t3.micro"
allocated_storage = 20
storage_type = "gp3"
db_name = "myapp"
username = "admin"
password = random_password.db_password.result
skip_final_snapshot = false
final_snapshot_identifier = "mydb-final-snapshot"
tags = {
Name = "production-db"
}
}
resource "random_password" "db_password" {
length = 16
special = true
}Load Balancer
resource "aws_lb" "main" {
name = "my-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = [aws_subnet.public1.id, aws_subnet.public2.id]
tags = {
Name = "production-alb"
}
}
resource "aws_lb_target_group" "app" {
name = "app-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
target_type = "instance"
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
interval = 30
path = "/"
matcher = "200"
}
}
resource "aws_lb_listener" "app" {
load_balancer_arn = aws_lb.main.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.app.arn
}
}Terraform Best Practices
Code Organization
terraform/
├── main.tf # Main configuration
├── variables.tf # Variable definitions
├── outputs.tf # Output definitions
├── backend.tf # Backend configuration
├── terraform.tfvars # Variable values (gitignored)
├── modules/
│ ├── vpc/
│ ├── rds/
│ └── ecs/
└── environments/
├── dev/
├── staging/
└── production/Validation and Testing
# Validate syntax
terraform validate
# Format code
terraform fmt -recursive
# Use tflint for linting
tflint --init
tflint
# Use terraform-docs to generate documentation
terraform-docs markdown . > README.md❓ What is the primary purpose of Terraform?
❓ What does `terraform plan` do?
❓ What is a Terraform module?
❓ Where should Terraform state be stored for team collaboration?
❓ What is the purpose of Terraform workspaces?