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

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

Related

How can I show a single fact in output from the ansible.builtin.package_facts in an AWX playbook

I am trying to gather what versions of nginx are running on instances in Openstack. I need to output if an instance has it or not, and if so, what version. I am failing miserably. Any help and patience would be greatly appreciated.
- name: Gather the package facts
ansible.builtin.package_facts:
manager: auto
- name: Check whether a package called nginx is installed
ansible.builtin.debug:
var: ansible_facts.packages['nginx']
msg: "Nginx version is ={{ ansible_version | type_debug }}"
when: "'nginx' in ansible_facts.packages"
register: op
Why you are printing ansible_version? You need to print nginx version
name: Gather the package facts
ansible.builtin.package_facts:
manager: auto
- name: Check whether a package called nginx is installed
ansible.builtin.debug:
msg: "Nginx version is ={{ ansible_facts.packages['nginx']}}"
when: "'nginx' in ansible_facts.packages"
register: op
I deleted also the var, you are not using it.

What is the best way to manage unsupported distros in an Ansible role?

An Ansible role supports Debian Stretch and Buster.
It is not able to do the job on Jessie or older versions.
Which is the best way to tell the user that the role cannot be used on a given old version?
Do nothing in main.yml file (controlling the distro version using when: declarations)
Let the role explicitly fail using the fail module
Do not check for a supported distro version and let tasks fail themselves
Developers should place the supported/tested versions in the Readme. Then users should always read the Readme. Then, common sense should be used.
But we all know that's not the case.
You could configure the host(s) which are too old skip to the role, to ensure the hosts do not execute any command for that role. But the way to go would be to built another role, or update that role, to let that playbook support that OS version.
This method is the least desired one: Do not check for a supported distro version and let tasks fail themselves. Because when you go down this path, then some unsupported tasks are executed on the host and then you can't guarantee the state of the system anymore. In short; you'll create a mess.
To simply prevent the nightmare, indeed, let the play fail:
- name: fail when using older version
fail:
msg: "You fail because reason, woohoo"
when: ansible_distribution is Ubuntu and ansible_distribution_version is 10.04
Q: "What is the best way to manage unsupported distros in an Ansible role?"
A: It's a good idea to end the host or play when the platform and version is not supported. In most cases, this means such a platform and version hasn't been tested yet. It's up to the user to add a new platform and version to the metadata, test it and optionally contribute to the development.
In a role, it's possible to read the variable galaxy_info from the role's file meta/main.yml and test the supported platforms and versions.
$ cat roles/role_1/meta/main.yml
galaxy_info:
author: your name
description: your role description
company: your company (optional)
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.9
platforms:
- name: Ubuntu
versions:
- bionic
- cosmic
- disco
- eoan
galaxy_tags: []
dependencies: []
For example the tasks in the role below
$ cat roles/role_1/tasks/main.yml
---
- name: Print OS and distro Ansible variables collected by setup
debug:
msg:
- "ansible_os_family: {{ ansible_os_family }}"
- "ansible_distribution: {{ ansible_distribution }}"
- "ansible_distribution_major_version: {{ ansible_distribution_major_version }}"
- "ansible_distribution_version: {{ ansible_distribution_version }}"
- "ansible_distribution_release: {{ ansible_distribution_release }}"
- name: Include roles' meta data
include_vars:
file: "{{ role_path }}/meta/main.yml"
- name: Test the distribution is supported. End the host if not.
set_fact:
supported_distributions: "{{ galaxy_info.platforms|json_query('[].name') }}"
- debug:
var: supported_distributions
- block:
- debug:
msg: "{{ ansible_distribution }} not supported. End of host."
- meta: end_host
when: ansible_distribution not in supported_distributions
- name: Test the release is supported. End the host if not.
set_fact:
supported_releases: "{{ (galaxy_info.platforms|
selectattr('name', 'match', ansible_distribution)|
list|first).versions }}"
- debug:
var: supported_releases
- block:
- debug:
msg: "{{ ansible_distribution_release}} not supported. End of host."
- meta: end_host
when: ansible_distribution_release not in supported_releases
- name: The distribution and release is supported. Continue play.
debug:
msg: "{{ ansible_distribution }} {{ ansible_distribution_release }} is supported. Continue play."
with the playbook
- hosts: localhost
gather_facts: true
roles:
- role_1
give
"msg": [
"ansible_os_family: Debian",
"ansible_distribution: Ubuntu",
"ansible_distribution_major_version: 19",
"ansible_distribution_version: 19.04",
"ansible_distribution_release: disco"
]
"supported_distributions": [
"Ubuntu"
]
"supported_releases": [
"bionic",
"cosmic",
"disco",
"eoan"
]
"msg": "Ubuntu disco is supported. Continue play."

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

Trying to split my Ansible playbook based on the Operating System

I have a playbook which needs to be run based on the operating System.
UseCase: Lets assume there is a service that is running.
On Linux we can check if it is installed and running using the
systemctl status application.service
While command and on windows we will be using the
sc query "ServiceName" | find "RUNNING"
Now we have to install it based on the output of the above a commands which requires us to segregate the playbook based on the OS.
Classic Example: Create a directory based on the OS
- name: Install QCA Agent on Linux targets
hosts: all
gather_facts: true
remote_user: root
tasks:
- name: Create Directory for Downloading Qualys Cloud Agent
sudo: yes
sudo_user: root
file:
path: /usr/q1/
state: directory
owner: root
group: root
mode: 0777
recurse: no
- name: Create Directory for Downloading Qualys Cloud Agent
win_file:
path: c:\q1
state: directory
owner: Administrator
group: Administrator
mode: 0777
recurse: no
The playbook will alwayz be successful only if one of the condition is met and it is whether it is Windows or Unix OS. I can alwayz add a condition which will prompt based on:
when: ansible_distribution == 'Redhat' or ansible_distribution == 'CentOS'
However what i would like to achieve is based on a condition it should trigger my playbook.yml file.
name: Load a variable file based on the OS type, or a default if not found. Using free-form to specify the file.
include_vars: "{{ item }}"
with_first_found:
- "{{ ansible_distribution }}.yaml"
- "{{ ansible_os_family }}.yaml"
- default.yaml
https://docs.ansible.com/ansible/2.5/modules/include_vars_module.html?highlight=with_first_found
I would like to know if there is a better example explaining the same that i could implement or if there are other ways to achieve the same.
Thank you,
The example you show from the Ansible docs is pretty much the best practice and is common in many playbooks (and roles for that matter) that deal with multiple OSes. If you have code that is different (instead of the variable example here), you'll be using include_tasks instead of include_vars, but the concept is the same.

Ansible playbook to install different packages depending on os version

I have an Ansible playbook (using ansible 1.9.3) that sets up and updates a bunch of servers. One of those steps is to install packages (using the 'apt' plugin). Until recently all of my servers have been uniform (same version of Ubuntu server). I'm introducing a newer Ubuntu server and some of package names have changed.
Here's what my tasks file looks like:
- name: install needed packages
apt: name={{ packages }} state=present update_cache=yes
(and I have a list of packages in a vars file).
I could define a variable in my inventory file for the hosts with a different Ubuntu version.
How do I change this task to use one list of packages for my current hosts, and another list of packages for my newer hosts?
Use a when clause. Here's an example that will only run on ubuntu 12.04
when: "'{{ ansible_distribution}}' == 'Ubuntu' and '{{ ansible_distribution_release }}' == 'precise'"
You could alternatively use:
when: "'{{ ansible_distribution }}' == 'Ubuntu' and '{{ ansible_distribution_version }}': '12.04'"
Edit:
Later versions of Ansible (e.g. 2.7.1) would print a warning for this statement:
[WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: '{{ansible_distribution}}' == 'Ubuntu' and '{{ ansible_distribution_release }}' == 'bionic'
Ansible now expects another syntax for conditional statements, as stated in the docs.
For Ansible 2.5 you could try this:
when: "ansible_distribution|string == 'Ubuntu' and ansible_distribution_release|string == 'bionic'"
or even:
when:
- ansible_distribution|string == 'Ubuntu'
- ansible_distribution_release|string == 'bionic'
The latest version of the docs suggests to use access the ansible_facts like this:
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6"
To keep your playbooks cleaner, you could do this within the roles, for example, you could have a single tasks/main.yml the one based on the OS it will include the tasks matching the condition used with when, for example, if using ansible_os_family the contents of main.yml could be something like:
---
- include_tasks: freebsd.yml
when: ansible_os_family == "FreeBSD"
tags:
- unbound
- include_tasks: debian.yml
when: ansible_os_family == "Debian"
tags:
- unbound
- include_tasks: redhat.yml
when: ansible_os_family == "RedHat"
tags:
- unbound
The advantage of this approach is that besides only having a set of task per OS, you can also tag the full tasks.
This can be done even simpler and more generic allowing re-use of the same task for multiple distributions or os:es
Simply storing the packages per distribution in a dictionary
vars file:
packages: {
bionic: [ bionic-package1, bionic-package2],
focal: [focal-package1, focal-package2]
}
playbook:
- name: Install unique packages per distro
package:
name: "{{ packages[ansible_distribution_release] }}"
state: present
Note that the change to "package" module from "apt" which allows task to run on any Linux distributions not only ones using apt as a package manager

Resources