Ansible Perform Tasks in localhost on Multiple Hosts - ansible

I'm planning an Ansible playbook which will query version number of specific .exe file on windows hosts, then generate diff version between the version installed on the Windows hosts and the latest version stored on the Ansible controller Linux machine ("localhost"), and deploy the diff version to the Windows hosts.
My playbook looks something like:
- hosts: winClients
gather_facts: False
tasks:
- name: check exe file version
win_file_version:
path: C:\my.exe
register: exe_file_version
- name: Set client version
set_fact:
winClientExeVersion: "{{ exe_file_version.win_file_version.file_version }}"
Then, on localhost, I have folder for each version, and I'd like to generate the diff between the latest version and the version on the winClients. The versions are stored on localhost in folders named with the version numbers, e.g. MyVersions/1.0.0.0/abc.exe, abc.dll, aaa.txt, ... and MyVersions/1.0.0.1/abc.exe, abc.dll, aaa.txt, ... etc. And I have special folder, MyVersions/LatestVersion/ that always contains the latest version. So I need something like:
- hosts: localhost
gather_facts: False
tasks:
- name: check latest version on server using PEV
shell: peres -v /home/user/MyVersions/LatestVersion/My.exe | awk '{print $3}'
register: latest_file_version
- name: Set server version
set_fact:
serverLatestVersion: "{{ latest_file_version.stdout }}"
- name: If versions differ, generate diff between client and latest versions to temp DiffFolder, then delete empty folders
shell: rsync -rvcm --compare-dest=../{{hostvars.winClients.winClientExeVersion}} . ../DiffFolder && find ../DiffFolder -depth -type d -empty -delete
args:
chdir: /home/user/MyVersions/LatestVersion
when: serverLatestVersion != hostvars.winClients.winClientExeVersion
Then I copy the generated diff to the Windows clients using win_copy.
Now, all this works OK when winClients represents only one specific client.
My problem is, how to do it for group of clients, i.e. in case when winClients represents a group instead of one specific computer? How can I generate diff for each client separately (and each client might have different version installed on it), based on its previously-retrieved version number, when its version number differs from the latest version on server?
The winClients upper section will assign version number for each of the clients, the problem is with the localhost tasks and their when condition.

So it turns out to be pretty simple: with_items does the trick, and it can be referred later in when as well.
Example:
- hosts: localhost
gather_facts: False
tasks:
- name: Notify if versions differ
debug:
msg: "client and server versions differ: Server: {{ serverLatestVersion}}, Client: {{ hostvars[item]['winClientExeVersion'] }}"
with_items: "{{ groups['clients'] }}"
when: serverLatestVersion != hostvars[item]['winClientExeVersion']
This will loop through the clients in clients group, and for each client that got a different version than the server, notify in debug.
Similar method can be used for each task (copy, rsync, etc.) that requires to be executed only against relevant client, based on when condition.

Related

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.

How to stop executing roles if the OS versions are different with in inventory

I have two different Centos versions 6.x and 7.x in my Inventory. I am able to print OS versions using below code
- name: Get OS details
debug: msg="{{ item }}"
with_items:
- "{{ ansible_distribution_version }}"
How to stop executing remaining roles if OS version is 7 instead of 6 or vice versa? I have all my inventory in one file.
If i'm running the code against version 6 then it needs to print the hostnames if it's not version 6 and stop executing remaining roles.
You can use the meta option, end_play, for example
- name: end play if not centos6
meta: end_play
when: ansible_distribution_version == "centos6"
More information in https://docs.ansible.com/ansible/latest/modules/meta_module.html
what you probably want to do is split your setup tasks into tasks/centos6.yml and tasks/centos7.yml and only include the relevant file in your playbook, for example
- name: setup centos7
include: tasks/centos7.yml
when: ansible_distribution_version == centos7

Ansible dictionary - Package validation by host, name and version

I have several packages (package-A, package-B, ...) installed through various hosts (host1, host2, ...).
I need to check if package-A with version 1.2.3, for example, is installed in host1, host2 and host3, need to check if package-B with version 3.2.1 is installed in host1, host3 and host9, ..., informing the user that they are installed or not with the specified version.
I have a playbook (validatorPlaybook.yml) who's purpose is to check and inform the user about installed versions in certain hosts defined in a dictionary in an outside playbook varsPlayBook1.yml:
#validation dictionary
packsDict:
pck1:
hosts: host1, host2, host3
version: 1.2.3
package_name: package-A
pck2:
hosts: host1, host3, host9
version: 3.2.1
package_name: package-B
(...)
This is the script I'm using in validatorPlaybook.yml:
---
- name: check if packages in dict are with the correct version
yum:
list="{{ item.package_name }}-{{ item.version }}"
loop: "{{ lookup('dict', packsDict) }}"
...
As you can see some packages are installed in one host and others are in another.
How can I run that yum list command by host? Is it possible?
Note: I will not discuss here the overall structure and goal of your current implementation. I think it can be greatly improved (by defining packages to check in inventory for example...)
The easiest solution I see from the existing script requires to transform your validation data a bit. Put the hosts in a list like so:
packsDict:
pck1:
hosts:
- host1
- host2
- host3
version: 1.2.3
package_name: package-A
pck2:
hosts:
- host1
- host3
- host9
version: 3.2.1
package_name: package-B
I take for granted that your actual playbook already runs on all needed hosts and that the names inside your validation dict are aligned to the names of hosts in your inventory. Modifying your task like the following should meet your requirement.
- name: check if packages in dict are with the correct version
yum:
list: "{{ item.package_name }}-{{ item.version }}"
loop: "{{ lookup('dict', packsDict) }}"
when: inventory_hostname in item.hosts

Ansible Version Forcing

We have multiple versions of ansible(2.3,2.3.0,2.8.6) we want to use a particular version '2.8.6'.
But whenever ansible execution starts, it uses a different version 2.4.2.0. If we check ansible --version it is ansible-playbook 2.4.2.0.
Is ansible and ansible-playbook versions are same?
How to force a version in ansible execution?
Please give your inputs.
Ansible is well-known to subtly break compatibility around variables, roles, includes and delegation. Therefore, it's considered to be the best practice to assert an Ansible version in a playbook before doing anything.
There are few things to assure:
Those checks should survive --tags option.
Those checks should survive --limit
Those checks should be relatively fast and cause as less verbosity as possible.
My current solution is:
Create playbook version_check.yaml
- import_playbook: version_check.yaml at the beginning of each playbook. (each, this is important, trust me).
version_check.yaml content (ansible 2.5+):
- hosts: all, localhost
gather_facts: false
run_once: true
tasks:
- name: Check ansible version
assert:
that:
- "ansible_version.full is version('2.8.0', '>=')"
- "ansible_version.full is version('2.9.0', '<')"
msg: >
Use Ansible to 2.8 to run this playbook
delegate_to: localhost
tags:
- always
As you can see, there are a lot here.
run_once do this once per Ansible run.
gather_facts: false speed it up a bit.
It is assigned to 'all, localhost', basically guaranee that it's in Ansible runlist regardless of any --limit
It has tag always almost guarantee to run in the case of use --tags. The single uncovered hole here is to use --skip always (which is insane).
There is an older answer with older syntax for versions (below 2.5):
- hosts: all, localhost
gather_facts: false
run_once: true
tasks:
- name: Check ansible version
assert:
that:
- "ansible_version.full | version_compare('2.8.0', '>=')"
- "ansible_version.full | version_compare('2.9.0', '<')"
msg: >
Use Ansible to 2.8 to run this playbook
delegate_to: localhost
tags:
- always
It's hard to say which version to use. If you are afraid of very old ansible, version_compare is better than version, but if you are sure it's at least 2.5+, you can use newer version syntax.

How to define when condition based on matching string

I am writing a playbook where i need to select host which will be a part of group which starts with name "hadoop". The host will be supplied as an extra variable in term of parent group. The task is about upgrading the java on all machines with repo but there are certain servers which dont have repo configured or are in dmz and can only use there local repo... i need to enable local_rpm:true so that when the playbook execute the server which belong to hadoop group have this fact enabled.
I tried like below :
- hosts: '{{ target }}'
gather_facts: no
become: true
tasks:
- name: enable local rpm
set_fact:
local_rpm: true
when: "'hadoop' in group_names"
tags: always
and then importing my role based on tag
It's probably better to use group_vars in this case.
https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#group-variables

Resources