How can I make Ansible execute a shell script if a (rpm) package is not installed? Is it somehow possible to leverage the yum module?
7 Answers
I don't think the yum module would help in this case. It currently has 3 states: absent, present, and latest. Since it sounds like you don't want to actually install or remove the package (at least at this point) then you would need to do this in two manual steps. The first task would check to see if the package exists, then the second task would invoke a command based on the output of the first command.
If you use "rpm -q" to check if a package exists then the output would look like this for a package that exists:
# rpm -q httpd
httpd-2.2.15-15.el6.centos.1.x86_64
and like this if the package doesn't exist:
# rpm -q httpdfoo
package httpdfoo is not installed
So your ansible tasks would look something like this:
- name: Check if foo.rpm is installed
command: rpm -q foo.rpm
register: rpm_check
- name: Execute script if foo.rpm is not installed
command: somescript
when: rpm_check.stdout.find('is not installed') != -1
The rpm command will also exit with a 0 if the package exists, or a 1 if the package isn't found, so another possibility is to use:
when: rpm_check.rc == 1

- 19,995
- 8
- 63
- 73
-
13The problem with the command is, if `rpm -q` exits with 0 (no package found), the playbook will fail. To prevent that append: `ignore_errors: True` to the task in order to use the output in subsequent tasks to install the rpm. – romanofski Aug 28 '14 at 03:40
-
6Additionally, you may want to add `changed_when: no` to force the `command` module not to say something has changed. – Alicia Oct 18 '14 at 19:22
-
3Also, to avoid the failure message when the package is not found you can set `failed_when: rpm_check.rc > 1`. – Alicia Oct 18 '14 at 19:28
-
1Wouldn't it be `failed_when: rpm_check.rc = 1` I tried this and on a failed RPM `rc` is always set to 1. I haven't seen a number higher than 1. – sdot257 Oct 01 '15 at 20:01
-
I tired it get this working using with_items but it doesn't seem to work http://serverfault.com/questions/747577/how-can-i-use-stdout-within-an-ansible-conditional-correctly – codecowboy Jan 08 '16 at 10:24
-
3With `ignore_errors: yes` it still counts as an error, so `failed_when: no` seems like a better option. – x-yuri Nov 18 '16 at 22:54
-
You can run tasks in check mode to avoid side effects. You can use yum module for this... Check my answer. – reegnz Oct 08 '17 at 12:04
Based on the Bruce P answer above, a similar approach for apt/deb files is
- name: Check if foo is installed
command: dpkg-query -l foo
register: deb_check
- name: Execute script if foo is not installed
command: somescript
when: deb_check.stdout.find('no packages found') != -1

- 2,311
- 2
- 23
- 23
-
4
-
1I'd like to point out that as of Ansible 2.1, the apt module has an option called `only_upgrade`, which is designed precisely for this end. The documentation describes the switch: 'Only install/upgrade a package if it is already installed.' – Erathiel May 11 '16 at 15:36
-
2@Erathiel I think what this playbook solves is run a "certain" task if the given package is found. So not necessarily about install a missing package, it could be something else. That `only_upgrade` option is for making sure the package is either installed or updated much like what happens with `state`. – JohnnyQ Jun 09 '16 at 06:34
-
@Erathiel The OP wants an if/then test. He/she doesn't state that they want to actually install anything. Cheers. – Jesse Adelman Dec 06 '17 at 22:21
-
1
If the package is installable through the system package manager (yum, apt, etc) itself, then you can make use of the check mode flag of ansible to register installation status without actually installing the package.
- name: check if package is installed
package:
name: mypackage
state: present
check_mode: true
register: mypackage_check
- name: run script if package installed
shell: myscript.sh
when: not mypackage_check.changed

- 837
- 9
- 16
-
5By using the `package` module instead of the specific `yum` one, this would make it the best answer as it would be working on more than RHEL or Debian family of Linux distributions. Well done! – Huygens Nov 08 '17 at 11:20
-
1This didn't work for me. If the package is not installed, Ansible errors out. I had to add "ignore_errors: yes" to the "check if package is installed" section. – Jesse Adelman Dec 06 '17 at 23:12
-
What ansible version? 2.3.2.0 is known to err out when you install from an RPM file. It got fixed with 2.4.0.0. – reegnz Dec 09 '17 at 11:46
-
When the package is not installed, the task `check if package is installed` reports a failure, when in fact it has run successfully and registered the variable `mypackage_check` <--- this variable is defined and is available to be used in successive tasks. I used a similar approach to the workaround invoked in the comment by @JesseAdelman: I added [`failed_when: False`](https://docs.ansible.com/ansible/2.6/user_guide/playbooks_error_handling.html#controlling-what-defines-failure) to the end of the `check if package is installed` task. – edesz Jul 26 '18 at 14:18
Related to this.
Putting everything together, complete playbook for Debian
(Ubuntu) which Updates package only if it's already installed:
---
- name: Update package only if already installed (Debian)
hosts: all
sudo: yes
tasks:
- name: Check if Package is installed
shell: dpkg-query -W -f='${Status}' {{ package }} | grep 'install ok installed'
register: is_installed
failed_when: no
changed_when: no
- name: Update Package only if installed
apt:
name: {{ package }}
state: latest
update_cache: yes
when: is_installed.rc == 0
Sadly Ansible still hasn't built-in support for making simple package updating, see ex: https://github.com/ansible/ansible/issues/10856

- 1,465
- 1
- 13
- 17
-
1As [noted below](https://stackoverflow.com/a/36405647/2192488), using `shell: dpkg --status {{ package }} |grep 'install ok installed'` is more robust than using `command: dpkg-query -l {{ package }}`. – Serge Stroobandt Jun 12 '17 at 11:23
Since Ansible 2.5, you can use the package_facts module:
- name: Gather package facts
package_facts:
manager: auto
- name: Debug if package is present
debug:
msg: 'yes, mypackage is present'
when: '"mypackage" in ansible_facts.packages'
- name: Debug if package is absent
debug:
msg: 'no, mypackage is absent'
when: '"mypackage" not in ansible_facts.packages'
Note: you need the python bindings for apt/rpm installed on the target, e.g. python-apt
for Debian.

- 799
- 6
- 8
You shouldn't be using dpkg -l package
because it has no idea if your package has been removed or is still installed.
Instead it's probably better to use dpkg -s package
.
To check if the package is installed :
- shell: dpkg -s package | grep 'install ok installed'
or if you don't mind the package on hold or other states :
- shell: dpkg -s package | grep 'installed'
This return 0 when installed and 1 if not.
(It's important to use the shell as we are using a pipe |
)

- 195
- 1
- 7
-
i had to change it to `shell: dpkg -s package | grep -c 'install ok installed'` in my case on Debian grep didn't return 0 or 1. With the `-c` option it returns the number of lines found. – luukvhoudt Feb 13 '20 at 20:20
I find using shell or command module is not "ansiblic".
I prefer to use yum module and json_query filter to check if a package is already installed. E.g. httpd package :
- yum:
list: httpd
register: apache_service
- assert:
that:
- "'installed' in apache_service|json_query('results[*].yumstate')"
msg: 'httpd is not installed'

- 149
- 1
- 2