How to Display Updated Packages List in Ansible Package Management for Ubuntu/CentOS Systems


2 views

When managing multiple servers through Ansible, tracking exactly which packages get updated during maintenance windows becomes crucial for change management and auditing. The default behavior of Ansible's package modules (apt for Ubuntu/Debian and yum for CentOS/RHEL) doesn't natively provide this information in their return values.

We can leverage Ansible's register feature combined with shell commands to capture the actual package changes. Here's an enhanced version of your playbook that implements this solution:

- hosts: ubuntu
  tasks:
    - name: Get current package list (pre-update)
      shell: dpkg-query -W -f='${Package} ${Version}\n'
      register: pre_update_packages
      changed_when: false

    - name: install all updates
      apt:
        upgrade: dist
        update_cache: yes
        autoremove: yes
        autoclean: yes
      register: apt_result

    - name: Get updated package list (post-update)
      shell: dpkg-query -W -f='${Package} ${Version}\n'
      register: post_update_packages
      changed_when: false
      when: apt_result.changed

    - name: Display package changes
      debug:
        msg: "Updated packages: {{ (post_update_packages.stdout_lines | difference(pre_update_packages.stdout_lines)) | join('\n') }}"
      when: apt_result.changed

- hosts: centos
  tasks:
    - name: Get current package list (pre-update)
      shell: rpm -qa --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n'
      register: pre_update_packages
      changed_when: false

    - name: install all updates
      yum:
        name: '*'
        update_cache: yes
        state: latest
      register: yum_result

    - name: Get updated package list (post-update)
      shell: rpm -qa --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n'
      register: post_update_packages
      changed_when: false
      when: yum_result.changed

    - name: Display package changes
      debug:
        msg: "Updated packages: {{ (post_update_packages.stdout_lines | difference(pre_update_packages.stdout_lines)) | join('\n') }}"
      when: yum_result.changed

For CentOS/RHEL systems, we can also parse yum history to get more detailed information about the updates:

- hosts: centos
  tasks:
    - name: Get last transaction ID before update
      shell: yum history list | head -n 5 | tail -n 1 | awk '{print $1}'
      register: pre_update_history_id
      changed_when: false

    - name: Run yum updates
      yum:
        name: '*'
        update_cache: yes
        state: latest
      register: yum_result

    - name: Display updated packages from yum history
      shell: |
        if [ {{ yum_result.changed | bool }} = true ]; then
          yum history info {{ pre_update_history_id.stdout | int + 1 }} | grep -E '^    [^ ]'
        fi
      register: yum_history_output
      changed_when: false
      when: yum_result.changed

    - name: Show yum history output
      debug:
        var: yum_history_output.stdout_lines
      when: yum_result.changed

In environments with hundreds of servers, you might want to collect this data more systematically. Here's how to store it in a file:

- hosts: all
  tasks:
    - name: Create update log directory
      file:
        path: /var/log/ansible_updates
        state: directory
        mode: '0755'
      
    - name: Log package updates
      copy:
        content: |
          Date: {{ ansible_date_time.iso8601 }}
          Host: {{ inventory_hostname }}
          Updated packages:
          {% for pkg in package_changes %}
          - {{ pkg }}
          {% endfor %}
        dest: /var/log/ansible_updates/updates-{{ inventory_hostname }}-{{ ansible_date_time.iso8601_basic }}.log
      delegate_to: localhost
      vars:
        package_changes: "{{ (post_update_packages.stdout_lines | difference(pre_update_packages.stdout_lines)) }}"
      when: package_changes | length > 0

When managing multiple servers through Ansible, one common frustration is the lack of visibility into which specific packages get updated during maintenance operations. The standard output from both apt and yum modules doesn't provide this crucial detail by default.

For apt-based systems, we can leverage the command module to capture the upgrade preview before executing:

- name: Preview available updates (Ubuntu/Debian)
  command: apt list --upgradable
  register: apt_upgradable
  changed_when: false

- name: Display upgradable packages
  debug:
    var: apt_upgradable.stdout_lines

- name: Execute the actual upgrade
  apt:
    upgrade: dist
    update_cache: yes
  register: apt_result

- name: Display upgrade results
  debug:
    var: apt_result.stdout_lines

For yum-based systems, we can use the check mode combined with verbose output:

- name: Check for available updates (CentOS/RHEL)
  yum:
    list: updates
  register: yum_updates

- name: Display available updates
  debug:
    var: yum_updates.results

- name: Perform the upgrade
  yum:
    name: '*'
    state: latest
  register: yum_result

- name: Display upgrade changes
  debug:
    var: yum_result.changes

For more detailed reporting, we can parse the output using Ansible filters:

- name: Parse apt upgrade output
  set_fact:
    updated_packages: "{{ apt_result.stdout | regex_findall('(^\\S+) ') }}"

- name: Display parsed package list
  debug:
    var: updated_packages

For systems where module output is limited, we can fall back to raw commands:

- name: Get upgrade details via command
  command: yum history info | grep -A10 "Updated Packages"
  register: yum_history
  changed_when: false

- name: Show upgrade history
  debug:
    var: yum_history.stdout_lines

For production use, consider creating a custom module or role that:

- Collects pre-upgrade state
- Performs the upgrade
- Compares pre/post states
- Generates a detailed report