Terraform lets you define and manage infrastructure through declarative configuration.
Basic Structure#
1# main.tf
2terraform {
3 required_version = ">= 1.0"
4
5 required_providers {
6 aws = {
7 source = "hashicorp/aws"
8 version = "~> 5.0"
9 }
10 }
11
12 backend "s3" {
13 bucket = "my-terraform-state"
14 key = "prod/terraform.tfstate"
15 region = "us-east-1"
16 }
17}
18
19provider "aws" {
20 region = var.aws_region
21}Variables and Outputs#
1# variables.tf
2variable "aws_region" {
3 description = "AWS region"
4 type = string
5 default = "us-east-1"
6}
7
8variable "environment" {
9 description = "Environment name"
10 type = string
11}
12
13variable "instance_type" {
14 description = "EC2 instance type"
15 type = string
16 default = "t3.micro"
17
18 validation {
19 condition = can(regex("^t3\\.", var.instance_type))
20 error_message = "Instance type must be t3 family."
21 }
22}
23
24# outputs.tf
25output "instance_ip" {
26 description = "Public IP of the instance"
27 value = aws_instance.web.public_ip
28}
29
30output "load_balancer_dns" {
31 description = "DNS name of load balancer"
32 value = aws_lb.main.dns_name
33}Resources#
1# VPC
2resource "aws_vpc" "main" {
3 cidr_block = "10.0.0.0/16"
4 enable_dns_hostnames = true
5
6 tags = {
7 Name = "${var.environment}-vpc"
8 Environment = var.environment
9 }
10}
11
12# Subnets
13resource "aws_subnet" "public" {
14 count = 2
15 vpc_id = aws_vpc.main.id
16 cidr_block = "10.0.${count.index + 1}.0/24"
17 availability_zone = data.aws_availability_zones.available.names[count.index]
18
19 map_public_ip_on_launch = true
20
21 tags = {
22 Name = "${var.environment}-public-${count.index + 1}"
23 }
24}
25
26# Security Group
27resource "aws_security_group" "web" {
28 name = "${var.environment}-web-sg"
29 description = "Security group for web servers"
30 vpc_id = aws_vpc.main.id
31
32 ingress {
33 from_port = 80
34 to_port = 80
35 protocol = "tcp"
36 cidr_blocks = ["0.0.0.0/0"]
37 }
38
39 ingress {
40 from_port = 443
41 to_port = 443
42 protocol = "tcp"
43 cidr_blocks = ["0.0.0.0/0"]
44 }
45
46 egress {
47 from_port = 0
48 to_port = 0
49 protocol = "-1"
50 cidr_blocks = ["0.0.0.0/0"]
51 }
52}Modules#
1# modules/vpc/main.tf
2variable "cidr_block" {
3 type = string
4}
5
6variable "environment" {
7 type = string
8}
9
10resource "aws_vpc" "this" {
11 cidr_block = var.cidr_block
12
13 tags = {
14 Environment = var.environment
15 }
16}
17
18output "vpc_id" {
19 value = aws_vpc.this.id
20}
21
22# Using the module
23module "vpc" {
24 source = "./modules/vpc"
25
26 cidr_block = "10.0.0.0/16"
27 environment = var.environment
28}
29
30resource "aws_subnet" "public" {
31 vpc_id = module.vpc.vpc_id
32 # ...
33}Data Sources#
1# Look up existing resources
2data "aws_ami" "amazon_linux" {
3 most_recent = true
4 owners = ["amazon"]
5
6 filter {
7 name = "name"
8 values = ["amzn2-ami-hvm-*-x86_64-gp2"]
9 }
10}
11
12data "aws_availability_zones" "available" {
13 state = "available"
14}
15
16resource "aws_instance" "web" {
17 ami = data.aws_ami.amazon_linux.id
18 instance_type = var.instance_type
19}Locals and Expressions#
1locals {
2 common_tags = {
3 Environment = var.environment
4 Project = "myapp"
5 ManagedBy = "terraform"
6 }
7
8 subnet_cidrs = [
9 for i in range(var.subnet_count) :
10 cidrsubnet(var.vpc_cidr, 8, i)
11 ]
12}
13
14resource "aws_vpc" "main" {
15 cidr_block = var.vpc_cidr
16 tags = merge(local.common_tags, { Name = "main-vpc" })
17}State Management#
1# Initialize with backend
2terraform init
3
4# View state
5terraform state list
6terraform state show aws_instance.web
7
8# Move resources
9terraform state mv aws_instance.old aws_instance.new
10
11# Import existing resources
12terraform import aws_instance.web i-1234567890abcdef0
13
14# Remove from state (without destroying)
15terraform state rm aws_instance.webWorkspaces#
1# Create and switch workspaces
2terraform workspace new staging
3terraform workspace new production
4terraform workspace select staging
5
6# Use in configuration
7resource "aws_instance" "web" {
8 instance_type = terraform.workspace == "production" ? "t3.large" : "t3.micro"
9}Best Practices#
1# 1. Use for_each over count when possible
2resource "aws_iam_user" "users" {
3 for_each = toset(var.user_names)
4 name = each.value
5}
6
7# 2. Use lifecycle rules carefully
8resource "aws_instance" "web" {
9 # ...
10
11 lifecycle {
12 create_before_destroy = true
13 prevent_destroy = true
14 ignore_changes = [tags]
15 }
16}
17
18# 3. Use depends_on sparingly
19resource "aws_instance" "web" {
20 depends_on = [aws_internet_gateway.main]
21}Terraform enables reproducible, version-controlled infrastructure management.