DevOps

Terraform Basics

Learn Infrastructure as Code with Terraform. Understand providers, resources, state management, and creating your first infrastructure.

By TechCoder TeamLast updated: 2026-06-02
In a Nutshell

Learn Infrastructure as Code with Terraform. Understand providers, resources, state management, and creating your first infrastructure. This hands-on tutorial focuses on practical implementation of terraform basics concepts.

Terraform Basics

Terraform is an open-source Infrastructure as Code tool that enables you to define and provision infrastructure using a declarative configuration language.

Why Terraform?

┌─────────────────────────────────────────────────────────────────┐
│                  Infrastructure as Code                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   Manual (Click-Ops)    ──────>   Terraform (IaC)             │
│                                                                  │
│   ❌ Slow              ✅ Fast automated provisioning           │
│   ❌ Error-prone       ✅ Consistent, repeatable                │
│   ❌ No history        ✅ Version controlled                    │
│   ❌ Hard to replicate ✅ Same code for all environments        │
│   ❌ Documentation     ✅ Self-documenting                      │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Terraform Architecture

┌─────────────────────────────────────────────────────────────────┐
│                      Terraform Workflow                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   Terraform Configuration (.tf files)                          │
│          │                                                       │
│          ▼                                                       │
│   ┌──────────────┐     ┌──────────────┐     ┌──────────────┐   │
│   │  terraform   │────>│  terraform   │────>│  terraform   │   │
│   │   init       │     │    plan    │     │   apply    │   │
│   │              │     │              │     │              │   │
│   │ • Download   │     │ • Refresh    │     │ • Create/    │   │
│   │   providers  │     │ • Compare    │     │   Update/    │   │
│   │ • Setup      │     │ • Show       │     │   Destroy    │   │
│   │   modules    │     │   execution  │     │   resources  │   │
│   │ • Initialize │     │   plan       │     │              │   │
│   │   backend    │     │              │     │              │   │
│   └──────────────┘     └──────────────┘     └──────────────┘   │
│                                  │              │               │
│                                  │              ▼               │
│                                  │       ┌──────────────┐        │
│                                  │       │  State File  │        │
│                                  │       │  (terraform. │        │
│                                  │       │   tfstate)   │        │
│                                  │       └──────────────┘        │
│                                  ▼                               │
│                           ┌──────────────┐                      │
│                           │   Providers  │                      │
│                           │  (AWS/Azure/ │                      │
│                           │   GCP/etc)   │                      │
│                           └──────────────┘                      │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Installation and Setup

# Install Terraform (macOS)
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

# Linux
wget https://releases.hashicorp.com/terraform/1.6.0/terraform_1.6.0_linux_amd64.zip
unzip terraform_1.6.0_linux_amd64.zip
sudo mv terraform /usr/local/bin/

# Verify
terraform -version

# Enable autocomplete
terraform -install-autocomplete

Terraform Configuration

File Structure

project/
├── main.tf          # Main configuration
├── variables.tf     # Variable definitions
├── outputs.tf       # Output values
├── providers.tf     # Provider configuration
├── terraform.tfvars # Variable values (gitignored)
├── versions.tf      # Provider version constraints
└── modules/         # Reusable modules
    └── vpc/
        ├── main.tf
        ├── variables.tf
        └── outputs.tf

Basic Configuration

# providers.tf
terraform {
  required_version = ">= 1.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.0"
    }
  }
  
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

provider "aws" {
  region = var.aws_region
  
  default_tags {
    tags = {
      Environment = var.environment
      ManagedBy   = "Terraform"
      Project     = var.project_name
    }
  }
}
# variables.tf
variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-east-1"
}

variable "environment" {
  description = "Environment name"
  type        = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod."
  }
}

variable "instance_count" {
  description = "Number of EC2 instances"
  type        = number
  default     = 2
}

variable "instance_type" {
  description = "EC2 instance type"
  type        = string
  default     = "t3.micro"
}
# main.tf
# Data source for latest Amazon Linux AMI
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

# VPC Module
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"
  
  name = "${var.environment}-vpc"
  cidr = "10.0.0.0/16"
  
  azs             = ["${var.aws_region}a", "${var.aws_region}b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]
  
  enable_nat_gateway = true
  single_nat_gateway = var.environment != "prod"
  
  tags = {
    Terraform = "true"
  }
}

# Security Group
resource "aws_security_group" "web" {
  name_prefix = "${var.environment}-web-"
  description = "Allow HTTP/HTTPS inbound traffic"
  vpc_id      = module.vpc.vpc_id
  
  ingress {
    description = "HTTP from anywhere"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    description = "HTTPS from anywhere"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  egress {
    description = "Allow all outbound"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  lifecycle {
    create_before_destroy = true
  }
}

# EC2 Instances
resource "aws_instance" "web" {
  count                  = var.instance_count
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = var.instance_type
  subnet_id              = module.vpc.public_subnets[count.index % length(module.vpc.public_subnets)]
  vpc_security_group_ids = [aws_security_group.web.id]
  
  user_data = <<-EOF
              #!/bin/bash
              yum update -y
              yum install -y nginx
              systemctl start nginx
              systemctl enable nginx
              echo "<h1>Server ${count.index + 1}</h1>" > /usr/share/nginx/html/index.html
              EOF
  
  tags = {
    Name = "${var.environment}-web-${count.index + 1}"
  }
}
# outputs.tf
output "vpc_id" {
  description = "ID of the VPC"
  value       = module.vpc.vpc_id
}

output "public_subnet_ids" {
  description = "IDs of public subnets"
  value       = module.vpc.public_subnets
}

output "instance_ips" {
  description = "Public IPs of EC2 instances"
  value       = aws_instance.web[*].public_ip
}

output "web_urls" {
  description = "URLs to access web servers"
  value       = [for ip in aws_instance.web[*].public_ip : "http://${ip}"]
}

Terraform Commands

# Initialize (download providers, setup backend)
terraform init

# Validate configuration
terraform validate

# Format code
terraform fmt
terraform fmt -recursive

# Generate execution plan
terraform plan
terraform plan -out=tfplan

# Apply changes
terraform apply
terraform apply tfplan
terraform apply -auto-approve

# Destroy infrastructure
terraform destroy

# Show state
terraform show
terraform state list
terraform state show aws_instance.web[0]

# Import existing resources
terraform import aws_instance.web i-1234567890abcdef0

# Refresh state (reconcile with real infrastructure)
terraform refresh

# Taint resource (force recreate)
terraform taint aws_instance.web[0]

# Workspaces (multiple environments)
terraform workspace new staging
terraform workspace select staging
terraform workspace list

State Management

Remote State (S3 + DynamoDB)

# State locking with DynamoDB
resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  
  attribute {
    name = "LockID"
    type = "S"
  }
}

State Commands

# Pull state
curl -o terraform.tfstate $(terraform state pull)

# Push state
terraform state push terraform.tfstate.backup

# Remove resource from state (don't destroy)
terraform state rm aws_instance.web[0]

# Move resource in state
terraform state mv aws_instance.old aws_instance.new

Quiz

Quiz

Question 1 of 5

What command initializes a Terraform working directory?

terraform start
terraform init
terraform setup
terraform prepare

Next Steps

Now let's explore advanced Terraform concepts: modules, workspaces, and remote state.