Is the Playbook I Wrote Secure and Effective?

Hello everyone,
This is my first time posting here, and I’m quite excited.

I’ve written a playbook and would like to know whether it achieves the intended results fully, and if there’s anything I should improve or optimize.

What I want this playbook to do:

  • Upgrade the current version of Ubuntu to the latest point release within the same version series.
    For example:
    • If the system is running Ubuntu 22.04, it should be updated to 22.04.5.
    • If it’s on 24.04.1, it should become 24.04.3, and so on.
  • Ensure that none of my existing configurations are lost or overwritten during the update process.
    Sometimes during upgrades, the system may prompt to keep or replace config files — I want to make sure all existing configurations are preserved automatically.
  • Finally, if a reboot is required after the update, the system should automatically reboot.

I’ve tested the playbook a few times and it seems to work, but I’d like to be fully confident that it’s safe and does exactly what I intend. :slightly_smiling_face:

Here is the playbook I wrote:

---
- name: Update Ubuntu Servers
  hosts: nesus
  become: yes
  gather_facts: yes

  tasks:
    - name: Display current version
      debug:
        msg: "Current version: {{ ansible_distribution }} {{ ansible_distribution_version }}"

    - name: Update APT cache
      apt:
        update_cache: yes
        cache_valid_time: 0

    - name: Check and terminate any running APT processes
      shell: |
        pkill -9 apt-get || true
        pkill -9 apt || true
        pkill -9 dpkg || true
        sleep 2
      ignore_errors: yes

    - name: Remove APT lock files
      shell: |
        rm -f /var/lib/dpkg/lock-frontend
        rm -f /var/lib/dpkg/lock
        rm -f /var/cache/apt/archives/lock
        rm -f /var/lib/apt/lists/lock
      ignore_errors: yes

    - name: Check for available updates
      command: apt list --upgradable
      register: upgradable_packages
      changed_when: false

    - name: Show number of available updates
      debug:
        msg: "{{ upgradable_packages.stdout_lines | length - 1 }} packages are available for update"
      when: upgradable_packages.stdout_lines | length > 1

    - name: Upgrade packages while preserving existing configurations
      apt:
        upgrade: dist
        update_cache: yes
        autoremove: yes
        autoclean: yes
        force_apt_get: yes
        dpkg_options: 'force-confold,force-confdef'
      environment:
        DEBIAN_FRONTEND: noninteractive
      register: apt_upgrade
      when: upgradable_packages.stdout_lines | length > 1
      ignore_errors: yes

    - name: Update completed
      debug:
        msg: "Update completed successfully!"
      when: apt_upgrade.changed | default(false)

    - name: No updates required
      debug:
        msg: "System is already up-to-date."
      when: upgradable_packages.stdout_lines | length <= 1

    - name: Show updated version
      debug:
        msg: "Updated version: {{ ansible_distribution }} {{ ansible_distribution_version }}"
      when: apt_upgrade.changed | default(false)

    - name: Check if a reboot is required
      stat:
        path: /var/run/reboot-required
      register: reboot_required

    - name: Reboot required notice
      debug:
        msg: "System reboot is required!"
      when: reboot_required.stat.exists

Hii

I think what you want (in-place OS upgrade) at some level contradicts what ansible is meant for.
From what you describe, there seems to be no source of truth regarding what packages are installed, and what configuration they have.

A usual pattern in the ansible world is work the opposite direction: you start with defining which packages/configurations you want to be there.

Letting a high level OS upgrade handle configuration file changes is very fragile. The logic behind that is well intended and may work, but there is absolutely no guarantee that they do.
If you define the configs etc yourself, you can test everything, and that implies you understand what changes are going on (which, IMHO, is a good thing).

Dick

These steps seem risky to run in an automated pipeline. I think you could easily corrupt your package database. Im more used to yum/dnf, but 99.99% of the time having a yum lock means something is changing the filesystem and I should wait

Other than that, I think it looks fine. I dont totally disagree with @dnmvisser; blindly upgrading all packages on a system is more risky that specifying packages and version. But I also think that this is a very common use case and is a practical application of Ansible to automate something you would otherwise need to do manually.