🏗️ Infrastructure as Code (IaC) Concepts¶
Overview¶
Infrastructure as Code (IaC) is the practice of managing and provisioning infrastructure through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools.
Core Concepts¶
1. Infrastructure as Code (IaC)¶
Definition: Manage & provision infrastructure via machine-readable config files instead of manual processes.
Benefits: - ✅ Version control for infrastructure - ✅ Reproducible environments - ✅ Automated provisioning - ✅ Reduced human error - ✅ Documentation as code
Traditional Approach IaC Approach
───────────────── ─────────────
Manual clicks in UI → Code in version control
Inconsistent setups → Reproducible deployments
Hard to track changes → Full audit trail
Slow provisioning → Automated & fast
Declarative vs Imperative¶
Declarative (Terraform's Approach)¶
You define WHAT the end state should be; Terraform figures out HOW to get there.
# Declarative - You describe the desired state
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
count = 3
tags = {
Name = "web-server"
}
}
Terraform automatically: - Creates 3 instances if none exist - Updates instances if configuration changes - Destroys extra instances if count is reduced - Does nothing if state matches desired state
Imperative (Traditional Scripting)¶
You define HOW to achieve the result step-by-step.
# Imperative - You describe the steps
for i in 1 2 3; do
aws ec2 run-instances \
--image-id ami-0c55b159cbfafe1f0 \
--instance-type t2.micro \
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=web-server-$i}]"
done
You must handle: - Checking if instances already exist - Updating existing instances - Cleaning up old instances - Error handling at each step
Idempotency¶
Definition: Running the same configuration multiple times always produces the same result.
┌─────────────────────────────────────────────────────────┐
│ Idempotent Behavior │
├─────────────────────────────────────────────────────────┤
│ │
│ Run 1: terraform apply → Creates 3 instances │
│ Run 2: terraform apply → No changes (already exists) │
│ Run 3: terraform apply → No changes (already exists) │
│ │
│ ✅ Safe to run multiple times │
│ ✅ No duplicate resources created │
│ ✅ Predictable outcomes │
└─────────────────────────────────────────────────────────┘
Example: Idempotent Configuration¶
resource "aws_s3_bucket" "data" {
bucket = "my-unique-bucket-name"
tags = {
Environment = "Production"
}
}
# First apply: Creates bucket
# Second apply: No changes
# Third apply: No changes
# If bucket exists outside Terraform: Import it, then no changes
Infrastructure Lifecycle¶
Day 0 - Initial Provisioning¶
Definition: Initial provisioning of brand-new infrastructure.
┌──────────────────────────────────────────────────┐
│ Day 0: Initial Setup │
├──────────────────────────────────────────────────┤
│ │
│ 1. Write Terraform configuration │
│ 2. terraform init │
│ 3. terraform plan │
│ 4. terraform apply │
│ │
│ Result: Infrastructure created from scratch │
└──────────────────────────────────────────────────┘
Example:
# Day 0: Creating initial infrastructure
terraform {
required_version = ">= 1.0"
}
provider "aws" {
region = "us-east-1"
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "main-vpc"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
tags = {
Name = "public-subnet"
}
}
Day 1+ - Ongoing Maintenance¶
Definition: Ongoing changes and maintenance of existing infrastructure.
┌──────────────────────────────────────────────────┐
│ Day 1+: Ongoing Operations │
├──────────────────────────────────────────────────┤
│ │
│ • Scaling resources up/down │
│ • Updating configurations │
│ • Adding new resources │
│ • Removing deprecated resources │
│ • Security patches │
│ • Cost optimization │
│ │
└──────────────────────────────────────────────────┘
Example:
# Day 1+: Scaling and updating
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.small" # Changed from t2.micro
count = 5 # Scaled from 3 to 5
tags = {
Name = "web-server"
Environment = "production" # Added new tag
}
}
Immutable vs Mutable Infrastructure¶
Immutable Infrastructure (Terraform's Approach)¶
Definition: Replace resources when changes are needed instead of modifying them in-place.
┌─────────────────────────────────────────────────────────┐
│ Immutable Infrastructure Pattern │
├─────────────────────────────────────────────────────────┤
│ │
│ Old Server (v1.0) New Server (v1.1) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Running │ │ Creating │ │
│ │ App v1.0 │ → │ App v1.1 │ │
│ └──────────────┘ └──────────────┘ │
│ ↓ ↓ │
│ Terminate Running │
│ │
│ ✅ No configuration drift │
│ ✅ Predictable deployments │
│ ✅ Easy rollback │
└─────────────────────────────────────────────────────────┘
Example:
resource "aws_launch_template" "app" {
name_prefix = "app-"
image_id = "ami-new-version" # New AMI
instance_type = "t2.micro"
# When this changes, new instances are created
# Old instances are terminated
user_data = base64encode(<<-EOF
#!/bin/bash
echo "App Version 2.0"
EOF
)
}
resource "aws_autoscaling_group" "app" {
desired_capacity = 3
max_size = 5
min_size = 1
launch_template {
id = aws_launch_template.app.id
version = "$Latest"
}
# Rolling update strategy
instance_refresh {
strategy = "Rolling"
preferences {
min_healthy_percentage = 50
}
}
}
Mutable Infrastructure (Traditional Approach)¶
Definition: Update resources in-place without replacement.
┌─────────────────────────────────────────────────────────┐
│ Mutable Infrastructure Pattern │
├─────────────────────────────────────────────────────────┤
│ │
│ Server (v1.0) Server (v1.1) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Running │ │ Running │ │
│ │ App v1.0 │ → │ App v1.1 │ │
│ └──────────────┘ └──────────────┘ │
│ ↓ ↓ │
│ Update in-place Same server │
│ │
│ ⚠️ Configuration drift possible │
│ ⚠️ Harder to rollback │
│ ⚠️ Inconsistent states │
└─────────────────────────────────────────────────────────┘
IaC Workflow Diagram¶
┌─────────────────────────────────────────────────────────────────┐
│ Terraform IaC Workflow │
└─────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ Write Code │ ← Define infrastructure in .tf files
└──────┬───────┘
│
▼
┌──────────────┐
│ Version │ ← Commit to Git (version control)
│ Control │
└──────┬───────┘
│
▼
┌──────────────┐
│ terraform │ ← Initialize & download providers
│ init │
└──────┬───────┘
│
▼
┌──────────────┐
│ terraform │ ← Preview changes
│ plan │
└──────┬───────┘
│
▼
┌──────────────┐
│ Review │ ← Human review of planned changes
│ Changes │
└──────┬───────┘
│
▼
┌──────────────┐
│ terraform │ ← Apply changes to infrastructure
│ apply │
└──────┬───────┘
│
▼
┌──────────────┐
│ Infrastructure│ ← Real cloud resources created/updated
│ Updated │
└──────────────┘
Benefits of IaC with Terraform¶
1. Version Control¶
Git History
───────────
commit abc123: Add production VPC
commit def456: Scale web servers to 5
commit ghi789: Update security groups
commit jkl012: Add RDS database
2. Collaboration¶
Team Workflow
─────────────
Developer A: Creates feature branch
Developer B: Reviews pull request
Developer C: Approves changes
CI/CD: Automatically applies to staging
Team Lead: Promotes to production
3. Disaster Recovery¶
Disaster Recovery
─────────────────
Problem: Region failure
Solution: terraform apply in new region
Result: Identical infrastructure in minutes
4. Multi-Environment Management¶
# Same code, different environments
module "infrastructure" {
source = "./modules/infra"
environment = var.environment # dev, staging, prod
instance_count = var.environment == "prod" ? 10 : 2
instance_type = var.environment == "prod" ? "t2.large" : "t2.micro"
}
Best Practices¶
✅ DO¶
- Use version control for all Terraform code
- Write modular, reusable configurations
- Use remote state with locking
- Implement proper naming conventions
- Document your infrastructure code
- Use workspaces or separate state files for environments
- Run
terraform planbeforeapply - Use
.gitignorefor sensitive files
❌ DON'T¶
- Store state files in version control
- Hardcode credentials in configurations
- Make manual changes to managed resources
- Skip the plan step
- Use
terraform apply -auto-approvein production - Share state files without locking
- Ignore Terraform warnings
Real-World Example: Complete Infrastructure¶
# Complete example showing IaC principles
terraform {
required_version = ">= 1.0"
# Remote state (collaboration)
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
# Provider configuration
provider "aws" {
region = var.region
default_tags {
tags = {
ManagedBy = "Terraform"
Environment = var.environment
Project = "MyApp"
}
}
}
# Variables (parameterization)
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "environment" {
description = "Environment name"
type = string
}
# Declarative resource definition
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "${var.environment}-vpc"
}
}
# Outputs (documentation & integration)
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
Summary¶
| Concept | Description | Terraform Approach |
|---|---|---|
| IaC | Infrastructure as code | ✅ Full support |
| Declarative | Define desired state | ✅ Core principle |
| Idempotent | Safe to run multiple times | ✅ Built-in |
| Day 0 | Initial provisioning | ✅ Supported |
| Day 1+ | Ongoing maintenance | ✅ Supported |
| Immutable | Replace vs update | ✅ Default behavior |