# How to compare RPM versions

**URL:** <https://forum.ansible.com/t/how-to-compare-rpm-versions/3794>\
**Category:** Get Help\
**Tags:** collections\
**Created:** [February 9, 2024, 7:12pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794 "2024-02-09T19:12:34Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![jms1](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/jms1/32/2255_2.png) [@jms1](https://forum.ansible.com/u/jms1)\
**Post date:** [February 9, 2024, 7:12pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/1 "2024-02-09T19:12:34Z")

</div>

Is there a test, similar to `ansible.builtin.version`, which knows how to compare RPM version strings, using the same algorithm that the `rpm` and `yum` commands use?

Or even better, the problem I’m hoping to solve with this … is there a way to make `ansible.builtin.package` support the idea of “version X or later”, so we can enforce required minimums in response to security notices?

---

<div class="post-metadata">

**Author:** ![flowerysong](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/flowerysong/32/2305_2.png) [@flowerysong](https://forum.ansible.com/u/flowerysong)\
**Post date:** [February 11, 2024, 4:58pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/2 "2024-02-11T16:58:53Z")

</div>

> [@jms1](#):
>
> Is there a test, similar to `ansible.builtin.version`, which knows how to compare RPM version strings, using the same algorithm that the `rpm` and `yum` commands use?

I am not aware of one, though it would be fairly straightforward to write a custom test for this.

> [@jms1](#):
>
> Or even better, the problem I’m hoping to solve with this … is there a way to make `ansible.builtin.package` support the idea of “version X or later”, so we can enforce required minimums in response to security notices?

`ansible.builtin.package` is just a convenience wrapper that calls whatever package module is appropriate for your system, it does not itself care what parameters you pass. To find out what syntax is actually supported by the module being called, you have to consult its documentation.

Most package installation modules permit you to place constraints on the version installed; since you asked about RPMs, you’re probably using either the `yum` or `dnf` module, which use identical syntax for this:

```yaml
- ansible.builtin.package:
    name:
      # Install an exact release
      - openssh-8.7p1-29.el9_2
      # Install a less exact version
      - openssl-3.0.7
      # Constrain the minimum version
      - git >= 2.40.1
    state: present

```

---

<div class="post-metadata">

**Author:** ![jms1](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/jms1/32/2255_2.png) [@jms1](https://forum.ansible.com/u/jms1)\
**Post date:** [February 13, 2024, 11:07pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/3 "2024-02-13T23:07:26Z")

</div>

Thank you for that - I remember reading that `ansible.builtin.package` was just a wrapper, but it didn’t “click” at the time. Too many things going on at once, and not enough coffee in the world, I guess.

In my case, I need it to work with `ansible.builtin.yum` (for CentOS/RHEL 7), `ansible.builtin.dnf` (for RHEL 9), and `ansible.builtin.apt` (for Debian 12).

What ended up working was something very similar to this (lists of package names and a few other company-specific things removed, but this should be enough for others to see what’s happening) …

```yaml
# ROLE/vars/main.yml
---
pkg_min :
  CentOS7 :
    ca-certificates : '2023.2.60_v7.0.306-72.el7_9'
  RedHat7 :
    ca-certificates : '2023.2.60_v7.0.306-72.el7_9'
  RedHat9 :
    ca-certificates : '2023.2.60_v7.0.306-90.1.el9_2'
  Debian12 :
    ca-certificates : '20230311'

```

```yaml
# ROLE/tasks/main.yml
---
- name : "Install/update packages with minimum versions"
  vars :
    os : "{{ ansible_facts.distribution }}"
    maj : "{{ ansible_facts.distribution_major_version }}"
    osmaj : "{{ os ~ maj }}"
    pkg : "{{ pkg_min[osmaj] }}"
    dfv : "{{ ansible_facts.distribution_file_variety }}"
    gt : "{{ ( dfv == 'Debian' ) | ternary( '>=' , ' >= ' ) }}"
  loop : "{{ pkg | d( {} , true } | dict2items }}"
  ansible.builtin.package :
    name : "{{ item.key ~ ge ~ item.value }}"
    state : present

```

The final detail was the fact that `yum` and `dnf` require spaces between the package name, operator, and version, while `apt` requires that there NOT be spaces between them. I handled this with the `gt` variable, which either does or does not include the spaces.

---

<div class="post-metadata">

**Author:** ![jms1](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/jms1/32/2255_2.png) [@jms1](https://forum.ansible.com/u/jms1)\
**Post date:** [February 14, 2024, 12:31pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/4 "2024-02-14T12:31:46Z")

</div>

One thing I’ve noticed is that doing it this way makes the target machine run a `yum` or `apt` command for every package name on the list, which can be rather slow.

I had already figured out a more convoluted way, using `ansible.builtin.package_facts` to figure out what’s already on each machine, breaking apart the (epoch,version,release), and doing the (hopefully) correct comparisons in a `when:` clause, so the only time a `yum`/`apt` command is run on a target machine is if the package _needs_ to be installed or updated. The Ansible “code” for this isn’t pretty to look at, but it seems to work and it runs a LOT more quickly than what I showed above.

Which leads me back to my original question … is there a way to compare version numbers, as a test? Or do I still need to bite the bullet, learn python, and update `ansible.builtin.version` to support `version_type : rpm`?

---

<div class="post-metadata">

**Author:** ![mkrizek](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/mkrizek/32/2055_2.png) [@mkrizek](https://forum.ansible.com/u/mkrizek)\
**Post date:** [February 14, 2024, 1:56pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/5 "2024-02-14T13:56:07Z")

</div>

It is recommended to provide a list of packages to packaging modules so the underlying package managers can process them in a batch (and satisfy the input in the most efficient way) rather than looping over each package and processing them individually, as you have identified.

To your example, IMHO it seems like you are duplicating the work that the package managers should do. Is there a reason not to use the example @flowerysong provided but extend it to be distribution specific? Roughly:

```auto
- hosts: my_hosts
  vars:
    pkgs:
      CentOS7:
        - "ca-certificates >= 2023.2.60_v7.0.306-72.el7_9"
      RedHat7:
        - "ca-certificates >= 2023.2.60_v7.0.306-72.el7_9"
      RedHat9:
        - "ca-certificates >= 2023.2.60_v7.0.306-90.1.el9_2"
      Debian12:
        - "ca-certificates>=20230311"
  tasks:
    - package:
        name: "{{ pkgs[ansible_distribution~ansible_distribution_major_version] }}"

```

The advantage is that there is almost no templating logic needed and it is more generic as you can provide, say, just package names without version constraints like `vim` in the list.

---

<div class="post-metadata">

**Author:** ![jms1](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/jms1/32/2255_2.png) [@jms1](https://forum.ansible.com/u/jms1)\
**Post date:** [February 14, 2024, 2:11pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/6 "2024-02-14T14:11:10Z")

</div>

Because [the documentation](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_module.html#parameter-name) doesn’t mention that you CAN use a list of names like that. All it says for the `name` parameter’s data type is “string / required”.

There seems to be a LOT of oversights like this in the Ansible documentation. I’ll be honest, Ansible was not my choice (especially after working with Puppet for 11 years), but I’m dealing with it as best I can. Poor documentation like this is one of the reasons that, after four months, I’m _still_ not a fan of Ansible. I _can_ work with it, I just don’t _like_ it.

And I STILL think that `ansible.builtin.version` needs to support comparing RPM version strings.

---

<div class="post-metadata">

**Author:** ![shertel](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/shertel/32/1241_2.png) [@shertel](https://forum.ansible.com/u/shertel)\
**Post date:** [February 14, 2024, 3:19pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/7 "2024-02-14T15:19:56Z")

</div>

The package documentation covers the common features of different backends, but the underlying package manager modules often support and document additional features (like allowing a list of packages).

---

<div class="post-metadata">

**Author:** ![shertel](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/shertel/32/1241_2.png) [@shertel](https://forum.ansible.com/u/shertel)\
**Post date:** [February 14, 2024, 3:38pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/8 "2024-02-14T15:38:55Z")

</div>

This is the option to select the backend module [ansible.builtin.package module – Generic OS package manager — Ansible Documentation](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_module.html#parameter-use). By default it’s auto-detected, but you could look up the documentation for the module it selects by looking at the playbook output.

For example, `apt` module accepts a list of packages: [ansible.builtin.yum module – Manages packages with the yum package manager — Ansible Documentation](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html#parameter-name)  
`yum` accepts a list of packages: [ansible.builtin.yum module – Manages packages with the yum package manager — Ansible Documentation](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html#parameter-name)  
etc

---

<div class="post-metadata">

**Author:** ![jms1](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/jms1/32/2255_2.png) [@jms1](https://forum.ansible.com/u/jms1)\
**Post date:** [February 14, 2024, 3:53pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/9 "2024-02-14T15:53:35Z")

</div>

I found that … and luckily, `dnf` (for RHEL 9) also supports `name` as an array, so for my purposes I’m good - and maybe later today I’ll have time to try _using_ a list of “package” or “package \>= N” or “package\>=N” strings to see how that goes.

Assuming it works (and is faster), my one concern is that the output won’t have a list of which packages it installed or upgraded, which is likely to make the team who manages the production servers, a bit nervous. They _like_ being able to see what (Puppet, Ansible, whatever) is doing (and I would too, if I were on that team).

---

<div class="post-metadata">

**Author:** ![jrglynn2](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@jrglynn2](https://forum.ansible.com/u/jrglynn2)\
**Post date:** [February 14, 2024, 4:11pm UTC](https://forum.ansible.com/t/how-to-compare-rpm-versions/3794/10 "2024-02-14T16:11:07Z")

</div>

I have never used this myself, but check out this thread here on the forum, it talks about modifying what the ansible output shows ==\> [Ansible Output Label](http://forum.ansible.com/t/ansible-output-label/3786) \<== maybe that can help display what you want.

Or, if that doesn’t work or is too complicated, you can always add some steps to read the `/var/log/yum.log` (or the corresponding log if not `yum`), and then echo to the screen/ansible log what was installed today.
