unit 4 · Playbooks

Registering output & debug

The only state that survives

essential · 6 min · linux + windows

you may want to read 12 · Playbook anatomy first — but nothing is locked

tl;dr

  • register: captures a task's full result dict into a named variable — the only state between tasks.
  • Inspect .stdout, .rc and .changed; print them with ansible.builtin.debug (msg or var).
  • register + debug is the everyday inspection loop: run, print, read, adjust.

why it matters Playbook output shows only ok or changed — when a run misbehaves, register + debug is how you see what a task actually returned instead of guessing.

Lesson 12’s run output was all status and no substance: ok, changed, a recap. If a task ran uptime, what it printed appeared nowhere. Lesson 2 told you why — each task copies a module to the node, executes it, deletes it. State does not persist between tasks. The one survivor is this lesson’s subject: register .

register: is a task keyword. Give it a name, and Ansible stores the task’s entire result (a dictionary of everything the module reported back) in a variable with that name, available to every later task. Capture, then print:

- name: Read uptime # runs on the managed node
  ansible.builtin.command: uptime
  register: uptime_result # the result dict survives under this name

- name: Print it # touches no host — just writes to run output
  ansible.builtin.debug:
    msg: 'Uptime: {{ uptime_result.stdout }}'
the everyday inspection loop: register, then debug

The first task executes remotely; its result travels back as uptime_result. The second task, ansible.builtin.debug, changes nothing — it prints into the run output and reports ok. You met its msg form in lesson 2: a templated string, so lesson 13’s quoting rules apply. Its other form is for when you don’t know what a module returns:

- name: Dump everything the task returned
  ansible.builtin.debug:
    var: uptime_result # a bare variable NAME — no {{ }}, no quotes
var dumps the entire result dict

That prints every field in the dict. Different modules return different fields, so this dump is how you discover what a result actually contains. For command and shell, three fields do most of the daily work:

  • .stdout — what the command printed (.stderr is its sibling).
  • .rc — the exit code. 0 means success, the same convention as PowerShell’s $LASTEXITCODE.
  • .changed — whether Ansible reports the task changed the host.
- name: Check nginx config syntax
  ansible.builtin.command: nginx -t
  register: cfg_check

- name: Read the verdict
  ansible.builtin.debug:
    msg: 'rc={{ cfg_check.rc }}, changed={{ cfg_check.changed }}'
reading the verdict fields

Two caveats. First, cfg_check.changed is true on every run: command cannot know whether anything on the host actually changed, so it always claims a change — fixing that is lesson 22. Second, if nginx -t exits nonzero, the task fails and the play stops for that host before your debug ever runs. Controlling when a result counts as failure is lesson 21.

One scoping note: results are per host. Run that play against twenty machines and each host carries its own uptime_result, holding what its command printed. Debug then prints twenty lines, one per host — exactly what you want when hunting the one box that differs.

Printing is half of what registered results are for. The other half is decisions: a later task can run only when cfg_check.rc == 0, or skip work a previous task proved unnecessary. That’s when:, lesson 18 — and registered variables feed it. For now, build the reflex: when a run surprises you, register the suspect task’s result and debug it. It is the cheapest look inside Ansible you will get, and the first move colleagues make before reaching for -vvv.

self-check — recall, not a test

  1. 1 Your command task ran fine, but its output appears nowhere in the run. Why, and what's the fix?

  2. 2 You registered a command result as cfg_check. Name the three fields you'd read first and what each tells you.

  3. 3 Why does register need to exist at all — what architectural fact makes it necessary?

lab (optional — never required)

Write a two-task playbook against localhost: run uptime via ansible.builtin.command, register the result, print result.stdout with debug — then swap msg for var and read the full dict.

needs: The control node from lesson 5.

next up 16 · Variables & the precedence that matters 8 min · unit 5