Skip to main content
RunBook Academy

AnsibleXLIII · Observability and Auditing of AutomationObservability and auditing

"Who changed three hundred servers?"

Advanced⏱ ~28 minansible-corejournalctlausearchgit

What you'll learn

  • Decompose the question into who, what, when, which hosts and which version
  • Join controller evidence to managed-node evidence on timestamp, account and host
  • Explain why the managed node can never name the human on its own
  • Apply the controls that restore human attribution to an automated change
  • Recognise the evidence that atomic file replacement leaves on the target

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.

Somebody senior asks the question in the title. It sounds like one question and it is five, and the reason it is hard is that no single system holds more than two of the answers.

  • What changed — the managed node knows, in detail.
  • When — both ends know, to the millisecond, if their clocks agree.
  • Which hosts — only the controller knows, and only if the run recorded its own target set.
  • Which version of the automation — only the repository knows, and only if the run recorded its commit.
  • Who — nobody knows, unless you arranged for somebody to.

This lesson is about assembling those five from evidence that exists in three places, and about the last one, which is the only genuinely hard part.

The evidence chain

Three sources, joined on two keys.

SourceAnswersDoes not answer
Controller run artefact and logwhich hosts were targeted and reached, which limit, which commit, which OS account ran it, exact timestampswhat actually changed on disk
Managed-node journald and auditdwhich files changed, which services restarted, which commands ran, under which accountwhy, or which run it belonged to
Package transaction historywhich packages moved from which version to which, and whenwho asked for it

The joins are timestamp and the automation account, per host. Both are fragile in ways worth knowing before you need them.

The Linux course teaches the managed-node half properly and this lesson does not repeat it: auditd architecture, audit record anatomy and attribution, ausearch and aureport, journalctl filters and authentication and sudo logs. If you are going to rely on that half, read those first — the controls described there are what make the join possible at all, and central logging design is what stops the evidence living only on a host that may be the one you just broke.

Working the chain, in order

Start at the host, because that is where the symptom is, and move outward.

1. What changed, and when. On the affected host, the service journal around the reported time, then the audit trail for the file. This is ordinary Linux forensics and the Linux course covers the syntax.

2. Which account did it. It will be your automation account. That is the expected answer and it is also the dead end — see the next section.

3. Take the timestamp to the controller. With the controller log enabled, the window is searchable:

Read-only / Safefind the run that was in flight at that moment
WINDOW='2026-08-11 14:3'
grep -F "$WINDOW" /var/log/ansible/ansible.log | head -40

The p= field groups the lines belonging to one run, and u= names the operating-system account that started it.

4. Take the run to the artefact. The run_id gives you the commit, the inventory, the limit and whether it was --check.

5. Take the commit to the repository. git show names the author, git log gives the message, and the merge request or review names the approver. This is where a human finally appears — and notice it is the human who wrote the change, not necessarily the human who ran it.

6. Reconcile the host list. Intended targets from the limit, minus the hosts that reported results, equals the hosts that were targeted and never reached. As lesson 1 established, those hosts are absent from the recap rather than listed with zeros, so this subtraction is the only way to find them.

Six steps, three systems, two joins. None of the systems can be dropped.

The attribution problem

Here is why the managed node cannot answer “who”, ever.

Ansible connects to every host as one account. That is the whole design — the least privilege for the automation account lesson argues for exactly one, tightly scoped. So the host’s audit record says the change was made by ansible, on every host, for every change, by every operator, forever.

The audit trail on a managed node is therefore complete about what and useless about who. Three hundred hosts, three hundred identical answers, and the person is not in any of them.

The human name exists in exactly one place at the moment of the run: the controller. And it exists there only if three things are true.

Controls that put a name back

Four, in increasing order of strength. Each survives a different attack on the evidence.

1. Individual accounts on the controller, plus SUDO_USER. If people log in as themselves and escalate to the automation account, u= records the escalated account and SUDO_USER in the environment records the human. Stamp it into the artefact, as lesson 4 does. Weak against someone who logs in directly as the automation account; strong against ordinary use.

2. Runs go through a system that authenticates the requester. CI, or a controller platform with job records — the platform object model lesson describes runs as first-class objects with an owner. The identity is established before the run starts, by a system whose job is identity. Strong, and it has the side effect of making ad-hoc runs the exception, which is desirable for other reasons.

3. Per-operator SSH keys for the automation account. The automation account’s authorized_keys contains one key per operator, each with a distinct comment. The managed node’s authentication log then records which key authenticated — so the host itself holds a per-operator record, not just the controller.

This is the strongest control in the list, because it is the only one where the evidence lives on the target rather than on the machine that made the change. Controller evidence can be lost with the controller; the host’s auth log cannot. Its cost is real: key distribution and rotation across the fleet, which the key-rotation lesson in Part XLVII treats as its own operation.

4. A change identifier carried into the target. The ticket or change id is stamped into the run artefact and into a managed header on every file the run renders — the pattern from template review and managed headers. Then the file on disk names the change that produced it, and the chain can be walked backwards from the host with no controller evidence at all.

None of these is expensive. What they have in common is that they must be in place before the question is asked; every one of them is a decision about how runs are made, not a query you can run afterwards.

Knowledge check

Knowledge check · 4 questions

  1. Q1. An auditor asks who changed a configuration file on 300 servers. The managed nodes have full auditd coverage and the records are intact. What will they show?

  2. Q2. A reconstruction correlates a host timestamp with a controller run and names an operator. What is the most important weakness of that conclusion?

  3. Q3. Which controls genuinely restore human attribution to an automated change? Select all that apply.

  4. Q4. An audit rule that watches a configuration file for write access may record very little when Ansible changes it, because copy and template replace the file with an atomic move rather than writing in place.

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