Ansible how to install a package only if same version not installed - ansible

I am trying to check the existing version of the package and run the install task if the same version is not been installed already.
Below is the code I am trying.
- name: Check for existing mono installation
command: "mono --version"
register: current_mono
ignore_errors: true
- name: Running "make install" for Mono
command: make install
args:
chdir: "{{ mono_install_dir }}"
become: yes
when: "mono_version|string not in current_mono.stdout"
First time this will fail because there won't be a stdout in current_mono var.
How to achieve this while running for the first time?

Since you are using make install you are using shell modules.
vars:
software_version: "1.2.3"
tasks:
First time this will fail ...
This is not absolutely necessary when using the following approach
- name: Check for existing version
shell:
cmd: software --version
warn: false
register: result
changed_when: false
failed_when: false
Please take note that some software packages like Java or Python are reporting his version to stderr.
- name: Show result
debug:
msg: "{{ result.stderr }}"
Now you can run your installer.
- name: Install latest version
shell:
cmd: "echo 'installing ...'"
warn: false
register: result
when: "software_version | string not in result.stderr"
- name: Show result
debug:
msg: "{{ result.stdout | default('was on latest version') }}"
You could test this sample playbook by using java or python as software.

Related

ansible-playbooks - install a list of apt packages from a file

I'm trying create a Ansible playbook that will read contents of a file and use those contents to install packages on a target machine.
In simpler terms, I want to run this command converted to an ansible playbook
cat ./meta/install-list/apt | xargs apt install -y
./meta/install-list/apt
neofetch
tmux
git
./ansible/playbooks/apt.yaml
- hosts: all
become: true
tasks:
- name: Extract APT packages to install
command: cat ../../meta/install-list/apt
register: _pkgs
delegate_to: localhost
run_once: true
- name: Install APT packages
apt:
name: "{{ _pkgs.stdout_lines }}"
state: latest
./ansible.cfg
[defaults]
inventory = ./ansible/inventory/hosts.yaml
./ansible/inventory/hosts.yaml
---
all:
children:
group-machines:
hosts:
target-machine.local
Command to run playbook
ansible-playbook --ask-become-pass ./ansible/playbooks/apt.yaml --limit group-machine
When running the command, it gets stuck on Extract APT packages to install
NOTE:
these files mentioned above are to be only on machine that is running the command. If possible, I'd like to prevent copying files to target machines and then running the playbooks tasks
PS: new to ansible
I don't see anything in your "Extract APT packages to install" task that should cause it to get stuck... but you don't need that task in any case; you can combine your two tasks into a single task like this:
- hosts: all
become: true
tasks:
- name: Install APT packages
apt:
name: "{{ packages }}"
state: latest
vars:
packages: "{{ lookup('file', '../../meta/install-list/apt').splitlines() }}"
Here we're using a file lookup to read the contents of a file. Lookups always run on the local (control) host.
Note that you could write the above like this as well...
- hosts: all
become: true
tasks:
- name: Install APT packages
apt:
name: "{{ lookup('file', '../../meta/install-list/apt').splitlines() }}"
state: latest
...but I like to keep longer jinja expressions in vars in order to keep the rest of the arguments more readable.
The above answer is more than enough by #Zeitounator. But if you do some formatting to your original file of package list as below
packages:
- neofetch
- tmux
- git
After that you can simply run the playbook like below
- hosts: all
become: true
vars_files: ../../meta/install-list/apt
tasks:
- name: Install APT packages
apt:
name: "{{ packages }}"
state: latest
Now suppose if you are lazy enough to not want to do the formatting then below playbook also will do the trick. Its much cleaner and scalable in my opinion.
---
- name: SHow the packages list
hosts: localhost
become: true
tasks:
- name: View the packages list file
shell: cat ../../meta/install-list/apt
register: output
- name: Install the package
apt:
name: "{{ output.stdout_lines }}"
state: latest

How to get latest package version with Ansible

I'm trying to replicate the command yum check-update package_name preferably with the Ansible yum module.
It provides an information to what version package would be updated after yum update execution (or ansible equivalent). For example:
root#host: $ yum check-update kernel
[...]
kernel.x86_64 3.10.0-1160.80.1.el7
[...]
root#host: $
I tried various combination of
- name: Xyz
ansible.builtin.yum:
list: updates
update_cache: true
But I can't limit it to a single package or pattern (like java*).
What I ended up with is ugly and slow (because of the download) workaround:
- name: Check latest available xyz version
yum:
name: xyz
state: latest
download_only: true
become: true
register: _result
- name: Register xyz version
set_fact:
latestXyz: "{{ _result.changes.updated[0][1] | regex_search('xyz-(.+).x86_64.*', '\\1') }}"
Is there any better way to achieve this?
One pretty easy way to achieve this is to run the yum module as you would to update the package, but enforce a dry run on it, with the check_mode option, and register the result of this task.
Inspecting the result, you'll realise that the module will list you the current version of the package and the version it would have been updated to, would it not have been run in dry run.
Given, for example
- yum:
name: expat
state: latest
check_mode: true
register: yum_latest_versions
Will populate the variable yum_latest_versions on a fedora:35 container with:
yum_latest_versions:
ansible_facts:
pkg_mgr: dnf
changed: true
failed: false
msg: 'Check mode: No changes made, but would have if not in check mode'
rc: 0
results:
- 'Installed: expat-2.5.0-1.fc35.x86_64'
- 'Removed: expat-2.4.9-1.fc35.x86_64'
Then to extract, you can indeed use a regex to search in the result:
- debug:
msg: >-
{{
(
yum_latest_versions.results
| map('regex_search', '^Installed: expat\-(.*)\.x86_64$', '\1')
).0.0
}}
Finally yields:
msg: 2.5.0-1.fc37

How to override a yum module state for multiple packages using environment variable in Ansible?

This is my code currently -
- name: software
hosts: localhost
tasks:
- name: install packages
yum:
name:
- ansible
- docker
state: latest
become: yes
So when I run this I get the latest ansible and docker installed.
What I want is for the default value of state to remain latest, so if I just run the playbook the latest versions are downloaded, as it is now. However I want a way for me to override the state for one or both using environment variables(extra vars) when running my playbook from the command line.
So I can choose what version of ansible or docker to install.
Is there a way?
Although I do not think this is the best way to manage software version and that the I consider the following a bit ugly, here is a in-a-nutshell example to get you on track for your experimentation (untested, you may have to adapt a bit):
---
- name: software
hosts: localhost
vars:
ansible_raw_suffix: "-{{ ansible_yum_version | default('') }}"
ansible_suffix: "{{ ansible_yum_version is defined | ternary(ansible_raw_suffix, '') }}"
docker_raw_suffix: "-{{ docker_yum_version | default('') }}"
docker_suffix: "{{ docker_yum_version is defined | ternary(docker_raw_suffix, '') }}"
tasks:
- name: install packages
yum:
name:
- "ansible{{ ansible_suffix }}"
- "docker{{ docker_suffix }}"
state: "{{ yum_state | default('present') }}"
become: yes
With the above, you can:
install to the latest version if first time install
ansible-playbook software.yml
install a specific version of one or both softwares:
ansible-playbook software.yml -e ansible_yum_version=2.9.2 -e docker_yum_version=20.10.6
upgrade to the lastest version
ansible-playbook software.yml -e yum_state=latest
I will let you go on with this to add more features (e.g. allow downgrades) if you feel you still want to walk that path.

unpacking yaml values in the ansible playbook

i'm a fresher in the professional world as i just joined amid corona situation as being work from i have been involved to understand and write ansible codes and at some level i am growing up by watching through SO posts to get variety of ticks & tricks.
I have the below ansible playbooks..
1- One is custom_pkgs.yml which basically installing some custom build packages using yum command where it calling a pkgs.yml file which lists the packages to be install.
I somewhat understood the code but
$ vi custom_pkgs.yml
---
- name: Install License
hosts: all
become: yes
become_user: root
become_method: sudo
tasks:
- name: Include the variables to install the license software
include_vars:
file: "vars/pkg.yml"
name: license
- name: Install license software
shell: "yum install -y {{ license[ item ] | join(' ') }}"
with_items: "{{ license }}"
changed_when: True
when: item != "remove"
- name: Remove any unwanted RPMS
shell: "yum remove -y {{ license.remove | join(' ') }}"
changed_when: True
when: license.remove is defined
...
Below is the pkg.yml
$ cat pkg.yml
---
license:
- fenixlmd.noarch
- tmpwatch
- xorg-x11-deprecated-libs.i386
- Tasking.noarch
- rotate_fix.noarch
- plexim.noarch
- interrad.noarch
- idsd.noarch
- gsi.noarch
- java-1.8.0-openjdk
- java-1.8.0-openjdk-devel
- java-1.8.0-openjdk-debug
- flexnet_agent
- magillem.noarch
- redhat-lsb-printing
- redhat-lsb-printing.i686
- redhat-lsb-core
- redhat-lsb-core.i686
- redhat-lsb
- redhat-lsb.i686
- git
- gcc
- python-devel
...
What i would like to Know:
I am trying to understand about below two lines..
shell: "yum install -y {{ license[ item ] | join(' ') }}" and when: item != "remove"
I have went through all the basics of asking question in SO in case i've ask something out of way i would like to be excused as this is my first post.
Regards ..
Thanks for learning Ansible, this is an old Ansible code. The pkg.yml file defines a list variable called licence containing a list of yum package to install on the remote host using the Ansible shell module. with_items: "{{ license }}" tell the module to iterate on that variable items.
The When condition help to skip the package when his name is remove.
Read the Ansible conditional documentation: https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#the-when-statement
But as i told, it's an old code. On Ansible recent version, you will use a yum module to install yum package (create two lists of package: package_to_install and package_to_remove
- name: Install License
hosts: all
become: yes
become_user: root
become_method: sudo
tasks:
- name: Include the variables to install the license software
include_vars:
file: "vars/pkg.yml"
name: license
- name: Install license software
yum:
name: "{{ package_to_install }}"
changed_when: True
when: item != "remove"
- name: Remove any unwanted RPMS
yum:
name: "{{ package_to_remove }}"
state: absent
changed_when: True
Make sure to use a recent Ansible version (2.9).
Read the yum module documentation: https://docs.ansible.com/ansible/latest/modules/yum_module.html
You can Also defined only one list of package variable with two fields:
license:
- { name: fenixlmd.noarch, state: present }
- { name: tmpwatch, state: absent }
- { name: xorg-x11-deprecated-libs.i386, state: present }
.
.
.
And then use the yum module like that:
- name: Install License
hosts: all
become: yes
become_user: root
become_method: sudo
tasks:
- name: Include the variables to install the license software
include_vars:
file: "vars/pkg.yml"
name: license
- name: Remove any unwanted or install needs package RPMS
yum:
name: "{{ item.name }}"
state: "{{ item.state }}"
with_items: "{{ licence }}"

Don't mark something as changed when determining the value of a variable

I have the following role in my Ansible playbook to determine the installed version of Packer and conditionally install it if it doesn't match the version of a local variable:
---
# detect packer version
- name: determine packer version
shell: /usr/local/bin/packer -v || true
register: packer_installed_version
- name: install packer cli tools
unarchive:
src: https://releases.hashicorp.com/packer/{{ packer_version }}/packer_{{ packer_version }}_linux_amd64.zip
dest: /usr/local/bin
copy: no
when: packer_installed_version.stdout != packer_version
The problem/annoyance is that Ansible marks this step as having "changed":
I'd like gather this fact without marking something as changed so I can know reliably at the end of my playbook execution if anything has, in fact, changed.
Is there a better way to go about what I'm doing above?
From the Ansible docs:
Overriding The Changed Result New in version 1.3.
When a shell/command or other module runs it will typically report
“changed” status based on whether it thinks it affected machine state.
Sometimes you will know, based on the return code or output that it
did not make any changes, and wish to override the “changed” result
such that it does not appear in report output or does not cause
handlers to fire:
tasks:
- shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"
# this will never report 'changed' status
- shell: wall 'beep'
changed_when: False
In your case you would want:
---
# detect packer version
- name: determine packer version
shell: /usr/local/bin/packer -v || true
register: packer_installed_version
changed_when: False
- name: install packer cli tools
unarchive:
src: https://releases.hashicorp.com/packer/{{ packer_version }}/packer_{{ packer_version }}_linux_amd64.zip
dest: /usr/local/bin
copy: no
when: packer_installed_version.stdout != packer_version

Resources