Illustration: Infrastructure as code on an estate you did not build The examples all begin with an empty account and build upward. That is not the situation most people are in. The situation is several hundred machines configured by hand over a decade by people who have mostly left, and the question is how to get from there to something declarative without a rebuild.

Do not try to import everything

The instinct is to bring the whole estate under management at once. It does not work, because the estate is inconsistent, and a declaration that describes inconsistency is not useful.

Also: importing existing resources into a state file is fiddly, and importing several hundred is a project that produces no value until it finishes.

Start with description, not control

Write code that describes what should be true, and run it in check mode only. No changes, just a report of where reality differs from the declaration.

That report is immediately valuable. It tells you how inconsistent the estate actually is, which is always worse than anyone expects, and it gives you a prioritised list of things to fix.

You can live in this mode for months, and it is a safe place to be while the team learns the tooling.

Then take one property at a time

Not one server at a time. One property across all servers.

Pick something narrow and safe: the NTP configuration, the SSH daemon settings, the monitoring agent’s presence, the syslog destination. Declare it, check the drift, fix the drift, then keep it enforced.

Each property you take control of is permanently solved across the whole estate. After a dozen of these, a meaningful fraction of each server’s configuration is managed, and you did it without ever rebuilding anything.

This is much more tractable than trying to fully describe one server, because fully describing one server means confronting everything unique about it, and the unique things are the hard ones.

Accept two tiers

You will end up with managed properties and unmanaged remainder, and that is fine. The remainder shrinks over time.

What matters is that the boundary is explicit. Write down which properties are under management, so that when something unexpected changes, people know whether to look in the repository or on the machine.

New things are born managed

Whatever you do with the existing estate, everything built from today is built by code. No exceptions, because exceptions are how you end up here again.

This is the rule that makes the problem finite. The unmanaged estate is now a fixed population that only shrinks.

The uncomfortable cases

The server nobody will let you touch. Describe it, do not enforce, and put the drift report in front of its owner monthly. Eventually something breaks and the report becomes interesting.

The application with manual configuration steps in a document. This is where the value is highest and the work is hardest. Converting a runbook to code is the single best thing you can do for a team’s resilience, and it takes longer than anyone estimates.

The thing that is genuinely unique. Some systems are one-offs and coding them saves nothing. Be honest about it, document them as deliberately unmanaged, and move on. A pragmatic boundary you can defend beats a purity target you will miss.

What success looks like after a year

Not a fully declarative estate. A team that can answer “is this setting correct everywhere” in one command, that builds new systems from code, and that has shrunk the unmanaged remainder to the genuinely awkward.

That is a large improvement on where you started, and it is achievable without a rebuild, a project code, or a consultant.