Terraform is a declarative IaC tool by HashiCorp. We describe what infrastructure we want in files, and Terraform figures out how to create it, update it, or tear it down. It works with AWS, GCP, Azure, Cloudflare, and hundreds of other providers.
It uses its own language called HCL (HashiCorp Configuration Language). It looks a lot like JSON but is more human-friendly.
Core Concepts
Providers — plugins that let Terraform talk to a specific platform. The AWS provider knows how to create EC2 instances, S3 buckets, etc. We always declare which providers we need.
Resources — the actual infrastructure we define. An S3 bucket, a DNS record, a Kubernetes cluster — each is a resource.
Variables — inputs to our configuration. Think of them like function parameters.
Outputs — values we want to expose after Terraform runs (like an IP address or a URL).
Data sources — let us read information about existing infrastructure that Terraform doesn’t manage.
A Simple Example
Here’s a Terraform config that creates an S3 bucket:
# Tell Terraform we need the AWS provider
provider "aws" {
region = "ap-south-1"
}
# Define a variable so we can reuse this config
variable "bucket_name" {
description = "Name of the S3 bucket"
type = string
default = "my-app-assets-2024"
}
# Create the actual S3 bucket
resource "aws_s3_bucket" "assets" {
bucket = var.bucket_name
tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
# Output the bucket's ARN so other configs can reference it
output "bucket_arn" {
value = aws_s3_bucket.assets.arn
}
Every resource has a type (aws_s3_bucket) and a local name (assets). We reference it elsewhere as aws_s3_bucket.assets.
The Workflow
Terraform has a simple four-step workflow that we’ll use constantly:
# 1. Initialize — downloads provider plugins
terraform init
# 2. Plan — shows what will change (dry run, nothing is created yet)
terraform plan
# 3. Apply — actually creates/updates the infrastructure
terraform apply
# 4. Destroy — tears everything down (careful with this one!)
terraform destroy
terraform plan is our safety net. It shows exactly what will be created, changed, or destroyed before we commit. Always read the plan output before running apply.
Variable Types
Terraform supports several variable types:
variable "instance_count" {
type = number
default = 2
}
variable "enable_monitoring" {
type = bool
default = true
}
variable "allowed_ips" {
type = list(string)
default = ["10.0.0.0/8", "172.16.0.0/12"]
}
variable "tags" {
type = map(string)
default = {
team = "platform"
}
}
We can pass variable values via CLI flags (-var), .tfvars files, or environment variables (TF_VAR_bucket_name).
Data Sources
Sometimes we need info about resources that already exist — maybe a VPC that was created manually or by another team:
# Look up an existing VPC by tag
data "aws_vpc" "main" {
filter {
name = "tag:Name"
values = ["production-vpc"]
}
}
# Use it in a resource
resource "aws_subnet" "app" {
vpc_id = data.aws_vpc.main.id
cidr_block = "10.0.1.0/24"
}
The only difference between resource and data is that resource creates things and data reads things.
In simple language, Terraform lets us describe our cloud infrastructure in text files, preview changes before they happen, and apply them with confidence. It’s like having a blueprint for our entire cloud setup.