Infrastructure as code has a presentation problem. It is usually explained by software companies, to software companies, through examples involving continuous deployment, container orchestration and dedicated platform teams. That makes a broadly useful operating discipline sound like a specialist engineering project.

The core idea is simpler: the configuration of your systems should exist as a written, versioned and reviewable definition, rather than as an accumulated history of changes made through an interface and never recorded. A manufacturer, clinic, law firm or school district can benefit from that idea without trying to operate like a software business. The useful question is not whether to adopt every infrastructure-as-code practice. It is which parts remove enough risk and repeated work to justify their upkeep.

The problem is configuration drift

Any environment administered by hand eventually accumulates what is politely called configuration drift and less politely called nobody knows why that is like that.

A firewall rule added during an incident in 2022 remains in place with no recorded purpose. Three servers built from the same plan become subtly different because two received a fix and one was missed. An application depends on a particular setting that was discovered by trial and error but documented nowhere. Staging slowly stops resembling production, so a successful test there proves less every month.

The environment then becomes something engineers must study before they can safely change it. Routine work starts with investigation. Recovery requires reconstructing knowledge that exists only in running systems. The person who has been around longest becomes the index to the whole estate. That concentration of knowledge is a business-continuity risk, not merely an IT inconvenience.

Our indicative rebuild figures show the direction of travel. A standard server rebuilt manually from memory has taken about eight hours; the same work from a written runbook, about three; a partially scripted build, about one and a half; and a defined, automated build, about 0.4 hours. These are figures from ALCO rebuild work rather than a universal benchmark, and the variance becomes larger as systems become more complex. The important result is not a faster typing speed. It is replacing uncertain reconstruction with a repeatable process.

Start by choosing a useful boundary

Do not begin with a promise to codify everything. Begin with one class of system whose failure or inconsistency matters: a standard Windows server, a Linux web host, a firewall policy, a cloud network, or a workstation baseline. Define what belongs inside the boundary and what does not. Data, for example, normally needs a backup and restore process; it should not be embedded in a server build. Vendor-managed appliances may expose too little configuration to automate safely.

Create an inventory before choosing a tool. For the selected system, record the operating-system version, packages and roles, services, local policy, network settings, firewall rules, scheduled tasks, certificates, external dependencies and the source of any secrets. Mark which settings are security controls and which are merely preferences. This exercise often produces value before a line of automation exists because it exposes contradictions and undocumented dependencies.

Then decide what must be identical, what may vary by site or environment, and what should remain manually approved. The definition should describe intent clearly enough that another engineer can review it. A thousand-line script that only its author understands is automation, but it is not a durable operating model.

Adopt the practice in order of return

Version control comes before automation. Put configuration files, scripts, network definitions and build documentation in a repository with a history, a diff, named reviewers and a reason for each change. That is a low-effort, high-return step. During an incident, the valuable question is usually not simply what the setting is; it is when it changed, who changed it and why.

Next, write the standard build. It should be precise enough for a competent engineer to execute without asking the original administrator what they meant. Write it before automating it. Ambiguous prose is cheaper to correct than ambiguous code, and the act of writing forces the team to resolve decisions that had previously lived in habit.

Automate provisioning after the definition is stable. New systems being identical by construction is usually worth more than the labor saved during installation. An automated build should set the expected versions and policy, verify its own outcome and be safe to run again. Idempotence matters: the second run should confirm or converge on the intended state, not duplicate users, append the same line twice or restart services unnecessarily.

Automated enforcement comes next, selectively. Enforce settings that carry security, compliance or recovery weight: logging, account policy, encryption, endpoint protection, backup agents, required services and firewall boundaries. Aesthetic or low-consequence preferences can stay documented. Continuous enforcement has a maintenance cost, and a non-software organization should spend that cost where drift would create a real exposure.

Full pipeline automation and automated infrastructure testing are valuable for environments that change frequently. If infrastructure changes monthly rather than daily, a sophisticated pipeline can cost more to maintain than it returns. Configuration in version control, a written standard build and automated provisioning are generally high-return outside software firms. Drift enforcement is medium effort and should be selective. Ephemeral rebuilt-not-patched systems are higher effort with low-to-medium return in this context. Full infrastructure CI/CD is also high effort and usually low return unless deployment frequency supports it.

A practical repository and change path

Keep the repository unsurprising. Separate reusable roles or modules from environment-specific values. Keep a short README that explains prerequisites, ownership, how to validate a change, how to apply it and how to recover from a failed run. Pin important tool and module versions so that next year’s run does not silently mean something different. Tag or release known production states.

Never place passwords, API keys, private certificates or recovery material directly in the repository. The definition can refer to a secret by name or path, while a password manager, secrets vault or protected platform store supplies the value at execution time. Check both current files and commit history when removing an exposed secret; deleting it in the latest revision does not remove it from history, and rotation is still required.

For systems that already exist, import carefully. Do not assume a newly written definition describes reality. Compare desired state with observed state and review the proposed change before enforcement. Some tools call this a plan, check mode or dry run. Whatever the name, the result must be understandable to the person approving it. A plan that proposes replacing a live resource should stop the change until its consequence is understood.

Use pull requests or an equivalent review even with a small team. The reviewer checks operational effect, not formatting: which systems will change, whether a restart occurs, whether access could be removed, what evidence will show success and how to reverse the change. Require a ticket or business reason in the change record. That history becomes unusually valuable six months later, when the original context is gone.

The cultural rule that prevents failure

The rule that makes the model work is that normal changes are made to the definition, not directly to the system. If an administrator fixes production at 2am and never reconciles the definition, service has been restored but drift has returned. Repeat that often enough and the repository becomes fiction. A fictional source of truth can be worse than none because people plan recovery around it.

There must be an emergency path. Direct intervention is sometimes the correct decision when service is down. Make it explicit: record the emergency change, create the follow-up before closing the incident, and reconcile or deliberately revert the difference within a defined window. Then run the definition again in a non-destructive validation mode. The requirement is not that humans never touch systems. It is that urgent knowledge cannot disappear into them.

Common failure modes

A program can fail while every script runs correctly. Watch for these patterns:

  • The team automates a bad or disputed build before agreeing on the standard. Automation then distributes inconsistency faster.
  • The repository contains code but not ownership, operating instructions or review. When its author leaves, it becomes another undocumented system.
  • Secrets are committed for convenience. Version control turns one exposed file into a durable credential incident.
  • Production is changed by hand while the definition is updated only when someone remembers. Drift becomes normal again.
  • Enforcement is too broad. A configuration agent repeatedly reverses a legitimate vendor or incident change, producing an outage loop.
  • A successful tool run is treated as proof of a successful service. Configuration converged, but the application cannot authenticate, resolve DNS or serve a real transaction.
  • Tooling becomes the project. Months go into selecting platforms while no standard build is written and no recovery time improves.

Operational checks that make it defensible

Test outcome, not merely execution. After a build, confirm expected services are listening, logging reaches its destination, monitoring checks in, backups complete, security agents report healthy, name resolution works and an application-level transaction succeeds. Run those tests after tool upgrades as well as after configuration changes.

Measure drift deliberately. On a suitable cadence, compare the defined state with the observed estate and assign every difference one of three outcomes: correct the system, update the definition because reality reflects an approved decision, or document an accepted exception with an owner and review date. An unowned exception is simply drift with better grammar.

Prove recovery. Build a fresh instance from the definition in an isolated environment, restore representative data, and record elapsed time and manual interventions. A backup test alone proves that data can be read. This exercise proves that the systems needed to use the data can be reconstructed. That distinction matters in ransomware recovery, where restoring four terabytes of files may be straightforward but rebuilding eleven servers and their undocumented interdependencies is where weeks disappear.

Review access to the repository, automation runner and secret store. Those systems can change an estate at scale and should have MFA, least-privilege roles, protected production branches, audit logs and a controlled route for administrative access. Infrastructure as code improves auditability only if the mechanism applying it is itself controlled.

What good looks like after a year

A new engineer can build a standard server without asking the person who built the first one. A production change appears as a dated diff with an author and reason. Staging and production come from the same definition, with their legitimate differences expressed as data rather than memory. Disaster recovery is a rebuild plus a data restore, not an archaeology exercise. No single person is the only reliable account of how a system is configured.

That is also why this is a security control. A defined environment can answer which systems carry a required setting with evidence, rather than by sampling and hoping. It supports consistent controls and gives an auditor something stronger than an assertion. It makes recovery repeatable and limits the knowledge an attacker, an accident or a departing employee can take away.

None of this requires becoming a software company. It requires treating configuration as an organizational asset: written somewhere other than the system it describes, reviewed when it changes, tested against reality and maintained in proportion to the risk it controls.