Infrastructure as code (IaC) means you describe servers, networks, databases and their settings in files, and a tool creates them for you. You stop clicking through a cloud console or typing commands on a server. The files live in Git, so infrastructure changes get the same review, testing and history as application code. IaC is one of the core DevOps practices.
How it works#
You write definition files that describe the resources you want. Common tools are Terraform and OpenTofu (.tf files), AWS CloudFormation (YAML or JSON templates), and Pulumi (general-purpose languages, including Python). Configuration tools such as Ansible set up the software inside servers.
Most IaC tools are declarative. You describe the end state ("one Postgres database of this size"), not the steps. The tool compares that with what exists and works out the changes. Running it twice with no edits changes nothing; this property is called idempotence.
Here's what that looks like day to day. A typical Terraform workflow:
- Edit the
.tffiles on a branch. - Run
terraform planto see what will be created, changed or destroyed. - Open a pull request. Reviewers read the code and the plan.
- After merge, a pipeline runs
terraform applyto make the changes.
Because the definitions are code, you can apply software habits to them: version control, code review, automated tests, and CI pipelines that validate every change.
A small example#
To make the workflow concrete, here's a definition file. This Terraform file defines a managed PostgreSQL database on Amazon RDS. It contains no password. With manage_master_user_password, RDS creates the password and stores it in AWS Secrets Manager.
provider "aws" {
region = "us-east-1"
}
resource "aws_db_instance" "orders" {
identifier = "orders-db"
engine = "postgres"
instance_class = "db.t3.micro"
allocated_storage = 20
username = "app_admin"
# RDS generates the password and keeps it in Secrets Manager,
# so no secret appears in this file or in Git.
manage_master_user_password = true
# Take a final snapshot if this database is ever destroyed.
skip_final_snapshot = false
final_snapshot_identifier = "orders-db-final"
}
To change the instance size, you edit instance_class, review the plan in a pull request, and apply it. Git then records who changed what and why.
Never write a real password into a .tf file. Use a managed secret like the one above, or a variable marked sensitive = true that a secrets manager fills in. Terraform state (the file where Terraform records what it has created) can also hold secret values, so store state remotely, encrypted, with tight access.
Goals of IaC#
With the mechanics covered, step back to why teams adopt IaC. It isn't only about speed. Its goals describe how a team should feel about changing infrastructure.
- Change is easy. Infrastructure supports change instead of blocking it.
- Change is routine. Updates are small, frequent and boring, not stressful events.
- People do less repetitive work. Engineers build tools and improve systems instead of repeating manual steps.
- Users serve themselves. Teams create the resources they need from approved definitions, without a ticket queue.
- Recovery is fast. You plan to rebuild quickly after a failure, instead of trying to prevent every failure.
- Improvement is constant. Many small changes beat one big, risky change.
- Solutions are proven by running them. You test an idea by applying it in a test environment, not by long debate.
The problems IaC fixes#
Those goals are a response to real problems. Cloud servers are cheap and quick to create. That's a strength, but without automation it causes three problems.
Server sprawl#
Server sprawl is when a team creates servers faster than it can look after them. Patches reach some servers and not others. Software versions differ. Nobody is sure what each server is for.
Configuration drift#
Configuration drift is when servers that started out identical slowly become different. Common causes:
- Someone logs in and tunes one server by hand to fix an urgent problem.
- One application gets an upgrade and the others don't.
- Different people install the same software in different ways.
Drift makes behavior inconsistent. A bug appears on one server only, and automation that assumes identical servers starts to fail.
Snowflake servers#
A snowflake server is one whose setup is unique and was done by hand, so nobody can rebuild it with confidence. Teams become afraid to touch it. It's hard to patch, hard to scale, and a disaster if it dies.
The IaC fix for all three is the same. Define every server from code, apply changes only through code, and rebuild servers instead of editing them in place.
Principles of IaC#
The fix above rests on four principles that guide how you build infrastructure with code.
| Principle | Meaning | What it gives you |
|---|---|---|
| Reproducible | You can rebuild any part with no manual decisions | Less fear of change; fast recovery |
| Disposable | Any resource can be destroyed and replaced at any time | Systems that survive failures and resizing |
| Consistent | Servers with the same role are the same, apart from things like IP addresses | Automation you can trust; no drift |
| Repeatable | Every action on infrastructure can be run again the same way | Reliable processes; no hand-made fixes |
Reproducible means every choice (software versions, hostnames, settings) lives in the code, not in someone's head. Disposable means your software must keep working while servers appear, disappear and change size. This matters in the cloud, where you can't count on any one machine. Consistent servers let one script work on all of them. Repeatable leads to a simple rule: if a task can be scripted, script it. If it's hard to script, look for a tool or a different approach before you fall back to doing it by hand.
Design keeps changing#
These principles also change how you approach design. In a data center, changing infrastructure was slow and costly, so teams tried to design everything up front. Those designs were complex and still failed to predict the future. In the cloud, change is cheap. So keep the design simple, build for today's needs, and change it often. Frequent small changes push you to keep good tooling and simple designs.
Stacks and environments#
Once you define infrastructure in code, you have to decide how to split it into files and runs. IaC tools make you group resources explicitly, and two terms describe these groups.
A stack is a set of infrastructure resources that you define and change as one unit. Size doesn't decide what a stack is. It can be one server, an app with its servers, network and storage, or a whole data center. What matters is that one definition creates and updates it together. Don't copy old habits blindly, such as one giant stack or stacks that follow network boundaries. Group resources by what changes together.
An environment is one instance of a service's stacks, such as development, test, staging or production. All environments should come from the same definitions, with a few parameters such as size changed. Keeping environments the same by hand is very hard. With IaC, it's the default. See deployment strategies for how code moves through environments.
What goes in version control#
Everything so far assumes your definitions live in Git. Version control is the center of IaC. It gives you a history of every change, easy rollback, links between related changes, visibility for the whole team, and triggers for automated tests.
The test for what to store: if all your infrastructure vanished, could you check out the repository, run a few commands and rebuild it? Restoring data from backups is the only extra step allowed.
| Store in Git | Keep out of Git |
|---|---|
Infrastructure definitions (.tf files, templates) |
Passwords, keys and other secrets |
| Configuration files and templates | Build artifacts you can rebuild from source |
| Configuration management code (playbooks, manifests) | Application data and log files |
| Tests, and CI and CD pipeline definitions | Packages and images (use an artifact registry) |
| Utility scripts and their source code | Terraform state files (use a remote backend) |
| Documentation |
Secrets belong in a secrets manager, such as AWS Secrets Manager or HashiCorp Vault. Built artifacts belong in a package or image registry. Back up that registry, or keep scripts in Git that can rebuild it. Store application data and logs in systems built for them, with their own backups.
Key takeaways#
- IaC defines infrastructure in files that a tool applies. Changes go through Git, review and CI like application code.
- Declarative tools such as Terraform compare the desired state with reality, and applying twice changes nothing.
- Manual changes cause drift and snowflake servers. The fix is to change infrastructure only through code and rebuild instead of patching in place.
- The core principles: reproducible, disposable, consistent and repeatable.
- A stack is what you change as one unit. Environments are copies of the same stacks from the same code.
- Everything needed to rebuild goes in Git, except secrets, data, artifacts and state.