bigonotes
Engineering PracticesDevOps and IaC

Infrastructure as Code

Why you define infrastructure in code, how drift and snowflake servers happen, the core IaC principles, stacks, and what goes in Git.

7 min readLast updated

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:

  1. Edit the .tf files on a branch.
  2. Run terraform plan to see what will be created, changed or destroyed.
  3. Open a pull request. Reviewers read the code and the plan.
  4. After merge, a pipeline runs terraform apply to 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.
Revision notes · 2 min

Core idea#

Infrastructure as code (IaC) defines servers, networks and databases in files that a tool applies. Changes go through Git, review and CI like application code.

Key points#

  • Tools: Terraform and OpenTofu, CloudFormation, Pulumi. Ansible configures software inside servers.
  • Declarative: you describe the end state, and the tool works out the changes.
  • Idempotent: applying twice with no edits changes nothing.
  • Workflow: edit, terraform plan, pull request review, pipeline runs terraform apply.
  • Never put passwords in .tf files. Use a managed secret or a sensitive variable.
  • Terraform state can hold secrets. Store it remotely, encrypted, with tight access.

Problems IaC fixes#

  • Server sprawl: more servers than the team can patch and track.
  • Configuration drift: identical servers diverge through manual fixes and uneven upgrades.
  • Snowflake server: unique, hand-built, and nobody can rebuild it with confidence.
  • Fix: change infrastructure only through code, and rebuild instead of editing in place.

Principles#

Principle Meaning
Reproducible Rebuild any part with no manual decisions
Disposable Destroy and replace any resource at any time
Consistent Same role, same setup, apart from things like IPs
Repeatable Every action can run again the same way
  • If a task can be scripted, script it.
  • Keep designs simple and change them often. Cloud change is cheap.

Stacks and environments#

  • Stack: resources defined and changed as one unit. Size does not matter.
  • Environment: one instance of the stacks, such as dev, staging or prod, from the same code.

Version control#

Store in Git Keep out of Git
Definitions, config files, templates Secrets
Playbooks, tests, pipeline definitions Rebuildable artifacts, packages, images
Scripts, documentation Application data, logs, state files
  • Test: could you rebuild everything from the repository plus data backups?

Pitfalls#

  • Hand-editing a server "just this once" starts drift.
  • One giant stack, or stacks copied from old network boundaries. Group by what changes together.

Say it in an interview#

  • "Every change goes through Git and terraform plan, so infrastructure is reviewed like code."
  • "We rebuild servers from code instead of patching them, so there is no drift."

Found a mistake? Email ask@bigonotes.com

Further reading#