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
- Play - a section of a playbook that maps a group of hosts to a list of tasks.
- Task - one module call with its options.
- PLAY RECAP - the summary table printed at the end of every run.
- ok - the task was evaluated and reality already matched - nothing was done.
- changed - the task altered the managed host.
- skipped - a condition on the task was false, so it was not run at all.
- unreachable - Ansible could not connect to the host at all.
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, pickansible-inventory-groups, and fix the real broken project - Ansible really does SSH into four managed hosts and converge them.