Reading a Run: ok, changed, failed, skipped

Scenario: A colleague says the deploy 'worked fine - it was all green'. The recap showed changed=14 on a run that was supposed to change nothing at all.

New words, in plain English

Ansible's output is the primary diagnostic tool, and changed is the single most informative signal in it. On a converged system - one that already matches the description - a correct run should report changed=0. Anything else means either the system really did drift, or a task is lying about its own result.

That second case is the common one, and it is the root of a surprising share of production incidents. A task that reports changed when nothing changed will re-trigger anything that depends on it - most importantly service restarts. "Nothing changed but the service bounced anyway" is a real outage pattern with a boring cause.

The recap distinguishes four failure-ish states worth separating in your head: failed (the task ran and did not succeed), unreachable (Ansible never got in - usually SSH, DNS or credentials), rescued (a failure that was caught and handled), and ignored (a failure that was deliberately suppressed).

Analogy: changed is a receipt, not a greeting. A shop that hands you a receipt every time you walk past the door, whether or not you bought anything, has made its receipts worthless - and if the receipt triggers a stock reorder, it has also made them expensive.

A worked example

PLAY RECAP ****************************************************
web01 : ok=6    changed=0    unreachable=0    failed=0    skipped=2
web02 : ok=6    changed=1    unreachable=0    failed=0    skipped=2
dbprod: ok=4    changed=0    unreachable=0    failed=0    skipped=4
lb01  : ok=0    changed=0    unreachable=1    failed=0    skipped=0

# Read this as:
#   web01  - already correct, nothing done
#   web02  - had drifted; one thing was fixed
#   dbprod - correct; 4 tasks did not apply to it
#   lb01   - never reached. Fix connectivity before trusting anything else.

A discipline worth adopting immediately: run every playbook twice. The first run converges the system. The second run must report changed=0. If it does not, you have found a non-idempotent task, and you have found it in a lab rather than during a change freeze.

Increase verbosity with -v through -vvvv when a result surprises you. -v shows module return values, -vvv shows the connection and the exact command, and -vvvv adds connection debugging - which is usually where an unreachable host explains itself.

Warning: A green playbook is not proof of a correct playbook. A task guarded by a condition that is accidentally always false will show as skipped forever, quietly doing nothing, and the run will still be entirely green.
Goal: Put this to work in the ansible-inventory-groups lab. Open /labs/ansible, pick ansible-inventory-groups, and fix the real broken project - Ansible really does SSH into four managed hosts and converge them.