What is Infrastructure as Code, and why does Terraform's declarative model matter operationally compared to writing imperative provisioning scripts?
Quick Answer
Infrastructure as Code means defining servers, networks, and cloud resources in version-controlled configuration files instead of clicking through a console or running one-off scripts, so infrastructure changes can be reviewed, tested, and reproduced exactly. Terraform's declarative model means you describe the end state you want, and Terraform figures out the exact steps to get there — including safely modifying or destroying existing resources — rather than you writing and maintaining those steps yourself.
Detailed Answer
Think of IaC like the difference between giving someone a furnished blueprint versus a list of instructions like 'walk to the store, buy a couch, carry it up two flights of stairs, put it in the corner.' The instruction list breaks the moment anything changes — the store is closed, the stairs are blocked — because it describes actions, not the outcome. A blueprint just says 'a couch belongs in this corner,' and whoever executes it figures out how to make that true right now, whatever the current situation is. Imperative provisioning scripts are the instruction list; Terraform's declarative configuration is the blueprint.
Infrastructure as Code exists because manually clicking through a cloud console to create resources is unrepeatable and unreviewable — nobody can diff two consoles, and reproducing a production environment for staging means re-clicking through dozens of screens and hoping you remembered every setting. Terraform specifically chose a declarative model, as opposed to imperative tools like a bash script running aws ec2 run-instances, because declarative code stays correct even as the real world changes: if someone manually deletes a resource Terraform created, your configuration file doesn't need to change at all — Terraform detects the drift on the next plan and knows how to fix it.
Internally, Terraform maintains a state file that records what resources it created and their exact current attributes. When you run terraform plan, it queries your cloud provider's API for the real current state of each resource, compares that against both your desired configuration and its recorded state file, and computes a diff — additions, changes, and deletions — without touching anything. Only terraform apply actually executes that diff, calling the cloud provider's API to create, modify, or destroy resources in dependency order (Terraform builds a dependency graph from resource references, so a subnet is always created before the EC2 instance that needs it).
In production, this state file becomes the single source of truth for what Terraform believes exists, which is why teams store it remotely (in S3 with DynamoDB locking, or Terraform Cloud) rather than on a laptop — two engineers running apply against the same infrastructure with different local state files is one of the most common ways teams accidentally destroy production resources. Terraform plans are also what make infrastructure changes reviewable in a pull request: reviewers read the plan's output (2 to add, 1 to change, 0 to destroy) the same way they'd read a code diff, before anything is actually applied.
The gotcha most engineers hit early: because Terraform is declarative, deleting a resource block from your configuration doesn't just stop managing it — it tells Terraform that resource should no longer exist, and the next apply will destroy it. Engineers coming from imperative scripting instinctively expect 'removing code' to be a no-op, but in Terraform, removing a resource block is an explicit instruction to tear that resource down.