Imagine being able to retrieve the patch status of hundreds of Linux servers with a simple script and display it neatly in a table. Sounds practical, right?
This small Ansible playbook makes exactly that possible.
The playbook checks the number of pending updates on Debian-based Linux systems and sends the results in a tabular format by email.
This way, you can instantly see whether a server was forgotten or if automatic updates are working properly.
The output includes:
- Hostname
- Number of pending updates (numeric)
Ansible Playbook: Number of Pending Updates
---
- name: Check update status and send email
hosts: all
become: yes
vars:
mailserver: dein-mailserver
mail_sender: ansible@example.com
mail_recipient: admin@example.com
pre_tasks:
# Ensure the temporary file for storing update statuses is empty and exists
- name: Ensure /tmp/all_update_status.txt is empty and exists
copy:
content: ""
dest: /tmp/all_update_status.txt
delegate_to: localhost
run_once: true
tasks:
# Update the APT cache
- name: Update the apt cache
apt:
update_cache: yes
# Check for available updates on Debian systems
- name: Check for available updates on Debian
shell: apt list --upgradable 2>/dev/null | grep -v 'Listing...'
register: update_output
# Ensure update_output.stdout_lines is defined to avoid errors
- name: Ensure update_output.stdout_lines is defined
set_fact:
update_lines: "{{ update_output.stdout_lines | default([]) }}"
# Format the update status for the current host
- name: Format update status
set_fact:
host_update_status: |
{{ inventory_hostname }}
{{ update_lines | length }}
delegate_to: localhost
# Add the formatted update status to the consolidated list
- name: Add host update status to list
lineinfile:
path: /tmp/all_update_status.txt
line: "{{ host_update_status }}"
delegate_to: localhost
- name: Send consolidated update status email
hosts: localhost
gather_facts: no
vars:
msmtp_config_path: "/root/.msmtprc"
mail_sender: ansible@example.com
mail_recipient: admin@example.com
tasks:
# Read all update statuses from the temporary file
- name: Read all update statuses
command: cat /tmp/all_update_status.txt
register: all_update_status_content
# Send the update status email using msmtp
- name: Send update status email with msmtp
shell: |
echo -e "From: {{ mail_sender }}\nTo: {{ mail_recipient }}\nSubject: Debian Update Status\nMIME-Version: 1.0\nContent-Type: text/html\n\nHostnameAusstehende Updates{{ all_update_status_content.stdout }}" | msmtp --file={{ msmtp_config_path }} "{{ mail_recipient }}"
register: msmtp_result
ignore_errors: yes
What the Ansible Playbook does
- Empties and creates a collection file on the control host
- Updates the APT cache on all target systems
- Determines the number of available updates
- Constructs HTML table rows from this data
- Consolidates all hosts into a single table
- Sends the result via email (HTML format)
Variant: Pending Updates including Package Names
If you want not only the number but also the names of the pending updates, you can use this playbook.
Ansible Playbook: Updates including Package Names
---
- name: Check update status and send email
hosts: all
become: yes
vars:
mailserver: dein_mail_server
mail_sender: sender@example.com
mail_recipient: recipient@example.com
pre_tasks:
- name: Ensure /tmp/all_update_status.txt is empty and exists
copy:
content: ""
dest: /tmp/all_update_status.txt
delegate_to: localhost
run_once: true
tasks:
- name: Update the apt cache
apt:
update_cache: yes
- name: Check for available updates on Debian
shell: apt list --upgradable 2>/dev/null | grep -v 'Listing...'
register: update_output
- name: Ensure update_output.stdout_lines is defined
set_fact:
update_lines: "{{ update_output.stdout_lines | default([]) }}"
- name: Format update status
set_fact:
host_update_status: |
{{ inventory_hostname }}
{{ update_lines | length }}
{% for line in update_lines %}
{{ line }}
{% endfor %}
delegate_to: localhost
- name: Add host update status to list
lineinfile:
path: /tmp/all_update_status.txt
line: "{{ host_update_status }}"
delegate_to: localhost
- name: Send consolidated update status email
hosts: localhost
gather_facts: no
vars:
msmtp_config_path: "/root/.msmtprc"
mail_sender: sender@example.com
mail_recipient: recipient@example.com
tasks:
- name: Read all update statuses
command: cat /tmp/all_update_status.txt
register: all_update_status_content
- name: Send update status email with msmtp
shell: |
echo -e "From: {{ mail_sender }}\nTo: {{ mail_recipient }}\nSubject: Debian Update Status\nMIME-Version: 1.0\nContent-Type: text/html\n\nHostnameAnzahl der UpdatesUpdate-Namen{{ all_update_status_content.stdout }}" | msmtp --file={{ msmtp_config_path }} "{{ mail_recipient }}"
register: msmtp_result
ignore_errors: yes
Extension Ideas
- Sorting by number of updates
- Highlighting critical packages
- CSV or JSON export
- Slack / Matrix / Teams notifications instead of email
- Cron execution on the control node