Files
ansible-msp-automations/roles/linux_patch/tasks/main.yml

132 lines
4.1 KiB
YAML

---
- name: Gather package facts before patching
ansible.builtin.package_facts:
manager: auto
register: packages_before
- name: Store pre-patch package versions
ansible.builtin.set_fact:
packages_pre_patch: "{{ ansible_facts.packages }}"
- name: Check for available updates (Debian/Ubuntu)
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
when: ansible_os_family == "Debian"
changed_when: false
- name: Get list of upgradable packages (Debian/Ubuntu)
ansible.builtin.shell: |
apt list --upgradable 2>/dev/null | grep -v "Listing..." | awk -F'/' '{print $1}'
register: upgradable_packages
changed_when: false
when: ansible_os_family == "Debian"
- name: Get list of upgradable packages (RHEL/CentOS)
ansible.builtin.shell: |
dnf check-update --quiet | awk '{print $1}' | grep -v "^$"
register: upgradable_packages
changed_when: false
failed_when: upgradable_packages.rc not in [0, 100]
when: ansible_os_family == "RedHat"
- name: Log packages to be updated
ansible.builtin.debug:
msg: "Packages to be updated on {{ inventory_hostname }}: {{ upgradable_packages.stdout_lines | length }} packages"
- name: Perform full upgrade (Debian/Ubuntu)
ansible.builtin.apt:
upgrade: dist
autoremove: true
autoclean: true
register: apt_upgrade_result
when:
- ansible_os_family == "Debian"
- patch_mode == "full" or patch_mode == "security"
- name: Perform security-only upgrade (RHEL/CentOS)
ansible.builtin.dnf:
name: "*"
state: latest
security: "{{ patch_mode == 'security' }}"
update_cache: true
register: dnf_upgrade_result
when: ansible_os_family == "RedHat"
- name: Gather package facts after patching
ansible.builtin.package_facts:
manager: auto
- name: Store post-patch package versions
ansible.builtin.set_fact:
packages_post_patch: "{{ ansible_facts.packages }}"
- name: Calculate changed packages
ansible.builtin.set_fact:
packages_updated: >-
{{
packages_post_patch | dict2items
| selectattr('key', 'in', packages_pre_patch)
| selectattr('value', '!=', packages_pre_patch[item.key] | default([]))
| list
| map(attribute='key')
| list
}}
loop: "{{ packages_post_patch | dict2items }}"
when: false
- name: Build packages updated list
ansible.builtin.set_fact:
packages_updated: >-
{%- set updated = [] -%}
{%- for pkg, details in packages_post_patch.items() -%}
{%- if pkg in packages_pre_patch -%}
{%- if details[0].version != packages_pre_patch[pkg][0].version -%}
{%- set _ = updated.append({
'name': pkg,
'version_before': packages_pre_patch[pkg][0].version,
'version_after': details[0].version,
'type': 'updated'
}) -%}
{%- endif -%}
{%- else -%}
{%- set _ = updated.append({
'name': pkg,
'version_before': 'not installed',
'version_after': details[0].version,
'type': 'new'
}) -%}
{%- endif -%}
{%- endfor -%}
{{ updated }}
- name: Log updated packages
ansible.builtin.debug:
msg: "Updated: {{ item.name }} {{ item.version_before }} -> {{ item.version_after }}"
loop: "{{ packages_updated }}"
- name: Check if reboot is required after patching (Debian/Ubuntu)
ansible.builtin.stat:
path: /var/run/reboot-required
register: reboot_required_post
when: ansible_os_family == "Debian"
- name: Update reboot required fact
ansible.builtin.set_fact:
host_reboot_required: "{{ reboot_required_post.stat.exists | default(false) }}"
when: ansible_os_family == "Debian"
- name: Reboot if required and auto_reboot is enabled
ansible.builtin.reboot:
reboot_timeout: 300
pre_reboot_delay: 10
post_reboot_delay: 30
msg: "Rebooting after patch run — initiated by Ansible"
when:
- host_reboot_required | bool
- auto_reboot | bool
- name: Patching complete
ansible.builtin.debug:
msg: "Patching complete on {{ inventory_hostname }} — {{ packages_updated | length }} packages updated"