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:
- To avoid taking extra time and doing extra work
- To make the role idempotent (changes are only made if changes are needed)
- 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 specifychanged_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 specifyignore_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 thewhen
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.
Thank you, it helps me 🙂
I’m new to ansible and devops, however what I did for my upgrade playbook was :
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.
I was not aware of the
version
orversion_compare
test in Ansible, looks like a great option: Version Comparison in Ansible (Ansible Docs)Thanks Matt for the tip!
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
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