Skip to content

Terraform


Terraform is an open-source Infrastructure as Code (IaC) tool that enables you to define and provision infrastructure using a high-level configuration language.

Terraform Overview
+------------------------------------------------------------------+
| |
| +------------------------+ |
| | Terraform | |
| +------------------------+ |
| | |
| +---------------------+---------------------+ |
| | | | | |
| v v v v |
| +----------+ +----------+ +----------+ +----------+ |
| | HCL | | State | | Providers| | Modules | |
| | Config | | Management| | | | | |
| | | | | | | | | |
| | - Declar | | - Track | | - AWS | | - Reuse | |
| | ative | | Infra | | - Azure | | - Share | |
| | - Idempo | | - Lock | | - GCP | | - Version| |
| +----------+ +----------+ +----------+ +----------+ |
| |
+------------------------------------------------------------------+
FeatureDescription
HCLHashiCorp Configuration Language
StateTracks infrastructure state
ProvidersCloud platform integrations
ModulesReusable configurations

Terraform Configuration Structure
+------------------------------------------------------------------+
| |
| main.tf |
| +------------------------------------------------------------+ |
| | | |
| | # Provider configuration | |
| | provider "aws" { | |
| | region = "us-east-1" | |
| | } | |
| | | |
| | # Resource definition | |
| | resource "aws_vpc" "main" { | |
| | cidr_block = "10.0.0.0/16" | |
| | tags = { | |
| | Name = "main-vpc" | |
| | } | |
| | } | |
| | | |
| +------------------------------------------------------------+ |
| |
| variables.tf |
| +------------------------------------------------------------+ |
| | | |
| | variable "region" { | |
| | type = string | |
| | default = "us-east-1" | |
| | description = "AWS region" | |
| | } | |
| | | |
| +------------------------------------------------------------+ |
| |
| outputs.tf |
| +------------------------------------------------------------+ |
| | | |
| | output "vpc_id" { | |
| | value = aws_vpc.main.id | |
| | description = "VPC ID" | |
| | } | |
| | | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
# Basic resource syntax
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "WebServer"
Environment = "Production"
}
}
# Resource with dependencies
resource "aws_eip" "web_eip" {
instance = aws_instance.web_server.id
vpc = true
}
# Resource with count
resource "aws_instance" "server" {
count = 3
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "Server-${count.index + 1}"
}
}
# Resource with for_each
resource "aws_instance" "server" {
for_each = toset(["web", "api", "db"])
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "Server-${each.key}"
}
}

Terraform State Management
+------------------------------------------------------------------+
| |
| Local State |
| +------------------------------------------------------------+ |
| | | |
| | terraform.tfstate | |
| | +--------------------------------------------------------+ | |
| | | - Stored locally | | |
| | | - Not recommended for teams | | |
| | | - No locking | | |
| | +--------------------------------------------------------+ | |
| | | |
| +------------------------------------------------------------+ |
| |
| Remote State (S3 + DynamoDB) |
| +------------------------------------------------------------+ |
| | | |
| | terraform { | |
| | backend "s3" { | |
| | bucket = "my-terraform-state" | |
| | key = "prod/terraform.tfstate" | |
| | region = "us-east-1" | |
| | encrypt = true | |
| | dynamodb_table = "terraform-locks" | |
| | } | |
| | } | |
| | | |
| +------------------------------------------------------------+ |
| |
| State Locking |
| +------------------------------------------------------------+ |
| | | |
| | - Prevents concurrent modifications | |
| | - DynamoDB table for S3 backend | |
| | - Automatic lock acquisition | |
| | | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Terraform Module Structure
+------------------------------------------------------------------+
| |
| modules/ |
| +------------------------------------------------------------+ |
| | | |
| | vpc/ | |
| | +--------------------------------------------------------+ | |
| | | main.tf # Main resource definitions | | |
| | | variables.tf # Input variables | | |
| | | outputs.tf # Output values | | |
| | | README.md # Module documentation | | |
| | +--------------------------------------------------------+ | |
| | | |
| +------------------------------------------------------------+ |
| |
| Using Modules |
| +------------------------------------------------------------+ |
| | | |
| | module "vpc" { | |
| | source = "./modules/vpc" | |
| | | |
| | vpc_cidr = "10.0.0.0/16" | |
| | environment = "production" | |
| | az_count = 3 | |
| | } | |
| | | |
| | # Using remote module from registry | |
| | module "vpc" { | |
| | source = "terraform-aws-modules/vpc/aws" | |
| | version = "~> 3.0" | |
| | | |
| | name = "my-vpc" | |
| | cidr = "10.0.0.0/16" | |
| | } | |
| | | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Terraform Workflow
+------------------------------------------------------------------+
| |
| 1. Write |
| +------------------------------------------------------------+ |
| | | |
| | - Define infrastructure in HCL | |
| | - Create configuration files | |
| | | |
| +------------------------------------------------------------+ |
| | |
| v |
| 2. Plan |
| +------------------------------------------------------------+ |
| | | |
| | $ terraform plan | |
| | | |
| | - Preview changes | |
| | - Compare state with configuration | |
| | - Show execution plan | |
| | | |
| +------------------------------------------------------------+ |
| | |
| v |
| 3. Apply |
| +------------------------------------------------------------+ |
| | | |
| | $ terraform apply | |
| | | |
| | - Execute changes | |
| | - Update state | |
| | - Provision infrastructure | |
| | | |
| +------------------------------------------------------------+ |
| | |
| v |
| 4. Destroy |
| +------------------------------------------------------------+ |
| | | |
| | $ terraform destroy | |
| | | |
| | - Remove all resources | |
| | - Clean up state | |
| | | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Terminal window
# Initialize Terraform
terraform init
# Initialize with backend configuration
terraform init -backend-config="bucket=my-state"
# Validate configuration
terraform validate
# Format configuration files
terraform fmt
# Plan changes
terraform plan
# Plan and save to file
terraform plan -out=tfplan
# Apply changes
terraform apply
# Apply saved plan
terraform apply tfplan
# Apply with auto-approve
terraform apply -auto-approve
# Destroy infrastructure
terraform destroy
# Show state
terraform show
# List resources in state
terraform state list
# Show specific resource
terraform state show aws_vpc.main
# Move resource in state
terraform state mv aws_vpc.main aws_vpc.primary
# Remove resource from state
terraform state rm aws_vpc.main
# Import existing resource
terraform import aws_vpc.main vpc-12345678
# Output values
terraform output
# Get specific output
terraform output vpc_id
# Workspace management
terraform workspace list
terraform workspace new dev
terraform workspace select dev
terraform workspace delete dev
# Refresh state
terraform refresh
# Graph dependencies
terraform graph | dot -Tpng > graph.png

Terraform Built-in Functions
+------------------------------------------------------------------+
| |
| String Functions |
| +------------------------------------------------------------+ |
| | join(", ", ["a", "b", "c"]) # "a, b, c" | |
| | split(", ", "a, b, c") # ["a", "b", "c"] | |
| | lower("HELLO") # "hello" | |
| | upper("hello") # "HELLO" | |
| | substr("hello world", 0, 5) # "hello" | |
| | replace("hello", "l", "L") # "heLLo" | |
| | format("Hello %s!", "World") # "Hello World!" | |
| +------------------------------------------------------------+ |
| |
| Numeric Functions |
| +------------------------------------------------------------+ |
| | max(1, 2, 3) # 3 | |
| | min(1, 2, 3) # 1 | |
| | sum([1, 2, 3]) # 6 | |
| | abs(-5) # 5 | |
| | ceil(4.3) # 5 | |
| | floor(4.7) # 4 | |
| +------------------------------------------------------------+ |
| |
| Collection Functions |
| +------------------------------------------------------------+ |
| | length(["a", "b"]) # 2 | |
| | element(["a", "b", "c"], 1) # "b" | |
| | contains(["a", "b"], "a") # true | |
| | distinct(["a", "a", "b"]) # ["a", "b"] | |
| | merge({a="1"}, {b="2"}) # {a="1", b="2"} | |
| | keys({a=1, b=2}) # ["a", "b"] | |
| | values({a=1, b=2}) # [1, 2] | |
| +------------------------------------------------------------+ |
| |
| Encoding Functions |
| +------------------------------------------------------------+ |
| | base64encode("hello") # "aGVsbG8=" | |
| | base64decode("aGVsbG8=") # "hello" | |
| | jsonencode({"hello"="world"}) # {"hello":"world"} | |
| | jsondecode("{\"hello\":\"world\"}") # {"hello"="world"} | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
# Conditional expression (ternary)
resource "aws_instance" "web" {
count = var.environment == "production" ? 3 : 1
instance_type = var.environment == "production" ? "t3.large" : "t3.micro"
tags = {
Name = var.enable_monitoring ? "Monitored-Server" : "Server"
}
}
# Dynamic blocks for repeated nested blocks
resource "aws_security_group" "example" {
name = "dynamic-sg"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
# For expressions
locals {
# Transform list
uppercase_names = [for name in var.names : upper(name)]
# Filter list
long_names = [for name in var.names : name if length(name) > 5]
# Transform map
instance_types = { for k, v in var.environments : k => v.instance_type }
# Create map from list
name_to_id = { for instance in aws_instance.web : instance.tags.Name => instance.id }
}
Terraform Data Sources
+------------------------------------------------------------------+
| |
| Data sources read existing AWS resources |
| +------------------------------------------------------------+ |
| | | |
| | # Get latest Amazon Linux 2 AMI | |
| | data "aws_ami" "amazon_linux" { | |
| | most_recent = true | |
| | owners = ["amazon"] | |
| | | |
| | filter { | |
| | name = "name" | |
| | values = ["amzn2-ami-hvm-*-x86_64-gp2"] | |
| | } | |
| | } | |
| | | |
| | # Get default VPC | |
| | data "aws_vpc" "default" { | |
| | default = true | |
| | } | |
| | | |
| | # Get subnet IDs in VPC | |
| | data "aws_subnets" "default" { | |
| | filter { | |
| | name = "vpc-id" | |
| | values = [data.aws_vpc.default.id] | |
| | } | |
| | } | |
| | | |
| | # Use data source in resource | |
| | resource "aws_instance" "web" { | |
| | ami = data.aws_ami.amazon_linux.id | |
| | instance_type = "t3.micro" | |
| | subnet_id = data.aws_subnets.default.ids[0] | |
| | } | |
| | | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
# Local values for DRY code
locals {
common_tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "Terraform"
CreatedAt = timestamp()
}
# Computed values
name_prefix = "${var.project_name}-${var.environment}"
# Conditional locals
instance_count = var.environment == "production" ? 3 : 1
# Complex transformations
subnet_cidrs = [for i in range(3) : cidrsubnet(var.vpc_cidr, 8, i)]
}
# Using locals in resources
resource "aws_instance" "web" {
count = local.instance_count
ami = var.ami_id
instance_type = "t3.micro"
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-web-${count.index + 1}"
})
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
# Lifecycle management
lifecycle {
# Prevent destruction
prevent_destroy = true
# Create before destroy (for zero-downtime updates)
create_before_destroy = true
# Ignore changes to specific attributes
ignore_changes = [
ami, # Ignore AMI changes
tags["LastUpdated"] # Ignore specific tag changes
]
# Replace resource when condition changes
replace_triggered_by = [
aws_security_group.web.id
]
}
}
# Blue-green deployment example
resource "aws_launch_template" "web" {
name_prefix = "web-"
image_id = var.ami_id
instance_type = "t3.micro"
lifecycle {
create_before_destroy = true
}
}

Terraform Cloud Architecture
+------------------------------------------------------------------+
| |
| +------------------------+ |
| | Terraform Cloud | |
| +------------------------+ |
| | |
| +---------------------+---------------------+ |
| | | | | |
| v v v v |
| +----------+ +----------+ +----------+ +----------+ |
| | Remote | | State | | Policy | | Private | |
| | Execution| | Management| | as Code | | Module | |
| | | | | | | | Registry| |
| | - VCS | | - Encrypted| | - Sentinel| | - Share | |
| | Integra-| | - Versioning| | - OPA | | - Version| |
| | tion | | - Locking| | - Cost | | - Access | |
| | - API | | - History| | Estim- | | - Control| |
| | - CLI | | | | ation | | | |
| +----------+ +----------+ +----------+ +----------+ |
| |
+------------------------------------------------------------------+
# Configure Terraform Cloud backend
terraform {
cloud {
organization = "my-organization"
workspaces {
name = "my-workspace"
}
}
}
# Workspace-based configuration
# terraform.tfvars in Terraform Cloud
environment = "production"
region = "us-east-1"
# Environment variables in Terraform Cloud
# TF_VAR_database_password = "sensitive-value"
# AWS_ACCESS_KEY_ID = "AKIA..."
# AWS_SECRET_ACCESS_KEY = "secret..."

.github/workflows/terraform.yml
name: Terraform CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.5.0
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Terraform Init
run: terraform init
- name: Terraform Format
run: terraform fmt -check
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
run: terraform plan -out=tfplan
if: github.event_name == 'pull_request'
- name: Terraform Apply
run: terraform apply -auto-approve
if: github.ref == 'refs/heads/main' && github.event_name == 'push'

Terraform Best Practices
+------------------------------------------------------------------+
| |
| 1. Use remote state with locking |
| +------------------------------------------------------------+ |
| | - S3 backend with DynamoDB for locking | |
| | - Enable encryption | |
| +------------------------------------------------------------+ |
| |
| 2. Use modules for reusability |
| +------------------------------------------------------------+ |
| | - Create reusable modules | |
| | - Use Terraform Registry modules | |
| +------------------------------------------------------------+ |
| |
| 3. Use variables for flexibility |
| +------------------------------------------------------------+ |
| | - Parameterize configurations | |
| | - Use variable files (terraform.tfvars) | |
| +------------------------------------------------------------+ |
| |
| 4. Implement proper naming |
| +------------------------------------------------------------+ |
| | - Consistent resource naming | |
| | - Use tags for organization | |
| +------------------------------------------------------------+ |
| |
| 5. Use workspaces for environments |
| +------------------------------------------------------------+ |
| | - Separate state per environment | |
| | - dev, staging, prod workspaces | |
| +------------------------------------------------------------+ |
| |
| 6. Version control your code |
| +------------------------------------------------------------+ |
| | - Store in Git | |
| | - Use branches for changes | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Exam Tip

Key Exam Points
+------------------------------------------------------------------+
| |
| 1. Terraform uses HCL (HashiCorp Configuration Language) |
| |
| 2. State tracks infrastructure and must be protected |
| |
| 3. Use S3 + DynamoDB for remote state with locking |
| |
| 4. terraform plan shows changes before apply |
| |
| 5. terraform import brings existing resources under management |
| |
| 6. Modules enable code reuse and organization |
| |
| 7. Workspaces separate environments |
| |
| 8. Providers connect Terraform to cloud platforms |
| |
| 9. Use terraform destroy to remove all resources |
| |
| 10. State file is sensitive - never commit to Git |
| |
+------------------------------------------------------------------+

Chapter 41 Summary
+------------------------------------------------------------------+
| |
| Terraform Core Concepts |
| +------------------------------------------------------------+ |
| | - HCL: Declarative configuration language | |
| | - State: Infrastructure tracking | |
| | - Providers: Cloud integrations | |
| | - Modules: Reusable configurations | |
| +------------------------------------------------------------+ |
| |
| Key Commands |
| +------------------------------------------------------------+ |
| | - init: Initialize configuration | |
| | - plan: Preview changes | |
| | - apply: Execute changes | |
| | - destroy: Remove infrastructure | |
| +------------------------------------------------------------+ |
| |
| Best Practices |
| +------------------------------------------------------------+ |
| | - Remote state with locking | |
| | - Use modules | |
| | - Version control | |
| | - Use workspaces | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Next Chapter: Chapter 42: Packer - Machine Image Building