Terraform and Ansible without the sprawl

Infrastructure as code a small team can still understand a year later: modules, state hygiene, and the parts I leave manual.

January 19, 2026 · 3 min read · 670 words
.md

Infrastructure code becomes dangerous when it starts trying to be a platform before the team has platform problems.

Terraform and Ansible can stay useful for years if they have a small job. Terraform owns durable cloud shape. Ansible owns machine and service setup where an image or managed service is not the better answer. Everything else needs to justify itself.

The sprawl starts with good intentions: one module for reuse, one role for consistency, one helper script for convenience. Six months later, nobody can tell whether a value comes from a variable, a default, a workspace, a secret store, or the one manual thing everyone forgot.

Modules should hide repetition, not decisions#

A Terraform module is useful when the decision has already been made.

If every caller overrides half the inputs, the module is not an abstraction. It is a negotiation layer. I would rather have three boring resources in the root module than a clever shared module with twenty knobs and no clear owner.

The test I use is simple: can a new engineer open the environment file and understand what will exist after terraform apply? If not, the module is probably hiding the wrong thing.

module-boundary.tf
module "service_bucket" {
  source = "../modules/private-bucket"
 
  name        = "inference-artifacts"
  environment = var.environment
}

The module can standardize encryption, labels, lifecycle policy, and access patterns. It should not hide whether the bucket exists, who owns it, or why it is there.

State is a production dependency#

Treat state like a database, not a cache.

That means remote state, locking, least-privilege access, and a written recovery path. It also means small state files. One giant state for the whole company feels convenient until a harmless change to a dashboard service needs to refresh half the cloud.

Split by ownership and blast radius:

  • network and shared foundations;
  • data stores;
  • application services;
  • observability;
  • experiments and temporary environments.

That split keeps a small service deploy from becoming an infrastructure ceremony.

flowchart LR A[foundation state] --> B[data stores] A --> C[app services] C --> D[observability] C --> E[temporary envs] B -. separate owner .-> D

Ansible is for the parts images do not cover#

I still like Ansible when the job is explicit: bootstrap a host, configure a service, rotate a config, repair drift, or document an operational procedure as code. I do not like it as a hidden runtime where business logic slowly moves into YAML.

Good roles are idempotent, noisy in the right places, and boring to re-run:

role-rules.yml
- name: write service config
  ansible.builtin.template:
    src: service.env.j2
    dest: /etc/my-service/env
    mode: '0600'
  notify: restart service

The handler matters. The mode matters. The fact that secrets are not printed matters. Those details are the difference between automation and a script that makes people nervous.

Leave some things manual on purpose#

Not every rare operation deserves a pipeline. Some risky actions should stay manual with a runbook, a checklist, and a second pair of eyes.

The goal is not maximum automation. The goal is reliable operations. Automate the repetitive and error-prone paths. Keep the irreversible paths explicit until the team has enough reps to encode them safely.

That is how Terraform and Ansible stay useful: fewer knobs, smaller states, idempotent roles, and no pretending that code removes the need to think.

Was this useful?

React if it helped; comment if you have a concrete question, correction, or field note.

-

Discussion (0)

Practical notes, bug stories, and disagreement with receipts are welcome.

No comments yet. A useful first comment is usually a field note: what failed, what held, or what you would check before shipping this idea.
Start the discussion
Markdown is supported. Keep it concrete and useful.