3
votes

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?

3
"Here's what my tasks file looks like"... and it doesn't work, because using bare variables in with-clauses has been deprecated for over 2 years.techraf

3 Answers

9
votes

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

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.

0
votes

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
   apt: 
    name: {{ packages[ansible_distribution_release] }}"
    state: present

Even better by modifying this example to use package "package" instead of "apt" module one can even support other Linux distributions in the same task.