Infrastructure as code with Terraform

Declaring cloud resources (databases, servers, queues) as code instead of clicking through a cloud console. How Terraform, an infrastructure tool, keeps your infrastructure versioned like your application code.

Beginner

4 min read

Why "infrastructure as code" matters

A decade ago, deploying a new database meant logging into AWS Console, clicking through forms, and manually recording what you'd created somewhere (maybe a wiki, maybe a ticket, maybe just hope). Every change you made was another manual step; every mistake lived in your live system and in someone's memory of what they'd clicked. Infrastructure as code reverses this: you declare resources (a database, a server, a queue) as text files, check those files into git like your application code, and a tool like Terraform applies those declarations to your cloud provider.

This gives you:

  • History: every infrastructure change lives in git commits, with a reason ("Why did we increase this timeout?") and who made the change.
  • Reproducibility: the same .tf files apply the same way in staging and production, reducing "works on my machine" disasters.
  • Review: changes to production infrastructure go through a PR; catching a typo in a database name before applying it beats discovering it in an incident.
  • Disaster recovery: your infrastructure declaration is a complete, runnable backup — if something goes sideways, you can re-apply it to a fresh account and get back to the same state.

The basic shape: resources, variables, outputs

A Terraform file (with a .tf extension) contains three main kinds of declarations:

Resources — the actual things you want to create:

resource "aws_s3_bucket" "documents" {
  bucket = "my-app-documents-${var.environment}"
}

This declares an S3 bucket. The resource type (aws_s3_bucket) tells Terraform what provider (AWS) and what thing to create. The local name (documents) lets other parts of your .tf files refer to this bucket. The bucket argument specifies its actual name.

Variables — inputs you can change without editing the .tf file:

variable "environment" {
  type        = string
  description = "Deployment environment: dev, staging, or prod"
  default     = "dev"
}
 
variable "instance_count" {
  type = number
  default = 1
}

Variables let you parameterize your infrastructure — same code, different var.environment for dev vs. prod, and pass different values via command-line flags or files.

Outputs — values you want to know after Terraform applies:

output "bucket_name" {
  value = aws_s3_bucket.documents.id
}
 
output "database_url" {
  value = "postgresql://${aws_db_instance.main.endpoint}"
}

Once Terraform finishes creating resources, it prints (or saves to a file) the outputs — often the URLs or names your application code needs. Instead of clicking AWS Console to find the database hostname, Terraform gives it to you directly.

State: Terraform's memory of what it created

Terraform maintains a state file — a JSON record of every resource it created and its current properties. When you run terraform apply a second time, Terraform compares your .tf files against the state file: "Did I create this bucket before? Is it still there? Have the properties changed?" This is how it knows what to update, delete, or leave alone.

State files must be stored carefully — typically in S3 or a Terraform Cloud backend, not committed to git, because they can contain sensitive values (database passwords, API keys). A typical setup:

terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "prod/terraform.tfstate"
    region = "us-east-1"
  }
}

This tells Terraform to store state remotely in S3, not locally, so multiple people on a team apply changes against the same state file.

From declaration to reality: the workflow

  1. Write .tf files declaring the infrastructure you want.
  2. Review what Terraform will do: terraform plan shows additions, changes, and deletions without actually applying them.
  3. Apply: terraform apply creates/updates/deletes real resources in your cloud account to match the .tf files.
  4. Verify that the resources actually exist by checking outputs or your cloud console.

When something goes wrong (a typo in a resource name, a invalid configuration), terraform plan catches it before anything breaks in production.

Why this matters for a real system

Without infrastructure as code, your infrastructure is invisible — living only in the cloud console and the brains of whoever set it up. With Terraform, infrastructure is a first-class citizen alongside your application code, reviewed in PRs, versioned in git, and reproducible from a single directory. A new team member can run terraform apply and get an identical staging environment without ever clicking a console.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What is the primary advantage of storing Terraform state in S3 instead of locally?

2. In Terraform, what does 'terraform plan' do?

3. Why should you use variables in Terraform instead of hardcoding values?

4. What is a Terraform output, and when would you use one?