Why Terraform — Infrastructure as Code
Overview
Infrastructure as Code (IaC) means managing servers, networks, and databases the same way you manage application code — in version-controlled text files that can be reviewed, tested, and reproduced. Terraform is the leading IaC tool because of its declarative model, provider ecosystem, and state-based lifecycle management.
The problem with manual infrastructure
Without IaC, infrastructure management has well-known failure modes:
- Click-ops: Configuring resources through web consoles is slow, error-prone, and impossible to reproduce or audit.
- Snowflake servers: Manually tweaked servers diverge over time. No two environments are alike, so staging never matches production.
- Configuration drift: An emergency fix applied directly to a server is lost on the next deployment because it wasn't captured in code.
- No change history: Without version control, you can't see who changed what or roll back to a known-good state.
IaC solves these by making infrastructure repeatable, auditable, and automatable.
Why Terraform vs other tools
| Category | Tool | Terraform's advantage |
|---|---|---|
| Configuration management | Ansible, Chef, Puppet | Terraform focuses on provisioning infrastructure; config mgmt tools focus on configuring software. They complement each other. |
| Cloud-native templates | CloudFormation (AWS), ARM (Azure), Deployment Manager (GCP) | Terraform is multi-cloud — the same workflow works across AWS, Azure, GCP, and Kubernetes. |
| Scripting | Bash, Python with SDKs | Terraform is declarative: you describe the desired state, and Terraform figures out how to achieve it. Scripts require handling idempotency, error recovery, and state tracking manually. |
| Pulumi | Use familiar programming languages | Terraform uses HCL, a domain-specific language optimized for infrastructure. Pulumi uses general-purpose languages. |
Declarative vs imperative
Terraform is declarative: you write what the infrastructure should look like, not the step-by-step instructions for building it.
# Declarative: "I want a web server with these properties"
resource "aws_instance" "web" {
ami = "ami-0fb653ca2d3203ac1"
instance_type = "t2.micro"
tags = { Name = "web-server" }
}
Compare with an imperative script that manually calls aws ec2 run-instances, waits for the instance to start, assigns tags in a separate call, checks error codes, and must handle partial failures. Terraform handles idempotency automatically — run apply twice, and the second time produces no changes because the resource already exists.
Key Terraform concepts for beginners
| Concept | Description |
|---|---|
| Provider | Plugin that interfaces with a specific cloud API (AWS, Azure, GCP, Kubernetes, etc.). |
| Resource | An infrastructure object managed by Terraform — an EC2 instance, a database, a DNS record. |
| Data source | Read-only query to fetch information about existing infrastructure (e.g., "find the default VPC"). |
| Variable | Parameterizable input that makes configurations reusable across environments. |
| Output | Value exported from a configuration for use by other configurations or for display. |
| State | JSON file mapping configuration to real-world resources. |
| Module | Reusable, composable group of resources. |
Bootstrapping a web server
A common first pattern: launch an EC2 instance and run a web server at boot time:
resource "aws_instance" "web" {
ami = "ami-0fb653ca2d3203ac1"
instance_type = "t2.micro"
availability_zone = "us-east-2a"
user_data = <<-EOF
#!/bin/bash
apt-get update -y
apt-get install -y apache2
systemctl start apache2
systemctl enable apache2
echo "<h1>Served by Terraform</h1>" > /var/www/html/index.html
EOF
tags = {
Name = "terraform-web"
}
}
output "public_ip" {
value = aws_instance.web.public_ip
description = "Public IP address of the web server"
}
What this does:
user_dataruns a shell script when the instance first boots.- The script installs Apache, starts it, and writes a simple HTML page.
- The
outputblock displays the instance's public IP after creation. - Access
http://<public_ip>in a browser to see the result.
When user_data changes
By default, Terraform does not replace an EC2 instance when user_data changes — it updates in-place. To force replacement (which runs the bootstrap script on a fresh instance), add:
resource "aws_instance" "web" {
# ... other settings ...
user_data_replace_on_change = true
}
The IaC mindset
- Everything as code: If it's in the AWS console, move it into Terraform.
- Never touch production by hand: Click-ops in production create drift. Apply changes through Terraform.
- Version everything: Configuration, state (in a backend), and module versions.
- Environments must be disposable: You should be able to
terraform destroyandterraform applyto recreate any environment.
See also
- Terraform Getting Started — your first deployment
- Terraform Configuration Syntax — variables, outputs, data sources