Skip to main content
RunBook Academy

AnsibleV · Inventory Design at Fleet ScaleFleet taxonomy

Where the truth about the fleet lives

Advanced⏱ ~18 minansible-core

What you'll learn

  • Choose an authoritative source for fleet membership and state the consequences of the choice
  • Distinguish a shadow host from a ghost inventory entry and explain the risk of each
  • Build a scheduled reconciliation that reports both directions of divergence
  • Record the source-of-truth decision where the next operator will find it

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

Not yet marked complete on this device.

Underneath every question this part has asked — what groups exist, who owns them, what all reaches — sits a question nobody writes down:

When the inventory file and reality disagree, which one is wrong?

Most estates have never answered it explicitly, which means they have answered it implicitly and differently in different situations. This lesson is about making the choice on purpose. It is deliberately about ownership rather than implementation; inventory plugins, caching and staleness are the subject of a later part.

Three candidate authorities

The Git inventory is authoritative

The inventory files in the repository define the fleet. A machine that exists but is not in the inventory is, by policy, not a managed machine — and probably should not exist.

What you gain: every membership change is a commit, with an author, a reviewer and a date. The whole apparatus of code review applies to “which machines are in production”, which is a remarkably strong control and costs nothing extra once the repository exists.

What you pay: freshness. A host provisioned at 14:00 is not in scope until somebody opens a pull request, and under pressure that step gets skipped. Estates that provision frequently find this model fights them.

Where it fits: physical fleets, long-lived VMs, anything where machines are created deliberately and rarely.

The CMDB or asset system is authoritative

An external system of record holds the fleet, and Ansible reads from it.

What you gain: one answer shared with everyone else who needs it — finance, security, capacity planning, the audit. The inventory stops being an Ansible-specific artefact.

What you pay: the review boundary moves out of Git and into the CMDB’s change process, which is frequently weaker — often a web form with no approval step. You have not removed the need for review; you have relocated it somewhere you may not control. And many CMDBs are maintained by hand, which makes them a second thing that drifts rather than a source of truth.

Where it fits: organisations that already have a CMDB people genuinely maintain, and a regulatory reason to have one.

The infrastructure provider is authoritative

The cloud API, the hypervisor, or the virtualisation platform is queried directly, and group membership derives from tags or attributes.

What you gain: accuracy about existence. Nothing is ever missing, because the thing that creates machines is the thing being asked.

What you pay: two things, and the second is easy to miss. First, a host enters scope the moment it is created — before anyone has reviewed whether it should be automated. Second, group membership now depends on tags, so a tagging mistake is a targeting mistake. Someone who mistypes an environment tag has moved a machine between environments with no commit, no review and no diff.

Where it fits: elastic estates where the machine population genuinely changes hourly and a Git-based inventory would be permanently wrong.

The two failure states

Whatever you choose, exactly two things can go wrong, and they are opposites.

The shadow host

A machine running in production that no inventory knows about.

It is not patched, because the patching play never targets it. It is not monitored, if monitoring is deployed by the same automation. It is not backed up. It does not appear in the audit, so its unpatched OpenSSL is not in the vulnerability report either. It works perfectly, quietly, until it does not.

Shadow hosts come from ordinary events: a machine provisioned by hand during an incident, a proof-of-concept that became load-bearing, a host rebuilt under a new name where the old name was never removed and the new one never added.

The ghost entry

An inventory entry for a machine that was decommissioned last quarter.

Its direct harm is small — it is unreachable in every run. Its indirect harm is large, and it is the reason this failure gets its own name: a fleet-wide run that always reports two unreachable hosts trains everybody to skip the unreachable count. When a real host goes down, the recap says three instead of two, and nobody notices the difference.

Reconciliation is the deliverable

Choosing an authority does not keep the inventory correct. A scheduled comparison does, and it must report both directions, because the two failure states are found by opposite comparisons.

Get the inventory’s view as a sorted host list:

Read-only / Safewhat automation believes exists
ansible -i inventory/production all --list-hosts \
| tail -n +2 | tr -d ' ' | sort > /tmp/from-inventory.txt

Get the authority’s view the same way — a cloud CLI, a hypervisor API query, a CMDB export — into /tmp/from-authority.txt, then compare:

Read-only / Safeboth directions, separately
$ comm -23 /tmp/from-inventory.txt /tmp/from-authority.txt
web-a4.example.com

That is a ghost: the inventory has it, the authority does not.

Read-only / Safethe direction that matters more
$ comm -13 /tmp/from-inventory.txt /tmp/from-authority.txt
web-a5.example.com

That is a shadow host: it exists, and no automation touches it.

Both lists should normally be empty. When they are not, the report should go to the team that owns the affected service — which is available, because lesson 3 of this part put service_owner_team in the group.

Writing the decision down

The decision is worth about ten lines, at the root of the inventory, where the next operator will meet it before they meet the consequences:

# inventory/SOURCE-OF-TRUTH.md

Authoritative for host existence:
  - Cloud instances (eu-west, us-east): the cloud provider API.
      Group membership derives from the `environment` and `role` tags.
      A tag change IS an inventory change and is not reviewed. Tag edit
      permission is restricted to the platform team for this reason.
  - Physical hosts and appliances: this repository. Adding a machine is
      a pull request; there is no other path.

Reconciliation:
  - Job: ci/reconcile-inventory, daily 06:00 UTC.
  - Reports both directions to the owning team named in group_vars.
  - A non-empty result is a ticket, not an email.

Owner: platform-infra
Last reviewed: 2026-08-11

The specific content matters less than the fact that somebody had to write “a tag change is an inventory change and is not reviewed” and read it back. That sentence tends to prompt the follow-up question about who can edit tags, which is the control the model actually needs.

Knowledge check

Knowledge check · 4 questions

  1. Q1. What is the specific cost of making the cloud provider API authoritative for group membership?

  2. Q2. Why is a ghost inventory entry - a host decommissioned months ago but still listed - worth fixing, given that it is merely unreachable? Select all that apply.

  3. Q3. Making Git authoritative for the inventory means a machine provisioned this afternoon is under automation as soon as it boots.

  4. Q4. A dynamic inventory resolves to 40 hosts instead of the usual 400 because the provider API is degraded, and the play proceeds. Why is that worse than the run failing outright?

Passing score: 75%. Answers are checked in this browser.