At Red Hat Tech Day Netherlands 2026 in Bunnik, Fred van Zwieten and Ismail Dhaoui demoed the new AAP 2.7 Automation Portal, a self-service template gallery where operators hit "Start" without knowing where a playbook lives or how it's configured. One catalog entry, CVE Patch, runs a patch for a given CVE. Here's a runnable playbook that models what that template kicks off.
Example playbook: cve-patch-rollout.yml
---
- name: Emergency CVE patch rollout
hosts: "{{ target_group | default('patch_targets') }}"
become: true
serial: "{{ batch_size | default('20%') }}"
max_fail_percentage: 10
vars:
cve_id: "CVE-2026-XXXXX" # supplied by the Automation Portal form
package_name: "openssl" # package affected by the CVE
fixed_version: "" # optional, pin to a specific fixed build
tasks:
- name: Record patch job start
ansible.builtin.debug:
msg: "Starting {{ cve_id }} remediation for {{ package_name }} on {{ inventory_hostname }}"
- name: Check currently installed package version
ansible.builtin.package_facts:
manager: auto
- name: Fail fast if package is not installed
ansible.builtin.assert:
that:
- package_name in ansible_facts.packages
fail_msg: "{{ package_name }} not found on {{ inventory_hostname }}, skipping"
success_msg: "{{ package_name }} present, proceeding with patch"
- name: Update package to fixed version (RHEL/CentOS)
ansible.builtin.dnf:
name: "{{ package_name if fixed_version == '' else package_name + '-' + fixed_version }}"
state: latest
update_cache: true
when: ansible_facts['os_family'] == "RedHat"
register: patch_result
- name: Update package to fixed version (Debian/Ubuntu)
ansible.builtin.apt:
name: "{{ package_name if fixed_version == '' else package_name + '=' + fixed_version }}"
state: latest
update_cache: true
when: ansible_facts['os_family'] == "Debian"
register: patch_result
- name: Restart affected service if package changed
ansible.builtin.service:
name: "{{ service_name | default(package_name) }}"
state: restarted
when: patch_result.changed
- name: Re-check package version after patch
ansible.builtin.package_facts:
manager: auto
- name: Report final patched version
ansible.builtin.debug:
msg: "{{ package_name }} on {{ inventory_hostname }} is now {{ ansible_facts.packages[package_name][0].version }}"
What it does
The play targets a patch_targets group (or whatever inventory group the Automation Portal template passes in) and rolls out in batches — serial: 20% by default — so a bad patch doesn't hit the whole fleet at once. It confirms the vulnerable package is actually present before touching anything, updates it to the latest (or a pinned fixed) version via the correct package manager for the OS family, restarts the affected service only when the package actually changed, and reports the resulting version for audit purposes.
This maps directly to how a gallery template is meant to work: cve_id, package_name, and fixed_version are the kind of extra_vars a self-service form would collect from the operator, while the underlying playbook stays generic enough to run against any CVE that boils down to "update this package."
Notes / Gotchas
max_fail_percentage: 10stops the rollout if too many hosts fail mid-batch — tune this for your risk tolerance during an active exploit window.- The
asserttask turns "package not installed" into a controlled skip instead of a hard failure across the whole batch; adjust toignore_errorsor a rescue block if you want per-host tolerance instead. - Pinning
fixed_versionis safer for regulated environments where "latest" isn't an approved change; leave it blank for pure "patch to whatever's current" runs. - Service names rarely match package names one-to-one (e.g.,
openssldoesn't have its own service) — always confirmservice_nameexplicitly rather than relying on the default. - In an AAP job template backing this gallery entry, expose
cve_id,package_name,fixed_version, andtarget_groupas survey fields so operators fill in only what changes per incident.
Takeaway
A CVE patch rollout doesn't need bespoke automation per vulnerability — one parameterized playbook, fed through a self-service template like AAP 2.7's Automation Portal gallery, covers the whole "verify, patch, restart, confirm" loop on demand.
Related Articles
- Ansible Playbook Example: RHEL Patching in a Maintenance Window
- Ansible Playbook Example: Firewall Rule Request Approval Workflow
- Ansible Playbook Example: Remediate a Firewall Rule Misconfiguration