AnsibleXLII · Ansible Beyond Linux ServersBeyond Linux servers
A decision table for mixed estates
What you'll learn
- Apply three questions that place any target on the ownership map
- Use a decision table covering the six target classes in a mixed estate
- State the specific cost of using Ansible where a purpose-built tool already fits
- Record an ownership map in the repository so the boundary survives staff turnover
- Recognise the estate-level failure that results from having no boundary at all
Prerequisites
Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11
The previous six lessons each drew one boundary. This one collects them into something you can put in a repository and review a change against, because a boundary that lives only in the heads of the two people who agreed it is not a boundary — it is a shared assumption, and shared assumptions do not survive a new hire, a reorganisation or an incident at 03:00.
Nothing here is new. It is the same reasoning, applied six times, in a form short enough to argue with.
Three questions place any target
1. Can it execute a module?
If yes, the standard model applies: the module is copied and run on the target, and Ansible’s whole toolkit works as taught. If no, execution inverts to the controller and everything from the network lesson follows — the connection plugin becomes the integration, facts come from somewhere else, and the controller becomes the resource constraint.
2. Who owns the fact that it exists?
If Ansible creates it, Ansible must also be the thing that retires it, and retirement is manual because there is no state record. If something else creates it, Ansible must not — and the seam is that the other tool’s outputs become Ansible’s inventory.
3. Is something else already reconciling it continuously?
A container runtime restarts containers. A hypervisor’s HA manager moves guests. A cluster scheduler places workloads. Where a continuous reconciler exists, Ansible acting on the same state produces a system whose behaviour depends on which one ran last.
Three questions, and the answers determine the row.
The table
| Target class | Can it run a module? | Who owns existence | Who reconciles continuously | Ansible’s role | Cost of forcing Ansible past the line |
|---|---|---|---|---|---|
| Linux and BSD hosts | Yes | Ansible, or the provisioner that built them | Nothing | Everything: packages, config, services, users, patching | None. This is the tool’s home |
| Container hosts | Yes, the host | Ansible for the host; the runtime for containers | The container runtime | Converge the host, deliver the validated stack definition, gate on health after applying | Two owners for container state; content written into containers vanishes on recreation; a variable edit becomes a fleet-wide restart |
| Hypervisors and guests | The hypervisor yes, the API no | The platform for placement and storage; whoever declares the guest | The HA manager, for placement | Configure the guests; API-side lifecycle only where deliberately chosen | Placement fights between the HA manager and a convergence run; guests that depend on the node they landed on stop being portable |
| Network devices | No | The device, physically | Nothing, but the device is stateful | Retrieve, diff, apply, verify — one device at a time, with out-of-band access as a precondition | Loss of the device rather than a broken service; a command-style interaction that is not idempotent and cannot be dry-run |
| Cloud and API resources | No | The provisioning tool if you have one, otherwise Ansible with manual retirement | The provider’s own control loops | Call the API from the controller, once, with run_once or a separate play; consume outputs as inventory | Resources that outlive their declaration; rate-limit incidents from the host loop; a blast radius set by the token, not the host list |
| Appliances and SaaS | No | The vendor | The vendor | Read state, integrate, open and close windows and tickets | An integration that breaks on every vendor API change, maintained by whoever wrote it |
Read the last column as the actual argument. Every row’s cost is a specific, observed failure, not a stylistic objection — and in every case the failure arrives quietly, weeks after the decision.
The costs, stated plainly
Container hosts. Managing container internals with Ansible produces state with no durable home. A file copied into a running container survives until the next recreation and then does not, on some hosts and not others, with a clean recap every time. The cost is a fleet that cannot be rebuilt from its repository.
Hypervisors. Encoding placement decisions in a playbook gives you two schedulers. The HA manager moves a guest during a hardware failure — doing exactly its job — and the next convergence run moves it back. Neither tool is wrong and the service is disrupted twice.
Network devices. This is the only row where the cost is losing the target. Everywhere else a bad change leaves you a working session to fix it from. Here the change can be the session.
Cloud and API resources. Two costs, and they arrive in the opposite order to expectation. First the rate-limit incident, when a fleet-wide call sitting inside the host loop is issued three hundred times. Later, and far more expensively, the resources nobody declared any more: found by a cost review or a security audit, not by a failing run.
Appliances and SaaS. The cost is maintenance rather than damage. Every bespoke integration is a small piece of software with one author, no tests, and a dependency on somebody else’s release schedule.
Write the map down, in the repository
The artefact this lesson exists to produce is a file, and its value is that a reviewer can hold a change against it.
<!-- docs/ownership-map.md -->
# Who owns what
| Resource class | Owner | Ansible does | Retired by |
|-------------------------|----------------|-----------------------------|--------------------|
| Linux guests | platform team | everything inside the guest | ticket + play |
| VM existence, placement | Proxmox | nothing | platform team |
| Container stacks | compose files | render + apply + health gate| removing the file |
| Container internals | the image | **nothing** | rebuilding the image |
| Firewall rules (edge) | network team | nothing | network team |
| DNS records | Ansible | create and update | `state: absent`, then delete the task |
| Monitoring silences | Ansible | open and close per run | automatically |
Exceptions, with dates and owners:
- 2026-06-02, jc: `validate_certs: false` against the lab cluster until its
certificate is replaced. Review 2026-09-01.
Four properties make that file useful rather than decorative:
- It names a retirement path for every row. The row with no answer is the row that leaks resources.
- It says “nothing” explicitly where Ansible’s role is to stay out. An empty cell reads as an oversight; the word does not.
- It carries dated exceptions with owners. The intentional exceptions lesson makes the general case: an undocumented deviation is indistinguishable from drift.
- It is short enough to be read in a review. A twelve-row table gets consulted. A forty-page standards document does not.
Knowledge check
Knowledge check · 4 questions
Q1. Which question does this lesson argue is decisive when placing a target on the ownership map, and why?
Q2. For which target class is the cost of overreach qualitatively different from all the others, and how?
Q3. Which properties make a written ownership map useful rather than decorative? Select all that apply.
Q4. Because a purpose-built tool exists for provisioning, using Ansible to build the controller and the CI runner is always the wrong choice.
Passing score: 75%. Answers are checked in this browser.