How to Handle Multiple Task Variable Registration in Ansible for Cross-Version Reboot Checks


4 views

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.