Terraform — Part 1 — What Is Terraform and Why Infrastructure as Code Matters
This opens a new series on Terraform for engineers who have never touched it, starting with the question underneath the whole tool: why describe infrastructure in text instead of just building it by hand?
Managing servers, networks, and cloud resources through versioned configuration files instead of manual clicks.
You describe the end state you want. Terraform works out the steps needed to get there.
A plugin that lets Terraform talk to a specific platform’s API — AWS, Azure, Proxmox, and hundreds more.
Introduction
You need three web servers, a load balancer, and a database, all wired together with the right networking and security rules. How do you build them? More importantly, six months from now when someone asks exactly what was built and why, how do they find out?
If the answer is “log into the console and look”, you already have the problem this series exists to solve. This is the first post in a new beginner series on Terraform, HashiCorp’s infrastructure as code tool. It assumes no prior Terraform knowledge. Before touching a single command, this post covers what problem Terraform actually solves and why that problem is worth solving in the first place — the later parts build the hands-on skills on top of that foundation.
What Is Infrastructure as Code?
Infrastructure as Code, usually shortened to IaC, means defining servers, networks, storage, and other infrastructure in configuration files rather than creating them by clicking through a console or running one-off commands by hand.
Those configuration files can be stored in version control, reviewed before they are applied, reused across environments, and read months later to see exactly what exists and why. The infrastructure becomes something you can diff, not something you have to remember.
Declarative vs Imperative: Describing the Destination, Not the Route
Automation tools generally fall into one of two styles: imperative and declarative.
An imperative tool is a set of step-by-step instructions: create this network, then create this subnet inside it, then launch this server inside that subnet, in that exact order. You are responsible for every step and for handling what happens if a step has already been done before.
A declarative tool works differently. You describe the end state you want, and the tool figures out the steps required to reach it — including which of those steps still need to happen and which already have.
Think of imperative automation like giving someone turn-by-turn directions: turn left, go two miles, turn right at the second light. Declarative automation is like giving someone a destination address instead: “end up here.” How they get there — which roads, in what order — is worked out for them, and if they already happen to be halfway there, they don’t repeat steps they’ve already completed.
Terraform’s configuration language, HCL (HashiCorp Configuration Language), is declarative. A configuration for a single virtual machine might look like this:
# Declarative: this describes the desired end state,
# not the individual API calls needed to reach it.
resource "aws_instance" "web" {
ami = "ami-0c101f26f147fa7fd"
instance_type = "t2.micro"
}
Nothing here says “call the EC2 API, then poll until the instance is running.” It says what the instance should look like. Terraform is responsible for turning that description into the correct sequence of API calls, and for working out what changes are actually needed if the instance already exists.
What Is Terraform?
Terraform is an infrastructure as code tool built by HashiCorp. It was created by Mitchell Hashimoto and released as open source in July 2014, written in Go, using HCL as its configuration language.
Terraform lets you define infrastructure across many different platforms — public clouds, private virtualisation platforms, DNS providers, monitoring tools, and more — using the same declarative language and the same command-line workflow, regardless of which platform the resource actually lives on.
How Terraform Works: Providers, Resources, and State
Three ideas carry most of Terraform’s design, and each one gets its own dedicated post later in this series. For now, the short version:
A plugin that lets Terraform speak a specific platform’s API. There are providers for AWS, Azure, Google Cloud, Proxmox, Kubernetes, and hundreds of others.
A single infrastructure object Terraform manages — a virtual machine, a DNS record, a security group, a storage bucket.
Terraform’s record of what it has already built, used to work out what has changed since the last run.
The day-to-day workflow built on top of these three ideas has three stages: write the configuration, plan to see exactly what Terraform intends to create, change, or destroy before anything happens, and apply to carry out that plan. Part 3 of this series walks through that workflow command by command; Part 6 covers state in depth, since it is the piece newcomers misunderstand most often.
Why Click-Ops Doesn’t Scale
Building infrastructure by hand through a web console works fine for a single test server. It stops working the moment more than one person touches the environment.
Manual changes leave no record of what was done or why. Two engineers building “the same” environment by hand inevitably produce two different ones — a slightly different security group rule here, a forgotten tag there. Nothing stops a change made directly in the console from silently drifting away from whatever documentation claims the environment looks like. And when something breaks at 2am, there is no diff to look at, only a console and a guess.
Terraform configuration fixes this by making the infrastructure’s definition a plain text artefact: it can be code-reviewed before it is applied, it lives in version control alongside the applications it supports, and running it twice from the same starting point produces the same result both times.
Terraform vs Ansible, CloudFormation, and Pulumi
Terraform is not the only infrastructure automation tool, and it is worth being clear about where it fits before diving deeper into it.
| Tool | Primarily For | Language | Cloud Scope |
|---|---|---|---|
| Terraform | Provisioning infrastructure (declarative) | HCL | Multi-cloud, hundreds of providers |
| Ansible | Configuring existing servers (procedural, push-based over SSH) | YAML | Any reachable host; can provision too |
| AWS CloudFormation | Provisioning infrastructure (declarative) | YAML or JSON | AWS only |
| Pulumi | Provisioning infrastructure (declarative under the hood) | General-purpose: TypeScript, Python, Go, C#, Java | Multi-cloud |
The distinction worth remembering: Terraform and CloudFormation and Pulumi are provisioning tools — they create the servers, networks, and databases themselves. Ansible is primarily a configuration management tool — it installs packages and manages settings on servers that already exist, though it can provision too. In many real environments these are not competitors; Terraform provisions the infrastructure, and Ansible configures what runs on top of it.
Where Terraform Fits in a Real Environment
Terraform configuration is code, so it behaves like code in the rest of the pipeline. It lives in a Git repository, gets reviewed through pull requests like an application change, and is applied through a CI/CD pipeline rather than run manually from someone’s laptop against production.
It also depends on the same guardrails as any other production system: credentials for cloud providers need to come from a secrets manager rather than being pasted into a config file, and the state file — which can contain sensitive values — needs to live somewhere access-controlled and shared, not on a single engineer’s disk. Both of those get their own dedicated coverage later in this series.
What This Series Will Cover
This is a foundations series, meaning the goal is a working, correct mental model before speed. The next several parts cover installing Terraform and running a first configuration, the full init/plan/apply/destroy workflow, HCL syntax, and variables and outputs — the mechanics needed to write and run real configuration.
After that, the series moves into the ideas that separate a toy configuration from a production one: state and remote state with locking, data sources, the meta-arguments that trip up nearly every newcomer (count, for_each, depends_on, lifecycle), modules for reusable infrastructure, and managing multiple environments cleanly.
It closes with a dedicated best-practices capstone and a troubleshooting reference covering the errors every newcomer eventually hits. Each part builds directly on the one before it, in the order listed in the sidebar.
Quick Reference Summary
| Term | Meaning |
|---|---|
| IaC | Infrastructure as Code — managing infrastructure through versioned configuration files. |
| Declarative | You describe the desired end state; the tool works out how to reach it. |
| Imperative | You specify the exact steps to run, in order, to reach a result. |
| HCL | HashiCorp Configuration Language — Terraform’s declarative configuration syntax. |
| Provider | A plugin that lets Terraform manage resources on a specific platform. |
| Resource | A single infrastructure object Terraform creates and manages. |
| State | Terraform’s record of what it has already built, used to plan future changes. |
| Plan | A preview of exactly what Terraform intends to create, change, or destroy. |
| Apply | Carrying out a plan against the real infrastructure. |
Final Thoughts
Terraform’s core idea is simple even though the tool has a lot of surface area: describe what you want, let the tool work out how to get there, and keep that description somewhere it can be reviewed, versioned, and trusted.
Everything in this series — providers, state, modules, workspaces, best practices — is really just detail sitting on top of that one idea. Once it feels obvious rather than clever, the rest of Terraform is mechanics.
Terraform — Part 2 — Installing Terraform and Running Your First Configuration. We will install the CLI, write a minimal configuration, and run it end to end for the first time.