Skip to content

Variables

Matt DePorter edited this page Aug 13, 2018 · 3 revisions

When working with multiple OSes, inevitably there will be packages, configuration files and service calls that are vastly different than one another. There can even be instances in which sub-versions of OSes can have major differences, such as sysvinit vs. systemd in CentOS 6 vs. 7

Defined here are some different ways to handle these issues both in the same playbook or play or by separating out tasks to other task files.

Task separation via Variables

This method pulls in variables based on the client's OS and uses those variables specific to that machine's needs.

A solution to this problem uses the Ansible module include_vars with the looping mechanism with_first_found

- name: gather os specific variables
  include_vars: "{{ item }}"
  with_first_found:
    - "{{ ansible_distribution }}-{{ ansible_distribution_major_version}}.yml"
    - "{{ ansible_distribution }}.yml"
  tags: vars

Now that variables are defined specific to each OS, you now have to leverage those variables by writing your Ansible in such a way that each task is so universal that each OS can use the same base-set of Ansible.

This works pretty simply most of the time, however, there are many cases where some tasks only apply to one OS or one specific type of configuration. If this is the case, you can use a few items to solve for these situations. A simple solution to this, is to use the when: operator to control which task is executed. This acts similar to a programming if statement. Use caution, it is very easy to put yourself into a position where you basically have to add a when: clause to every task. If you find yourself doing that, it might be worth considering separating repeat or special cases into separate task files, or even using another role or in newer versions of Ansible, items like blocks can come in handy.

Task separation via Includes

Alternatively, one could separate tasks into a set of tasks that are OS specific, then come back and finish execution of the playbook.

Consider the case that each OS has a very specific installation requirement for some software that you are trying to install. Instead of repeating code or having a zillion when clauses, it can be useful to variable-ize a task file and call it after setting your variables, or just separate out tasks meant for specific use-cases and call when needed. Again, use caution because this can easily pigeon-hole you into a situation where it is extremely difficult or even impossible to upgrade/support other OSes without a complete re-write of your entire role/playbook.

Here is a simple example of how OS or software specifics can be handled with separate files:

# main.yml
- include: centos-5-setup.yml
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version < '6'

- include: centos-6-setup.yml
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == '6'

- include: centos-7-setup.yml
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version > '6'

- include: ubuntu-setup.yml
  when:
    - ansible_distribution == "Ubuntu"

The directory stucture will look something like this:

ntp/
├── defaults
│   └── main.yml
├── tasks
│   └── centos-5-setup.yml
│   └── centos-6-setup.yml
│   └── centos-7-setup.yml
│   └── main.yml
│   └── ubuntu-setup.yml
├── templates
└── vars

Notice that main.yml is still the entry-point, but each sub-task file is called via main.yml. It's also important to note that the path of the include: statement automatically assumes <role-name>/tasks/<task-file-name>.yml where <task-file-name>.yml is the file you are including. When using include: inside of a playbook under the pre_tasks: or tasks: section, it will automatically use the same directory the playbook is in, but inside a tasks directory on the same level.

A useful trick, especially when you might need to point the path somewhere else, playbook_dir is an Ansible reserved variable that will contain the pwd equivalent path to whereever your playbook is located. For example, if your plabook is located in /etc/ansible/my-playbook.yml, playbook_dir will resolve to /etc/ansible. This is extremely handy when you need to pull a task file or variable file from a location outside your role or default playbook location.

A simpler version with less requirements may look like this:

 
# Include OS specific tasks
- include: CentOS.yml
  when: ansible_distribution == "CentOS"
  
- include: Ubuntu.yml
  when: ansible_distribution == "Ubuntu"

Troubleshooting

Be aware that when you include a task file, it must have a full path at start of run, as in if there are variables in the inclusion, they must be declared in defaults/main.yml or something similar or you will get errors on load.

Clone this wiki locally