Back to Blog
TerraformInfrastructure as CodeDevOpsAWS

Terraform: Infrastructure as Code Fundamentals

Manage infrastructure with Terraform. Learn resources, modules, state management, and best practices.

B
Bootspring Team
Engineering
February 27, 2026
4 min read

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.web

Workspaces#

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.

Share this article

Help spread the word about Bootspring