Why Infrastructure as Code?
Before Infrastructure as Code, provisioning a server meant clicking through a cloud console, SSHing into a box, and running commands you hoped you would remember next time. That approach works once. It does not survive a team, a second environment, or a 3 a.m. outage. Infrastructure as Code (IaC) replaces those manual steps with declarative configuration files that you commit, review, and apply like any other source code, and Terraform (with its open-source fork OpenTofu) is the tool most teams reach for to do it.
The problem with click-ops
Manual infrastructure management, often called “click-ops,” fails in predictable ways:
- Snowflake servers. Two machines that were supposed to be identical drift apart because someone tweaked one by hand. Nobody knows exactly what is on either.
- No record of why. A security group rule exists, but the reason is buried in a Slack thread from eighteen months ago, if it was ever written down at all.
- Unrepeatable environments. Staging looks nothing like production, so bugs slip through and “works on staging” becomes meaningless.
- Slow, error-prone recovery. When a region goes down or a resource is deleted, rebuilding it by memory takes hours and reintroduces mistakes.
IaC attacks every one of these directly.
Infrastructure you can version and review
With IaC, your infrastructure lives in plain text. That single fact unlocks the entire software-engineering toolchain. You can diff it, branch it, and open a pull request for a firewall change exactly as you would for an application change.
resource "aws_security_group" "web" {
name = "web-sg"
description = "Allow HTTPS inbound from the internet"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
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"]
}
tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
A reviewer sees the exact ports being opened and can comment before anything reaches the cloud. The Git history becomes an audit log: who changed the rule, when, and in which commit.
Repeatable, reproducible environments
Because the configuration is declarative, applying it produces the same result every time. Spinning up an identical staging environment is a matter of pointing the same code at a different workspace or variable file, not a day of careful clicking.
terraform plan -var-file=staging.tfvars
terraform apply -var-file=staging.tfvars
Terraform shows you exactly what it will do before it touches anything:
Output:
Terraform will perform the following actions:
# aws_security_group.web will be created
+ resource "aws_security_group" "web" {
+ name = "web-sg"
+ description = "Allow HTTPS inbound from the internet"
+ vpc_id = "vpc-0a1b2c3d4e5f"
}
Plan: 1 to add, 0 to change, 0 to destroy.
Tip: The
planstep is one of IaC’s biggest wins over click-ops. You get a dry-run preview of every create, update, and destroy before committing, so surprises happen on screen instead of in production.
Automation and faster recovery
IaC code runs in CI/CD pipelines, so infrastructure changes ship through the same automated, gated process as your application. No human needs to be online to provision a new queue or scale a cluster.
When disaster strikes, recovery is terraform apply against your config rather than a frantic rebuild from memory. The code is the runbook, and it is always up to date because it is what actually built the system.
What IaC gives you, at a glance
| Concern | Click-ops (manual) | Infrastructure as Code |
|---|---|---|
| Repeatability | Best effort, drifts over time | Deterministic, identical every run |
| Change review | Verbal, untracked | Pull requests + diffs |
| Audit trail | Cloud logs only | Full Git history with intent |
| Environment parity | Hard to maintain | Same code, different variables |
| Disaster recovery | Manual, slow | Re-apply config |
| Documentation | Separate, stale | The code itself |
Warning: IaC only delivers these benefits if the code is the single source of truth. Editing resources by hand in the console after Terraform created them causes drift, and the next
applymay revert or fight your manual change. Make a rule: if it is managed by Terraform, change it in Terraform.
An honest note on the learning curve
IaC is not free. You will learn a new language (HCL), a state model, and a plan/apply workflow, and the first project genuinely takes longer than clicking buttons would. State management, provider authentication, and module structure all have sharp edges early on. The payoff is real but deferred: the investment pays back the second time you need that environment, and every time after that. For a one-off throwaway sandbox, manual setup may be fine. For anything you will run more than once or share with a team, IaC wins quickly.
Best Practices
- Treat your IaC repository as the single source of truth and forbid manual changes to managed resources.
- Run
terraform planin CI on every pull request so reviewers see the impact before merge. - Store state remotely with locking (for example an S3 backend with DynamoDB) so teammates never clobber each other.
- Tag every resource with
ManagedBy = "terraform"and an environment so ownership is obvious in the console. - Keep environments in sync by sharing modules and varying only
.tfvars, not copy-pasted code. - Pin provider and Terraform/OpenTofu versions so applies are reproducible across machines and time.
- Start small: convert one stable, low-risk service first to build confidence before migrating critical infrastructure.