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