Ansible register: Save Task Output to Variables (Complete Guide)
By Luca Berton · Published 2024-01-01 · Category: installation
Complete guide to Ansible register. Save task output to variables, access stdout, stderr, return codes, and use registered results in conditions, loops.
The register keyword captures a task's output into a variable for use in subsequent tasks. It's one of the most used Ansible features — essential for conditional logic, debugging, chaining tasks, and building dynamic playbooks.
Basic Syntax
- name: Run a command
ansible.builtin.command: whoami
register: result
- name: Show the output
ansible.builtin.debug:
var: result
The registered variable result is a dictionary containing the task's full return data.
See also: Ansible Variable Precedence: Complete Order of Priority (2026)
Registered Variable Structure
Every registered variable contains at minimum:
result:
changed: true/false # Whether the task made changes
failed: false # Whether the task failed
msg: "" # Module message (if any)
# For command/shell modules:
cmd: "whoami" # The command that ran
stdout: "root" # Standard output
stdout_lines: ["root"] # Stdout split by newlines
stderr: "" # Standard error
stderr_lines: [] # Stderr split by newlines
rc: 0 # Return code
start: "2026-04-12 ..." # Start timestamp
end: "2026-04-12 ..." # End timestamp
delta: "0:00:00.003" # Execution time
Access Registered Data
stdout and stderr
- name: Get hostname
ansible.builtin.command: hostname -f
register: hostname_result
changed_when: false
- name: Use the hostname
ansible.builtin.debug:
msg: "This server is {{ hostname_result.stdout }}"
Return Code
- name: Check if file exists
ansible.builtin.command: test -f /etc/app/config.yml
register: file_check
failed_when: false
changed_when: false
- name: Create config if missing
ansible.builtin.template:
src: config.yml.j2
dest: /etc/app/config.yml
when: file_check.rc != 0
stdout_lines (List)
- name: List running services
ansible.builtin.command: systemctl list-units --type=service --state=running --no-pager --plain
register: services
changed_when: false
- name: Show service count
ansible.builtin.debug:
msg: "{{ services.stdout_lines | length }} services running"
- name: Check if nginx is running
ansible.builtin.debug:
msg: "nginx is running"
when: services.stdout_lines | select('search', 'nginx') | list | length > 0
See also: Ansible default() Filter: Set Fallback Values for Undefined Variables
Use in Conditions (when)
- name: Get OS release
ansible.builtin.command: cat /etc/os-release
register: os_release
changed_when: false
- name: Ubuntu-specific task
ansible.builtin.debug:
msg: "This is Ubuntu"
when: "'Ubuntu' in os_release.stdout"
- name: RHEL-specific task
ansible.builtin.debug:
msg: "This is RHEL"
when: "'Red Hat' in os_release.stdout"
Check Success or Failure
- name: Try to connect to database
ansible.builtin.command: pg_isready -h {{ db_host }}
register: db_check
failed_when: false
changed_when: false
- name: Database is available
ansible.builtin.debug:
msg: "Database is ready"
when: db_check is success
- name: Database is down
ansible.builtin.debug:
msg: "Cannot reach database"
when: db_check is failed
Register with Loops
When used with loops, the registered variable contains a results list:
- name: Check multiple services
ansible.builtin.command: "systemctl is-active {{ item }}"
register: service_status
loop:
- nginx
- redis
- postgresql
failed_when: false
changed_when: false
- name: Show stopped services
ansible.builtin.debug:
msg: "{{ item.item }} is {{ item.stdout }}"
loop: "{{ service_status.results }}"
when: item.stdout != 'active'
Loop Results Structure
service_status:
results:
- item: nginx # The loop item
stdout: "active"
rc: 0
changed: false
- item: redis
stdout: "inactive"
rc: 3
changed: false
See also: Ansible vars_files: Load Variables from External YAML Files (Guide)
Register Module-Specific Data
File Module (stat)
- name: Check file details
ansible.builtin.stat:
path: /etc/nginx/nginx.conf
register: nginx_conf
- name: Show file info
ansible.builtin.debug:
msg: |
Exists: {{ nginx_conf.stat.exists }}
Size: {{ nginx_conf.stat.size | default('N/A') }}
Owner: {{ nginx_conf.stat.pw_name | default('N/A') }}
Modified: {{ nginx_conf.stat.mtime | default('N/A') }}
Package Module
- name: Install nginx
ansible.builtin.package:
name: nginx
state: present
register: install_result
- name: Notify if newly installed
ansible.builtin.debug:
msg: "nginx was just installed"
when: install_result.changed
URI Module (API calls)
- name: Call API
ansible.builtin.uri:
url: https://api.example.com/users
return_content: true
register: api_response
- name: Process API response
ansible.builtin.debug:
msg: "Found {{ api_response.json | length }} users"
- name: Use specific field
ansible.builtin.debug:
msg: "First user: {{ api_response.json[0].name }}"
Service Facts
- name: Gather service facts
ansible.builtin.service_facts:
register: services
- name: Check specific service
ansible.builtin.debug:
msg: "nginx state: {{ services.ansible_facts.services['nginx.service'].state }}"
when: "'nginx.service' in services.ansible_facts.services"
Register with set_fact
Store extracted data for use across plays:
- name: Get app version
ansible.builtin.command: /opt/myapp/bin/version
register: version_output
changed_when: false
- name: Store version as fact
ansible.builtin.set_fact:
app_version: "{{ version_output.stdout | trim }}"
cacheable: true
- name: Use the version later
ansible.builtin.debug:
msg: "Running version {{ app_version }}"
Register with Templates
- name: Get server info
ansible.builtin.command: uname -a
register: uname_result
changed_when: false
- name: Generate report
ansible.builtin.template:
src: report.j2
dest: /tmp/server_report.txt
vars:
server_info: "{{ uname_result.stdout }}"
Common Patterns
Parse JSON Output
- name: Get docker info
ansible.builtin.command: docker info --format '{{ '{{' }}json .{{ '}}' }}'
register: docker_info
changed_when: false
- name: Parse JSON
ansible.builtin.set_fact:
docker_data: "{{ docker_info.stdout | from_json }}"
- name: Show containers
ansible.builtin.debug:
msg: "Running containers: {{ docker_data.ContainersRunning }}"
Conditional Task Chain
- name: Check if app is installed
ansible.builtin.command: which myapp
register: app_installed
failed_when: false
changed_when: false
- name: Install app
ansible.builtin.package:
name: myapp
when: app_installed.rc != 0
- name: Get current version
ansible.builtin.command: myapp --version
register: current_version
changed_when: false
when: app_installed.rc == 0
- name: Upgrade if old version
ansible.builtin.package:
name: myapp
state: latest
when:
- app_installed.rc == 0
- current_version.stdout is version('2.0', '<')
Common Mistakes
Using Undefined Register
# Bug: result not defined if when is false
- name: Conditional task
ansible.builtin.command: echo "hello"
register: result
when: run_task | default(false)
# Fix: check if defined
- name: Use result safely
ansible.builtin.debug:
msg: "{{ result.stdout | default('task was skipped') }}"
when: result is defined and result is not skipped
Forgetting changed_when for Read-Only Commands
# Bug: always reports "changed"
- name: Check version
ansible.builtin.command: python3 --version
register: py_version
# Fix: mark as read-only
- name: Check version
ansible.builtin.command: python3 --version
register: py_version
changed_when: false
FAQ
What does register do in Ansible?
The register keyword saves a task's complete output (stdout, stderr, return code, changed status, and module-specific data) into a variable. You can then use this variable in subsequent tasks for conditions, templates, or debugging.
How do I access stdout from a registered variable?
Use result.stdout for the full output as a string, or result.stdout_lines for a list split by newlines: msg: "{{ result.stdout }}".
Can I register variables in a loop?
Yes, when used with loop, the registered variable contains a results list. Each item has the loop value in item.item and the task output in the usual fields (item.stdout, item.rc, etc.).
How do I check if a registered task failed?
Use when: result is failed or when: result is success. For command/shell tasks, you can also check result.rc != 0.
Does register work with all modules?
Yes, every Ansible module returns data that can be registered. The exact fields vary by module — command/shell return stdout/stderr/rc, uri returns json/status, stat returns file info, etc.
Conclusion
register is fundamental to dynamic Ansible playbooks:
• Capture output: register: result on any task
• Access data: result.stdout, result.rc, result.json, result.stat
• Use in conditions: when: result is success or when: "'text' in result.stdout"
• Loop results: Access via result.results[].item
• Always add changed_when: false to read-only command/shell tasks
Related Articles
• Ansible set_fact Module: Set Variables Dynamically • Ansible changed_when & failed_when: Control Task Status • Print Text or Variable: Ansible debug Module • Ansible when Conditionals: Complete GuideCategory: installation