AnsibleXLIII · Observability and Auditing of AutomationObservability and auditing
"Who changed three hundred servers?"
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
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.
| Source | Answers | Does not answer |
|---|---|---|
| Controller run artefact and log | which hosts were targeted and reached, which limit, which commit, which OS account ran it, exact timestamps | what actually changed on disk |
| Managed-node journald and auditd | which files changed, which services restarted, which commands ran, under which account | why, or which run it belonged to |
| Package transaction history | which packages moved from which version to which, and when | who 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:
WINDOW='2026-08-11 14:3'
grep -F "$WINDOW" /var/log/ansible/ansible.log | head -40The 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
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?
Q2. A reconstruction correlates a host timestamp with a controller run and names an operator. What is the most important weakness of that conclusion?
Q3. Which controls genuinely restore human attribution to an automated change? Select all that apply.
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.