Terraform: Infrastructure as Code for Beginners
Managing cloud infrastructure manually through web consoles is slow, error-prone, and impossible to reproduce. Terraform by HashiCorp solves this by letting you define your entire infrastructure in code — servers, databases, DNS records, load balancers, and more — then create and manage it all with a single command.
This guide covers Terraform fundamentals: installation, HCL syntax, providers, resources, state management, and practical examples you can run today.
What is Terraform?
Terraform is an open-source Infrastructure as Code (IaC) tool. You write configuration files that describe the infrastructure you want, and Terraform figures out how to create it. If you change the configuration, Terraform calculates what needs to change and applies only the differences.
Key benefits:
- Declarative: You describe what you want, not how to build it
- Provider-agnostic: Works with AWS, GCP, Azure, Cloudflare, DigitalOcean, and 3000+ other services
- State tracking: Terraform keeps track of what it’s managing so it can update or destroy resources safely
- Plan before apply: Always preview what changes will be made before executing them
- Reproducible: The same configuration creates identical infrastructure every time
Official site: https://www.terraform.io
Installing Terraform
macOS (Homebrew)
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
Linux (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
Windows
Download from https://developer.hashicorp.com/terraform/downloads or use Chocolatey:
choco install terraform
Verify installation:
terraform --version
HCL: HashiCorp Configuration Language
Terraform uses .tf files written in HCL. The syntax is straightforward:
# This is a comment
resource "type" "name" {
argument1 = "value1"
argument2 = 42
nested_block {
key = "value"
}
}
Core Concepts
- Provider: A plugin that connects Terraform to a cloud service (AWS, GCP, etc.)
- Resource: A piece of infrastructure (server, database, DNS record)
- Data Source: Read-only information fetched from a provider
- Variable: Input parameters for your configuration
- Output: Values displayed after applying
- Module: A reusable group of resources
Your First Terraform Configuration
Let’s create a simple example that provisions an AWS EC2 instance.
Step 1: Create a Project Directory
mkdir terraform-demo && cd terraform-demo
Step 2: Define the Provider
Create main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
required_version = ">= 1.0"
}
provider "aws" {
region = "us-east-1"
}
Step 3: Define a Resource
Add to main.tf:
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "WebServer"
Environment = "dev"
}
}
Step 4: Initialize, Plan, Apply
# Download the AWS provider plugin
terraform init
# Preview what will be created
terraform plan
# Create the infrastructure
terraform apply
Terraform will show you exactly what it plans to create and ask for confirmation. Type yes to proceed.
Step 5: Inspect and Destroy
# Show current state
terraform show
# List managed resources
terraform state list
# Destroy everything
terraform destroy
Variables and Outputs
Input Variables
Create variables.tf:
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "environment" {
description = "Deployment environment"
type = string
default = "dev"
}
variable "allowed_ports" {
description = "List of allowed ingress ports"
type = list(number)
default = [80, 443, 22]
}
Use variables in main.tf:
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.instance_type
tags = {
Name = "WebServer"
Environment = var.environment
}
}
Pass variables via:
# Command line
terraform apply -var="instance_type=t3.small"
# Variable file (terraform.tfvars)
echo 'instance_type = "t3.small"' > terraform.tfvars
terraform apply
# Environment variables
export TF_VAR_instance_type="t3.small"
terraform apply
Output Values
Create outputs.tf:
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web_server.id
}
output "public_ip" {
description = "Public IP address"
value = aws_instance.web_server.public_ip
}
After applying, outputs are displayed. You can also query them:
terraform output public_ip
State Management
Terraform tracks infrastructure in a state file (terraform.tfstate). This file maps your configuration to real resources.
Remote State (Recommended)
For team environments, store state remotely:
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
}
}
Other backends include Azure Blob Storage, Google Cloud Storage, and Terraform Cloud.
State Commands
# List all resources in state
terraform state list
# Show details of a resource
terraform state show aws_instance.web_server
# Remove a resource from state (without destroying it)
terraform state rm aws_instance.web_server
# Move a resource in state (after renaming)
terraform state mv aws_instance.old_name aws_instance.new_name
Practical Examples
Security Group + EC2 Instance
resource "aws_security_group" "web_sg" {
name = "web-server-sg"
description = "Allow HTTP and SSH"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["YOUR_IP/32"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.web_sg.id]
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl start nginx
EOF
tags = {
Name = "WebServer"
}
}
Cloudflare DNS Record
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}
resource "cloudflare_record" "www" {
zone_id = var.zone_id
name = "www"
value = "203.0.113.50"
type = "A"
proxied = true
}
DigitalOcean Droplet
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {
token = var.do_token
}
resource "digitalocean_droplet" "web" {
image = "ubuntu-22-04-x64"
name = "web-1"
region = "nyc3"
size = "s-1vcpu-1gb"
ssh_keys = [var.ssh_fingerprint]
}
Best Practices
1. Use a Consistent File Structure
project/
├── main.tf # Resources
├── variables.tf # Input variables
├── outputs.tf # Output values
├── providers.tf # Provider configuration
├── terraform.tfvars # Variable values (don't commit secrets)
└── .gitignore # Ignore state files
2. .gitignore for Terraform
# .gitignore
*.tfstate
*.tfstate.*
*.tfvars
.terraform/
.terraform.lock.hcl
crash.log
3. Use Modules for Reuse
module "web_server" {
source = "./modules/ec2"
instance_type = "t2.micro"
environment = "production"
}
4. Always Run Plan First
# Save plan to file
terraform plan -out=tfplan
# Apply the saved plan (no confirmation needed)
terraform apply tfplan
5. Use Workspaces for Environments
terraform workspace new staging
terraform workspace new production
terraform workspace select staging
terraform workspace list
Essential Terraform Commands
| Command | Purpose |
|---|---|
terraform init | Initialize and download providers |
terraform plan | Preview changes |
terraform apply | Create/update infrastructure |
terraform destroy | Delete all managed resources |
terraform fmt | Format configuration files |
terraform validate | Check syntax |
terraform show | Display current state |
terraform output | Show output values |
terraform import | Import existing infrastructure |
Resources
- Documentation: https://developer.hashicorp.com/terraform/docs
- Registry (Providers & Modules): https://registry.terraform.io
- Tutorials: https://developer.hashicorp.com/terraform/tutorials
- GitHub: https://github.com/hashicorp/terraform
Terraform transforms infrastructure management from a manual, error-prone process into a repeatable, version-controlled workflow. Start with a simple resource, get comfortable with the init-plan-apply cycle, and gradually build more complex configurations.