Using Ansible to check version before install or upgrade

One thing that I do frequently with an Ansible role is check to see if software is already installed and at the desired version. I do this for several related reasons:

  1. To avoid taking extra time and doing extra work
  2. To make the role idempotent (changes are only made if changes are needed)
  3. So that the play recap summary lists accurate results

I’m thinking particularly of software that needs to be unpacked, configured, compiled, and installed (rather than .rpm or .deb packages). In this example, I’ll be installing the fictional widgetizer software.

First I add a couple variables to the defaults/main.yml file for the role:

---
path_to_widgetizer: /usr/local/bin/widgetizer
widgetizer_target_version: 1.2
...

Next I add a task to see if the installed binary already exists:

- name: check for existing widgetizer install
  stat:
    path: "{{ path_to_widgetizer }}"
  register: result_a
  tags: widgetizer

Then, if widgetizer is installed, I check which version is installed:

- name: check widgetizer version
  command: "{{ path_to_widgetizer }} --version"
  register: result_b
  when: "result_a.stat.exists"
  changed_when: False
  failed_when: False
  tags: widgetizer

2 things to note in the above:

  • The command task normally reports changed: true, so specify changed_when: False to prevent this.
  • Although this task should only run if widgetizer is present, we don’t want the task (and therefore the entire playbook) to fail if it is not present. Specify failed_when: false to prevent this. (I could also specify ignore_errors: true, which would report the error but would not prevent the rest of the playbook from running.)

Now I can check the registered variables to determine if widgetizer needs to be installed or upgraded:

- name: install/upgrade widgetizer, if needed
  include: tasks/install.yml
  when: "not result_a.stat.exists or widgetizer_target_version is not defined or widgetizer_target_version not in result_b.stdout"
  tags: widgetizer

However, when I ran my playbook I received an error:

$ ansible-playbook -i hosts site.yaml --limit localhost --tags widgetizer

...

fatal: [localhost]: FAILED! => {"failed": true, "msg": "The conditional check 'not result_a.stat.exists or widgetizer_target_version is not defined or widgetizer_target_version not in result_b.stdout' failed. The error was: Unexpected templating type error occurred on ({% if not result_a.stat.exists or widgetizer_target_version is not defined or widgetizer_target_version not in result_b.stdout %} True {% else %} False {% endif %}): coercing to Unicode: need string or buffer, float found\n\nThe error appears to have been in '/home/chris/projectz/roles/widgetizer/tasks/install.yml': line 3, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: copy widgetizer source\n  ^ here\n"}

The key piece of information to note in that error message is:

need string or buffer, float found

We’ve supplied widgetizer_target_version as 1.2 (a floating point number), but Python/jinja2 wants a string to search for in result_b.stdout.

There are at least 2 ways to fix this:

  • Enclose the value in quotes to specify widgetizer_target_version as a string in the variable definition, e.g. widgetizer_target_version: "1.2"
  • Convert widgetizer_target_version to a string in the when statement, e.g. widgetizer_target_version|string not in result_b.stdout

After making either of those changes, the playbook runs successfully and correctly includes or ignores the install.yml file as appropriate.

5 thoughts on “Using Ansible to check version before install or upgrade”

  1. I’m new to ansible and devops, however what I did for my upgrade playbook was :

    ### Get old version of "software" .
      - name: check current software VERSION 
        shell: readlink -f /path/to/software | sed -e s'|/path/to/software||'
        register: OLD_VERSION 
    
    ### compare installation version.  end play if no upgrade is needed.     
      - block: 
          - debug: 
              msg: "Nothing to Upgrade, exiting upgrade"
          - meta: end_play
        when: OLD_VERSION.stdout | version_compare(VERSION,'>=')
    

    This will exit the play if there’s no software to install, OR, if the version you’re trying to install is less than what’s already on the box… if you’re doing multiple servers, it’ll skip this box and move to the next.

  2. HI,
    This is very informative script.
    I have one query and want to have simple script in ansible playbook like :
    I have several windows hosts and they running citrix client tool want to pull information (inventory) of installed version in my local ansible host for all those windows hosts that can be consolidated or one-by-one.
    I am ansible beginner and learner .
    Please let me know how can I accomplish this.

    Thanks,
    Tushar

  3. I am trying to update patches for the list of packages. Thanks in advance if someone helps me with comparing versions to validate if the patches are applied.

    For ex: Firefox 91.3.0 is already installed and I am patching for 91.4.0 which is not a major upgrade.

    I just need to validate comparing for all the packages in a loop that I am updating patches for

Leave a Reply

Your email address will not be published. Required fields are marked *