How to override a yum module state for multiple packages using environment variable in Ansible? - 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.

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

ansible yum remove packages on first run, not on all after

I'm attempting to uninstall a list of packages from our RHEL servers. However, I need to account for servers where these packages to uninstall are needed for the application. An good example of this is httpd, which is listed on our uninstall list, but it is an dependency for the application running on the server. Basically I'm managing two states with one playbook.
So here is the list of packages to remove, which is in the role's defaults/main.yml
packagesRemove:
- telnet
- nfs
- nfs-server
- nfs-utils
- named
- httpd
- rsync
- postfix
- autofs
- cups
- smb
- squid
Currently, I'm doing something basic to uninstall the packages on the first run.
- name: Check for packageRemove file
stat:
path: /root/packageRemove.txt
register: stat_result
- name: remove packages not needed
yum:
name: "{{ packagesRemove }}"
autoremove: yes
register: packageRemove_output
when: not stat_result.stat.exists
- name: create packageRemove file
template:
src: output.txt.j2
dest: /root/packageRemove.txt
owner: root
group: root
mode: 0600
when: not stat_result.stat.exists
Basically if the /root/packageRemove.txt file exits, these tasks just get skipped. How can I make this more dynamic, and remove the need for the /root/packageRemove.txt file. I would like to make the packages that are needed into some sort of inventory variables.
Right now, I just have the following to gather a list of packages installed on the server.
- name: gather installed packages
dnf:
list: installed
no_log: true
register: yum_packages
- name: make installed packages a list
set_fact:
installed_packages: "{{ yum_packages.results | map(attribute='name') | list }}"
This is now where I'm stumped, and I'm not quite sure what my next step should be or if I'm on the right track. Any help would be great.

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

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.

Ansible yum module to install a list of packages AND remove any other packages

I have to deal with new machines (same OS version on all) that have been previously managed manually by many different admins.
The purpose is to use Ansible to make all these machines sharing the same list of installed packages,
AND remove any packages not in the list that might be installed already.
Is this feasible with Ansible ?
vars:
- yum_rpm:
- tcpdump
- tmux
- psacct
tasks:
- name: "Install all package in our list"
yum:
name: "{{ yum_rpm }}"
state: absent
update_cache: no
- name: "Remove any other unexpected package already installed"
## NO IDEA
Building up on #gary lopez answer to add security and performance.
First you will need to get an actual list of all packages you want to see installed on your final machine, including the default ones that come with the system. I assume that list will be in var yum_rpm
Once you have that, the next step is to get the list of currently installed packages on the machine. To create an actual list we can reuse:
- name: Get installed packages
yum:
list: installed
register: __yum_packages
- name: Make installed packages a list of names
set_fact:
installed_packages: "{{ __yum_packages.results | map(attribute='name') | list }}"
From there, adding and removing is just a matter of making a difference on lists. The goal here is to avoid looping on the yum module package by package (because it is damn slow and listed as a bad practice on the module documentation page) and to make the install and remove operations in one go.
- name: align packages on system to expected
yum:
name: "{{ item.packages }}"
state: "{{ item.state }}"
loop:
- packages: "{{ yum_rpm | difference(installed_packages) }}"
state: present
- packages: "{{ installed_packages | difference(yum_rpm) }}"
state: absent
when: item.packages | length > 0
In the first task you need to use state: present. You could try this
vars:
- yum_rpm:
- tcpdump
- tmux
- psacct
tasks:
- name: "Install all package in our list"
yum:
name: "{{ yum_rpm }}"
state: present
update_cache: no
- name: Get packages installed
yum:
list: installed
register: __yum_packages
- name: "Remove any other unexpected package already installed"
yum:
name: "{{ item.name }}"
state: absent
with_items: "{{ __yum_packages.results }}"
But I recommend you validate packages to uninstall because you could uninstall some packages required for your OS.

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 }}"

Resources