Infrastructure as Code¶
IaC Overview¶
Terraform¶
Basic Structure¶
# main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
Project = var.project_name
}
}
}
Variables¶
# variables.tf
variable "environment" {
description = "Environment name"
type = string
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "Environment must be dev, staging, or production."
}
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.medium"
}
variable "vpc_cidr" {
description = "VPC CIDR block"
type = string
default = "10.0.0.0/16"
}
variable "availability_zones" {
description = "List of availability zones"
type = list(string)
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}
variable "tags" {
description = "Additional tags"
type = map(string)
default = {}
}
# terraform.tfvars
environment = "production"
instance_type = "t3.large"
Resources¶
# VPC and Networking
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
}
}
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index)
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.project_name}-private-${count.index + 1}"
}
}
# ECS Cluster
resource "aws_ecs_cluster" "main" {
name = "${var.project_name}-cluster"
setting {
name = "containerInsights"
value = "enabled"
}
}
# ECS Service
resource "aws_ecs_service" "app" {
name = "${var.project_name}-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = var.desired_count
launch_type = "FARGATE"
network_configuration {
subnets = aws_subnet.private[*].id
security_groups = [aws_security_group.ecs.id]
}
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = "app"
container_port = 8080
}
lifecycle {
ignore_changes = [desired_count] # Allow autoscaling
}
}
Modules¶
# modules/vpc/main.tf
resource "aws_vpc" "this" {
cidr_block = var.cidr_block
# ...
}
output "vpc_id" {
value = aws_vpc.this.id
}
# main.tf (usage)
module "vpc" {
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
environment = var.environment
}
module "vpc_prod" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "production-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
single_nat_gateway = false
}
Data Sources¶
# Look up existing resources
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
# Use in resources
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
}
Outputs¶
# outputs.tf
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "load_balancer_dns" {
description = "DNS name of the load balancer"
value = aws_lb.main.dns_name
}
output "database_endpoint" {
description = "RDS endpoint"
value = aws_db_instance.main.endpoint
sensitive = true
}
Terraform Commands¶
# Initialize
terraform init
terraform init -upgrade # Upgrade providers
# Plan
terraform plan
terraform plan -out=tfplan # Save plan
terraform plan -target=aws_instance.web # Specific resource
# Apply
terraform apply
terraform apply tfplan # Apply saved plan
terraform apply -auto-approve # Skip confirmation
# Destroy
terraform destroy
terraform destroy -target=aws_instance.web
# State management
terraform state list
terraform state show aws_instance.web
terraform state mv aws_instance.web aws_instance.new_name
terraform state rm aws_instance.web
terraform import aws_instance.web i-1234567890
# Workspace (for multiple environments)
terraform workspace list
terraform workspace new staging
terraform workspace select production
# Format and validate
terraform fmt
terraform fmt -recursive
terraform validate
# Output
terraform output
terraform output -json
State Management¶
# Remote state configuration
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
# Reference remote state
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-terraform-state"
key = "network/terraform.tfstate"
region = "us-east-1"
}
}
# Use outputs from remote state
resource "aws_instance" "web" {
subnet_id = data.terraform_remote_state.network.outputs.subnet_id
}
Best Practices¶
Comparison: Terraform vs CloudFormation vs Pulumi¶
Pulumi Example¶
# Python Pulumi example
import pulumi
import pulumi_aws as aws
# Create VPC
vpc = aws.ec2.Vpc("main-vpc",
cidr_block="10.0.0.0/16",
enable_dns_hostnames=True,
tags={"Name": "main-vpc"}
)
# Create subnet
subnet = aws.ec2.Subnet("main-subnet",
vpc_id=vpc.id,
cidr_block="10.0.1.0/24",
availability_zone="us-east-1a",
tags={"Name": "main-subnet"}
)
# Create ECS cluster
cluster = aws.ecs.Cluster("app-cluster",
settings=[aws.ecs.ClusterSettingArgs(
name="containerInsights",
value="enabled"
)]
)
# Export outputs
pulumi.export("vpc_id", vpc.id)
pulumi.export("cluster_arn", cluster.arn)
Common Interview Questions¶
- What is Terraform state?
- Maps real infrastructure to config
- Tracks resource metadata
-
Should be stored remotely for teams
-
How to manage multiple environments?
- Workspaces
- Separate directories
-
Variable files per environment
-
Terraform vs CloudFormation?
- Terraform: Multi-cloud, HCL, community modules
-
CF: AWS-native, managed state, deep AWS integration
-
What is drift?
- When real infrastructure differs from code
- Detected with
terraform plan -
Reconciled with
terraform apply -
How to handle secrets?
- Don't store in code
- Use AWS Secrets Manager, Vault
- Mark outputs as sensitive
- *