Skip to main content
RunBook Academy

AnsibleXLIII · Observability and Auditing of AutomationObservability and auditing

Reporting outward without making it mandatory

Advanced⏱ ~25 minansible-coreansible-galaxy

What you'll learn

  • Evaluate an outward integration by the question it answers that the artefact does not
  • State the cost a notification callback imposes on every run
  • Keep a reporting integration off the critical path of a production change
  • Assess what leaves your controller when run data is sent to a third party
  • Recognise the coverage gap a platform job history does not close on its own

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.

Everything so far in this part stays on the controller: a log file, a structured artefact, some queries over them. That is deliberate. It is the baseline this course recommends, it costs nothing to operate, and it answers most of the questions an operator actually has.

This lesson is about the things you can add on top, and it is written as an evaluation rather than a recommendation, because each of them buys something real and charges for it in a way that is easy to miss until the charge arrives.

One question decides most of them:

What does this integration answer that the run artefact does not?

If the answer is “nothing, but it is more visible”, that is a legitimate reason — visibility is worth something. It is just a different reason from “we cannot currently answer X”, and confusing the two is how estates end up with three reporting systems and no evidence chain.

The three categories

Notification callbacks push events outward as they happen. The community.general collection carries the familiar ones — community.general.slack, community.general.mail and community.general.log_plays among them — and all of them are collection content, not ansible-core, so they are a pinned dependency like any other.

They answer: did the run happen, and how did it end? — to people who are not watching a terminal.

External run recorders capture the whole run into a queryable store. ARA is the established one: it records plays, tasks, hosts and results into a database and gives you a web interface and an API over them.

It answers: what happened across many runs, without writing queries against files?

Platform job history — AWX or Automation Platform — makes the run a first-class object with an owner, a status, a log and an inventory snapshot. Part XXXIX’s platform object model lesson owns this subject; here it appears only as one of three ways to get run history.

It answers: who ran what, with which credentials, against which inventory — with identity established before the run rather than inferred afterwards.

The costs, stated honestly

Callbacks run in the controller process

This is the cost people meet first and attribute to something else.

A notification callback is code executing inside ansible-playbook, on the controller, synchronously with the run. A webhook with a five-second connect timeout, fired per task, on a play with 200 tasks, adds time to every run — and the failure mode when the endpoint is slow rather than down is worse, because the run does not fail, it just takes twice as long and nobody knows why.

The rule that follows: notify at run level, not at task level. A single message at the end of the run, containing the recap, costs one request. A message per task costs 200, and nobody reads them.

A reporting failure must not be a change failure

The design principle for anything in this lesson:

A production change must not depend on a chat service, a ticketing system or a recording database being available.

The reasoning is straightforward once stated. The runs you most need to complete are the ones during an incident, and an incident is exactly when your chat provider, your ticketing system or your internal database is most likely to be degraded — sometimes by the same underlying cause.

Practically:

  • Test the integration’s failure path, deliberately, before relying on it. Point it at an unreachable endpoint and run a play. If the run fails, or hangs for minutes, you have created a dependency and you found out cheaply.
  • Keep timeouts short on anything a callback contacts.
  • Prefer aggregating at the end over streaming during, so a failed notification costs you a message rather than a run.

Sending run data outward is sending module results outward

Everything from lesson 3 applies, with the boundary moved.

A notification callback that includes task results is transmitting whatever those tasks returned — including anything no_log did not cover — to a third-party service, where it is retained under somebody else’s policy, searchable by everyone in the channel, and subject to their breach history rather than yours.

An external recorder is a system you now operate

ARA and its equivalents give you a real query interface, and in exchange you are running a database and a web service.

That has consequences that are ordinary and easy to underestimate:

  • It needs backup, patching, capacity and a restore procedure — and it is now part of your audit trail, so losing it loses evidence.
  • It has an access-control question of its own: it holds task results from every run, which makes it approximately as sensitive as the controller.
  • It is one more thing that can be down during the incident when you want to query it, which argues for keeping the file artefacts as well rather than instead.

None of that is an argument against it. It is an argument for deciding deliberately, because a recorder adopted casually becomes an unowned service holding your audit evidence.

Platform job history does not cover the runs that bypass it

The strongest of the three, with one specific gap.

A platform records the runs made through the platform. Runs made by somebody with a checkout and a terminal are invisible to it — and adopting a platform tends to create a confident belief that all runs are recorded, which is more dangerous than knowing they are not.

Closing that gap is an access decision rather than a tooling one: the automation account’s credentials live only on the platform, so a laptop cannot reach the fleet at all. Until that is true, the platform’s job history is a record of the disciplined runs and not of the interesting ones.

Choosing

Read-only / Safethe baseline, before adding anything
ansible-config dump --only-changed

If log_path is set, a structured callback is enabled and artefacts carry the metadata from lesson 4, you already have an evidence chain. Add outward integrations against a named question:

QuestionSmallest thing that answers it
Did the nightly run happen and how did it end?A run-level notification, or an alert on artefact absence
Which host did this run touch?The artefact — no integration needed
What changed across the fleet last month?Queries over retained artefacts
Who ran this, established rather than inferred?Platform job history, or CI with authenticated requesters
Can a non-operator see run history without shell access?An external recorder, or a platform
Is this run slower than usual?Timing callbacks plus retained artefacts

Two rows in that table are answered by things you already have. That is the point of the table.

Knowledge check

Knowledge check · 4 questions

  1. Q1. A team enables a chat notification callback that posts on every task. Runs get slower and nobody can say why. What is the mechanism?

  2. Q2. What gap remains after adopting a platform whose job history records every run made through it?

  3. Q3. Which of these are real costs of an external run recorder such as ARA? Select all that apply.

  4. Q4. Enabling a notification callback in ansible.cfg applies it to every playbook in the repository, including dry runs, which is usually not what a team wants.

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