Make Ansible Playbook Distribution Agnostic

I’ve been thinking about switching to a rolling distribution on my working machine for quite some time. Because the packages in Ubuntu repositories are just too outdated for my needs and I want to save myself the headache of PPAs. And when Ubuntu announced dropping Unity in favor of Gnome, my mind was made up. I would use a rolling distro on my main computer and Ubuntu 16.04 elsewhere.

Unify package installation

In the end, I decided to go for OpenSuse. Since I use an Ansible playbook to setup my machines, it would take no time to have it up an running. Or so I thought. However, there was a little caveat. As my playbook used the apt module to install software, it would obviously not run on OpenSuse. Luckily, since the version 2.0, Ansible provides a platform-agnostic package manager module. It is called simply package. As a result, installing packages across different distributions is as easy as running:

---
- name: Install packages
  become: yes
  package:
    name: git
    state: present

Or is it? While it may work in the case of Git, not all packages have a same name in different Linux distributions. So you have to set up variables for those: First of all, defaults in vars/default.yml:

apache_package: apache2

Then, for Debian family vars/Debian.yml

---
apache_package: apache2

And for Redhat family vars/Redhat.yml

---
apache_package: httpd

Afterwards, just include them in your tasks:

 - include_vars: "{{ item }}"
   with_first_found:
     - "{{ ansible_os_family }}.yml"
     - "default.yml"

 - name: Install Apache
   become: yes
   package: name: "{{ apache_package }}"
   state: present

Run different tasks on various distros

Using variables is sometimes not enough. As depending on the system, you might want to run compeletely different tasks. In such case, just create two (or more) tasks for the respective systems. A good example is upgrading packages, since it vastly differs across distributions: Firstly, for Debian family (including Ubuntu) in tasks/update-Debian.yml:

---
- name: Update packages
  become: yes
  apt:
    update_cache: yes
    upgrade: dist

Then, for OpenSuse in tasks/update-SLES.yml:

---
- name: Update packages
  zypper:
    name: '*'
    state: latest
    update_cache: yes

Lastly, conditionally include them in the main task (tasks/main.yml):

---
- include: "update-{{ ansible_os_family }}.yml"

That’s it. Now you can easily make your Ansible playbook distribution agnostic, so it runs across different Linux flavors.