When working with Ansible playbooks across different OS versions, a common pitfall occurs when registering variables with the same name across conditional tasks. The original playbook demonstrates this:
- name: Check for outstanding reboot (CentOS 6)
shell: needs-restarting > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int < 7
register: result
- name: Check for outstanding reboot (CentOS 7+)
shell: needs-restarting -r > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int >= 7
register: result
Ansible's task execution model processes tasks sequentially, and when multiple tasks register the same variable name, each subsequent registration overwrites the previous one. This becomes problematic when:
- Tasks run conditionally on different hosts
- You need to reference the output later in the playbook
- You want to maintain clean, DRY (Don't Repeat Yourself) code
Here are three approaches to solve this issue:
1. Unique Variable Names per Task
- name: Check for outstanding reboot (CentOS 6)
shell: needs-restarting > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int < 7
register: result_6
- name: Check for outstanding reboot (CentOS 7+)
shell: needs-restarting -r > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int >= 7
register: result_7
- name: Report reboot
debug:
msg: "{{ (result_6.stdout_lines if result_6 is defined else result_7.stdout_lines) }}"
2. Using set_fact for Unified Variable
- name: Check for outstanding reboot (CentOS 6)
shell: needs-restarting > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int < 7
register: temp_result
changed_when: false
- name: Check for outstanding reboot (CentOS 7+)
shell: needs-restarting -r > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int >= 7
register: temp_result
changed_when: false
- name: Set final result
set_fact:
final_result: "{{ temp_result.stdout_lines }}"
- name: Report reboot
debug:
var: final_result
3. Combining Conditions in a Single Task
- name: Unified reboot check
shell: |
if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -lt 7 ]; then
needs-restarting > /dev/null || echo Reboot required
else
needs-restarting -r > /dev/null || echo Reboot required
fi
register: result
changed_when: false
- name: Report reboot
debug:
var: result.stdout_lines
- Prefix variables with task-specific identifiers when multiple registrations exist
- Use
changed_when: false
for purely informational commands - Consider using
set_fact
to create a unified variable namespace - Document variable usage in playbook comments or README files
For more complex scenarios, you can use Jinja2 templating to create dynamic variable names:
- name: Version-specific reboot check
shell: >
{% if ansible_distribution_major_version|int < 7 %}
needs-restarting > /dev/null || echo Reboot required
{% else %}
needs-restarting -r > /dev/null || echo Reboot required
{% endif %}
register: "reboot_check_{{ ansible_distribution_major_version }}"
- name: Report results
debug:
msg: "{{ hostvars[inventory_hostname]['reboot_check_' ~ ansible_distribution_major_version].stdout_lines }}"
When working with Ansible's conditionally executed tasks, a common pitfall occurs when registering variables with the same name across multiple tasks. The issue manifests when:
- name: Task 1
command: cmd1
when: condition_a
register: result
- name: Task 2
command: cmd2
when: condition_b
register: result # This overwrites previous registration
- name: Debug
debug: var=result # Which result are we seeing?
Ansible maintains variable scope at the playbook level, not per task. When you register a variable with the same name in multiple tasks:
- The last registration overwrites previous ones
- Skipped tasks don't register variables at all
- This leads to undefined variable errors when referenced later
Option 1: Unique Variable Names
The simplest approach is to use distinct variable names:
- name: Check reboot for CentOS 6
shell: needs-restarting > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int < 7
register: result_6
- name: Check reboot for CentOS 7+
shell: needs-restarting -r > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int >= 7
register: result_7
- name: Report reboot status
debug:
msg: "{{ (result_6.stdout_lines if result_6 is defined else result_7.stdout_lines) | default([]) }}"
Option 2: Using set_fact to Merge Results
For more complex scenarios, combine results with set_fact:
- name: Check reboot for CentOS 6
shell: needs-restarting > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int < 7
register: tmp_result
changed_when: false
- name: Check reboot for CentOS 7+
shell: needs-restarting -r > /dev/null || echo Reboot required
when: ansible_distribution_major_version|int >= 7
register: tmp_result
changed_when: false
- name: Consolidate results
set_fact:
final_result: "{{ tmp_result }}"
when: tmp_result is defined
- name: Report reboot status
debug:
var: final_result.stdout_lines
when: final_result is defined
For maximum reliability, combine with default filters:
- name: Unified report task
debug:
msg: "{{ (result_6.stdout_lines if result_6 is defined else result_7.stdout_lines) | default('No reboot check performed') }}"
These techniques work across Ansible versions, but newer versions (2.5+) offer additional options like vars
keyword for task-specific variables. For legacy systems (like the mentioned 2.4.4), the approaches above remain the most reliable.