Anyone who has managed servers for a few years knows the scene: a system that works perfectly, configured by hand years ago by someone who has since left, and that nobody dares touch because nobody knows any more how it was built. If that server fails, rebuilding it becomes a work of archaeology. Infrastructure as code was born to avoid exactly this situation.
The idea
Infrastructure as Code (IaC) means describing servers, networks, databases, firewall rules, DNS records and permissions in text files, instead of creating them by hand from a web console or the command line. These files are read by a tool that creates or modifies the real resources to match the description.
The consequence is that infrastructure gains the same properties as software:
- it is versioned: every change has an author, a date and a reason in the repository history;
- it is reviewable: a colleague can check a change before it is applied;
- it is reproducible: the same code creates identical environments for development, testing and production;
- it is documented: the code itself describes how the system is built.
Declarative and imperative
IaC tools follow two different approaches.
| Approach | What you describe | Example tools |
|---|---|---|
| Declarative | The desired end state | Terraform, OpenTofu, CloudFormation, Kubernetes |
| Imperative | The steps to perform | Shell scripts, some uses of SDKs and Pulumi |
With the declarative approach you write "I want three virtual machines with these characteristics"; the tool works out what is missing and what needs changing. With the imperative one you write "create a machine, then another, then another". The first is more robust, because running it twice does not create six machines. This property, called idempotence, is the heart of IaC: applying the same code several times must always lead to the same result.
Configuration management tools, such as Ansible, sit in between: they describe a state, but through an ordered sequence of tasks.
The problem it solves: drift
When changes are made by hand, environments slowly diverge. Someone opens a port on the production firewall for a test and forgets to close it; someone else increases a server's memory but not the staging one's. After a few months, staging no longer represents production and tests lose their value.
This divergence is called configuration drift. With IaC the code is the source of truth: the tool can compare the actual state with the declared one and report every difference. Manual changes become visible instead of staying hidden.
Concrete benefits
- Disaster recovery: if an entire cloud region becomes unavailable, recreating the infrastructure elsewhere means running the code, not days of rebuilding from memory.
- Temporary environments: you can create a complete environment to try a change and destroy it a few hours later, paying only for that time.
- Review of risky changes: opening a port or changing a permission goes through a review request, just like application code.
- Shared knowledge: a new team member reads the repository and understands the architecture without having to ask whoever built it.
The limits to be aware of
IaC is not free. It requires learning a tool, keeping the code up to date and above all giving up manual changes. If half the team keeps clicking around the provider's console, the code stops representing reality and becomes more dangerous than useful.
Some resources lend themselves less well: data inside databases, content uploaded by users, keys generated only once. IaC describes the container, not the content, and does not replace backups.
Finally, a mistake in the code propagates as efficiently as a correct change. That is why declarative tools show a plan of the changes before applying them, and it is important to read it carefully.
How to introduce it without upheaval
You don't need to rewrite everything at once. A reasonable path:
- Choose a tool suited to your context and provider, preferably one others on the team already use.
- Start with new resources: everything you create from today goes through code.
- Gradually import the most important existing resources, starting with those that change most often, such as DNS and network rules.
- Put the code in a repository with mandatory review and, as soon as possible, apply it through a pipeline rather than from one person's computer.
- Restrict manual change permissions in the provider's console, so code remains the only way.
In my work the first resource I bring under code is almost always the DNS zone: it changes often, a mistake has immediate and visible effects, and having the history of every record proves useful very soon.
In short
- Infrastructure as code describes servers, networks and permissions in versioned files instead of manual changes.
- The declarative approach and idempotence let you apply the code repeatedly with the same result.
- The code becomes the source of truth and makes drift between environments visible.
- It only works if people stop changing resources by hand, and it does not replace data backups.
- It is best to start with new resources and gradually import existing ones.