How infrastructure actually gets built
You click through a provider’s console at eleven at night. A database, a firewall rule, an environment variable, a cron job, a DNS record, a queue, a bucket with the public flag turned off. It works, you go to bed, and the site runs for a year.
Then one of four things happens. You need a second environment and cannot remember what the first one has. Somebody deletes something and nobody knows what it was for. The provider has an outage in that region and you would like the same thing somewhere else. Or you leave, and the person after you inherits a machine nobody can describe.
The clicks were the design. They were never written down.
What infrastructure as code actually buys
Writing the infrastructure as files in your repository gives you four things, and it is worth being precise about them because the marketing is vague:
- A diff. You can see that this change opens port 5432 to the world before it does.
- A recreate. The environment can be built again from nothing, which is also your disaster recovery plan.
- A record. Git history says who added the bucket, when, and in which pull request they explained why.
- A second environment for almost nothing. Staging becomes the same file with different variables.
What it does not buy is safety from mistakes. A typo in a Terraform file can destroy a database far faster than a typo in a console, because the console asks and the tool does not.
The tools, and the free paths
Terraform is the industry standard and the one job ads name. In 2023 its licence changed from open source to the Business Source Licence, which triggered a fork: OpenTofu, now under the Linux Foundation, MPL-licensed, and a drop-in replacement for most projects. Both are free to use for the work you are doing here; if you want a licence with no ambiguity, use OpenTofu and the same .tf files.
Pulumi lets you write the same thing in TypeScript or Python, which suits people who find a new declarative language annoying. Ansible is the neighbouring tool for configuring a machine you already have, rather than creating it. Docker Compose counts too: for a single box, one committed compose.yml is a real, honest version of this idea and might be all you need for years.
And the smallest version that still works: a shell script called provision.sh in the repository, with the exact commands, run against a fresh machine once to prove it does. This is not a lesser answer for a small project. It is the answer that gets written.
The state file, which is the part that bites
Terraform and OpenTofu keep a state file mapping your code to real resources. Three facts about it, all learned the hard way:
- It contains secrets. Database passwords end up in plain text. Never commit it to git.
- Two people applying at once will corrupt it. Use a remote backend with locking: an S3-compatible bucket plus DynamoDB, Terraform Cloud’s free tier, or a Postgres backend.
- Deleting a resource from the file means deleting it in the world. Read the plan. Every time.
That last point is why plan before apply is not a formality. The plan output names each resource and marks it as create, update, or destroy, and the ones that say replace are the dangerous ones, because replacing a database means a new empty database.
tofu plan -out=tfplan # read this output line by line
tofu apply tfplanWire plan into CI on every pull request so the diff is reviewed by a person before it exists.
Drift, and the honest compromise
Somebody will fix something in the console at three in the morning during an outage. That is correct behaviour: the site matters more than the process. What must not happen is that the change stays only in the console, so the next apply silently reverts it.
The habit is: fix it by hand, then write it into the file within a day, then run plan and confirm it reports no changes. terraform plan with an empty diff is the check that your code and your world still agree, and running it on a schedule turns drift into a notification rather than a surprise.
What to put under it first
Do not try to codify everything. Start with the parts that are hard to remember and expensive to lose: DNS records, firewall and security-group rules, the database and its backup settings, buckets and their access policies, and the environment variables’ names (not their values, which stay in a secret manager).
Leave the rest clicked for now, and write a one-line note in the repository saying so. A partially codified system with an honest map beats a fully codified system that nobody maintains.
Every lesson here is open. Your progress is recorded once you pass the Agents and Automation exam.
Check your understanding
During an outage a colleague opens the console and widens a firewall rule to restore service. The rule is not added to the Terraform files. What happens on the next apply, and why?
Pick the one you would defend. Your answer stays private.
Stuck on something?Ask Tri, your AI tutor
