Link

Ansible Code Snippets

This document demonstrates useful code snippets for Ansible roles/playbooks. These snippets are provided for Ansible role authors and contributors to avoid repetitive typing for typical tasks and steps.

Table of contents


Tasks and Variables Separation

When working with multiple OS versions/distributions (RedHat vs Debian for instance), there are packages, configuration files and parameters that are vastly different than one another. There can even be instances in which sub-versions of OSes can have major differences, such as service vs. systemctl in CentOS 6 vs. 7 etc.

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

Variables Separation

# bad
- name: Set path
  set_fact:
    my_var: /tmp/mypath
  when: ansible_distribution == 'CentOS'
 
# good
- name: Load a variable file based on the OS type, or a default if not found
  include_vars: '{{ platform_vars }}'
  with_first_found:
    - '{{ ansible_os_family }}.yml'
    - '{{ ansible_distribution }}.yml'
    - default.yml
  loop_control:
    loop_var: platform_vars

- name: Load a variable file based on the service manager
  include_vars: '{{ service_manager }}'
  with_first_found:
    - '{{ ansible_service_mgr }}.yml'
    - systemv.yml
  loop_control:
    loop_var: service_manager 

Tasks Separation

# bad
- name: ensure that directory for apache is ready
  file:
    path: '{{ apache_dir }}'
    owner: '{{ web_server_user }}'
    group: '{{ web_server_group }}'
    recurse: true
    state: directory
  become: true
  when: ansible_os_family == 'RedHat'
 
# good
- name: Configure and install packages for current OS
  include_tasks: '{{ platform_tasks }}'
  with_first_found:
    - '{{ ansible_os_family }}.yml'
    - not_supported.yml
  loop_control:
    loop_var: platform_tasks 

Why?

The way of tasks and variables separation using dynamic includes is more flexible and it’s easier to support. For example, to add new OS support you just need to put an additional file into Ansible role with corresponding name and content without changing the exact code of Ansible role or playbook. Thus avoid separation of code and variables by using when conditionals in playbooks.


Install requirements

- name: Install requirements
  package:
    name: '{{ requirements }}'
    state: present
  register: installed_packages
  until: installed_packages is succeeded
  become: true
 
- name: Install required packages
  package:
    name: '{{ packages_base | union(packages_additional) | unique }}'
    state: present
  register: installed_packages
  until: installed_packages is succeeded
  become: true 

Selinux support

---
- name: install ansible selinux support library
  become: true
  package:
    name: libselinux-python
    state: present
  register: installed_package
  until: installed_package is succeeded
 
- name: Install ansible selinux configure libraries
  become: true
  package:
    name:
      - policycoreutils-python
      - libsemanage-python
    state: present
  register: installed_package
  until: installed_package is succeeded
  when: ansible_selinux.status == "enabled"
 
- name: Enable connections to HTTP port
  become: true
  seport:
    ports: "{{ selinux_ports }}"
    proto: tcp
    setype: http_port_t
    state: present
  when:
    - ansible_selinux.status == "enabled"
    - ansible_selinux.mode != "disabled" 

Conditionals

Avoid comparison to empty string and ‘true/false’

Use when: var rather than when: var == True (or conversely when: not var) Use when: var rather than when: var != "" (or conversely when: not var rather than when: var == "")

Why?

Ansible came from Python world. It’s supposed to follow Zen of Python principles and related codestyle conventions. As for conditionals, Python code guides recommend to use direct truth value testing.