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: 10 stops the rollout if too many hosts fail mid-batch — tune this for your risk tolerance during an active exploit window.
  • The assert task turns "package not installed" into a controlled skip instead of a hard failure across the whole batch; adjust to ignore_errors or a rescue block if you want per-host tolerance instead.
  • Pinning fixed_version is 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., openssl doesn't have its own service) — always confirm service_name explicitly rather than relying on the default.
  • In an AAP job template backing this gallery entry, expose cve_id, package_name, fixed_version, and target_group as 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.

References