Terraform is the most popular Infrastructure as Code (IaC) tool, used by over 80% of organizations managing cloud infrastructure. Instead of clicking through AWS console or running CLI commands, you define your infrastructure in code files that are version-controlled, reviewable, and repeatable.
Why Infrastructure as Code?
Traditional infrastructure management has problems:
- Manual setup is error-prone and slow
- No record of what changed and when
- Environment drift (dev differs from prod)
- Disaster recovery is difficult
IaC solves all of these by treating infrastructure like application code.
Terraform Core Concepts
Providers
Providers are plugins that let Terraform interact with cloud platforms, SaaS tools, and APIs. AWS, Azure, GCP, Kubernetes, and 3000+ others are supported.
Resources
Resources are the infrastructure components you want to create: EC2 instances, S3 buckets, databases, etc.
State
Terraform tracks what infrastructure it manages in a state file. This lets it know what to create, update, or delete.
Modules
Modules are reusable packages of Terraform configuration. They help you organize code and avoid repetition.
Installing Terraform
# macOS
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
# Ubuntu/Debian
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
# Verify installation
terraform versionYour First Terraform Project
Let's create an AWS S3 bucket. Create a new directory and file:
mkdir terraform-demo && cd terraform-demo
touch main.tfAdd this to main.tf:
# Configure the AWS Provider
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
# Create an S3 bucket
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-unique-bucket-name-12345"
tags = {
Name = "My First Terraform Bucket"
Environment = "Dev"
}
}
# Output the bucket name
output "bucket_name" {
value = aws_s3_bucket.my_bucket.bucket
}The Terraform Workflow
Step 1: terraform init
Downloads provider plugins and initializes the working directory:
terraform init
# Output:
# Initializing provider plugins...
# - Finding hashicorp/aws versions matching "~> 5.0"...
# - Installing hashicorp/aws v5.x.x...
# Terraform has been successfully initialized!Step 2: terraform plan
Shows what Terraform will create, modify, or destroy:
terraform plan
# Output shows:
# + aws_s3_bucket.my_bucket will be created
# + bucket = "my-unique-bucket-name-12345"
# + tags = { "Name" = "My First Terraform Bucket" }
# Plan: 1 to add, 0 to change, 0 to destroy.Step 3: terraform apply
Creates the actual infrastructure:
terraform apply
# Review the plan, then type "yes" to confirm
# aws_s3_bucket.my_bucket: Creating...
# aws_s3_bucket.my_bucket: Created!
# Apply complete! Resources: 1 added, 0 changed, 0 destroyed.Step 4: terraform destroy
Removes all resources managed by this configuration:
terraform destroy
# Type "yes" to confirm deletionUsing Variables
Make your configuration flexible with variables:
# variables.tf
variable "environment" {
description = "Deployment environment"
type = string
default = "dev"
}
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
# main.tf - using variables
provider "aws" {
region = var.region
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.instance_type
tags = {
Environment = var.environment
}
}Pass variables via command line or .tfvars files:
# Command line
terraform apply -var="environment=prod" -var="instance_type=t3.large"
# Or create terraform.tfvars
# environment = "prod"
# instance_type = "t3.large"Complete Example: EC2 with VPC
Here's a more complete example deploying an EC2 instance with networking:
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = { Name = "main-vpc" }
}
# Subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
tags = { Name = "public-subnet" }
}
# Internet Gateway
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
}
# Route Table
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# Security Group
resource "aws_security_group" "web" {
name = "web-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# EC2 Instance
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
user_data = <<-EOF
#!/bin/bash
yum install -y httpd
systemctl start httpd
echo "Hello from Terraform!" > /var/www/html/index.html
EOF
tags = { Name = "web-server" }
}
output "public_ip" {
value = aws_instance.web.public_ip
}Managing Terraform State
For team environments, store state remotely:
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}Best Practices
- Use remote state with locking for teams
- Never commit .tfstate files to git
- Use variables for environment differences
- Create modules for reusable components
- Use terraform fmt to format code
- Run terraform plan in CI before apply
- Tag all resources for cost tracking
FAQ
Terraform vs CloudFormation?
Terraform works across all clouds (AWS, Azure, GCP) while CloudFormation is AWS-only. Terraform also has a larger ecosystem and more readable syntax.
What if I accidentally delete state?
This is catastrophic. Always use remote state with versioning enabled. You can import existing resources with terraform import, but it's painful.
Need Help with Infrastructure as Code?
CloudElevate has implemented Terraform for dozens of organizations. We help teams establish IaC best practices, create reusable modules, and set up CI/CD for infrastructure.
Contact us at info@cloudelevate.ai to modernize your infrastructure.
Tagged with
Ready to elevate your cloud infrastructure?
Get a free consultation with our DevOps experts.