Ansible - Clean Formatted Playbooks

5 minute read

While going through and doing some cleanup to various different roles and playbooks in my Ansible collection I wanted to share what I feel is good clean formatting. This will of course not be for everyone but I wanted to share this nonetheless. The goal of writing playbooks is clean formatted tasks and such to make them easily readable to other or even yourself when going back to look your playbooks. One thing I have noticed over time is that not everyone follows this and sometimes it is extremely difficult to understand what is going on and especially so for those who are just learning. So below you will find an example of an Ansible playbook that is running many different plays and utilizing roles throughout. Feel free to leave your thoughts so others may learn from this as well.

---
- name: Configure DDI Nodes
  hosts: ddi-nodes
  become: true
#  remote_user: remote
  roles:
    - role: ansible-manage-lvm
      when: manage_lvm is defined and manage_lvm
      tags:
        - manage_lvm
    - role: ansible-config-interfaces
      tags:
        - config_interfaces
    - role: ansible-apache2
    - role: ansible-ntp
      tags:
        - config_ntp
    - role: ansible-mariadb-galera-cluster
    - role: ansible-logstash
      when: install_logstash is defined and install_logstash

- name: Configure Quagga on DDI Nameserver Nodes
  hosts: ddi-nameserver-nodes
  become: true
#  remote_user: remote
  roles:
    - role: ansible-quagga
      when: >
            (install_quagga is defined and install_quagga) and
            (enable_pdns_anycast is defined and enable_pdns_anycast)

- name: Configure DDI Services on DDI Nameserver Nodes
  hosts: ddi-nameserver-nodes
  become: true
#  remote_user: remote
  tasks:
    - name: debian | uninstalling ISC-DHCP
      apt:
        name: isc-dhcp-server
        state: absent
      when: install_dhcp is defined and not install_dhcp
  roles:
    - role: ansible-powerdns
      when: install_dns is defined and install_dns
    - role: ansible-phpipam
      when: install_phpipam is defined and install_phpipam
    - role: ansible-isc-dhcp
      tags:
        - config_dhcp
      when: install_dhcp is defined and install_dhcp

- name: Configure DDI Services on DDI Nameserver RO Nodes
  hosts: ddi-nameserver-ro-nodes
  become: true
#  remote_user: remote
  tasks:
  roles:
    - role: ansible-config-interfaces
      tags:
        - config_interfaces
    - role: ansible-logstash
      when: install_logstash is defined and install_logstash
    - role: ansible-ntp
      tags:
        - config_ntp
    - role: ansible-powerdns
      when: install_dns is defined and install_dns

As you can see from above the when conditions are being derived from either the defaults in the roles themselves or from group_vars and/or host_vars. Also note the when conditions highlighted and see how we can write them differently. The second one is a good way to format a clean extended when condition so the whole when condition is not across the whole screen (I am guilty of this one as well).

How about another way to define these differently then using external var definitions? In the example below you will see how we can define within our playbook on how to define vars for specific roles within each of our plays in our playbook.

---
- name: Configure DDI Nodes
  hosts: ddi-nodes
  become: true
#  remote_user: remote
  roles:
    - role: ansible-manage-lvm
      manage_lvm: true
      when: manage_lvm is defined and manage_lvm
      tags:
        - manage_lvm
    - role: ansible-config-interfaces
      tags:
        - config_interfaces
    - role: ansible-apache2
    - role: ansible-ntp
      tags:
        - config_ntp
    - role: ansible-mariadb-galera-cluster
    - role: ansible-logstash
      install_logstash: false
      when: install_logstash is defined and install_logstash

- name: Configure Quagga on DDI Nameserver Nodes
  hosts: ddi-nameserver-nodes
  become: true
#  remote_user: remote
  roles:
    - role: ansible-quagga
      install_quagga: false
      enable_pdns_anycast: false
      when: >
            (install_quagga is defined and install_quagga) and
            (enable_pdns_anycast is defined and enable_pdns_anycast)

- name: Configure DDI Services on DDI Nameserver Nodes
  hosts: ddi-nameserver-nodes
  become: true
#  remote_user: remote
  tasks:
    - name: debian | uninstalling ISC-DHCP
      apt:
        name: isc-dhcp-server
        state: absent
      when: install_dhcp is defined and not install_dhcp
  roles:
    - role: ansible-powerdns
      install_dns: true
      when: install_dns is defined and install_dns
    - role: ansible-phpipam
      install_phpipam: true
      when: install_phpipam is defined and install_phpipam
    - role: ansible-isc-dhcp
      install_dhcp: true
      tags:
        - config_dhcp
      when: install_dhcp is defined and install_dhcp

- name: Configure DDI Services on DDI Nameserver RO Nodes
  hosts: ddi-nameserver-ro-nodes
  become: true
#  remote_user: remote
  tasks:
  roles:
    - role: ansible-config-interfaces
      tags:
        - config_interfaces
    - role: ansible-logstash
      install_logstash: true
      when: install_logstash is defined and install_logstash
    - role: ansible-ntp
      tags:
        - config_ntp
    - role: ansible-powerdns
      install_dns: true
      when: install_dns is defined and install_dns

As you can see from the highlighted lines dictate what our vars should be set to for that specific play against its respective role.

And yet one other example of defining variables for a whole play to be applied against all roles.

---
- name: Configure DDI Nodes
  hosts: ddi-nodes
  become: true
#  remote_user: remote
  vars:
    install_logstash: false
    manage_lvm: true
  roles:
    - role: ansible-manage-lvm
      when: manage_lvm is defined and manage_lvm
      tags:
        - manage_lvm
    - role: ansible-config-interfaces
      tags:
        - config_interfaces
    - role: ansible-apache2
    - role: ansible-ntp
      tags:
        - config_ntp
    - role: ansible-mariadb-galera-cluster
    - role: ansible-logstash
      when: install_logstash is defined and install_logstash

- name: Configure Quagga on DDI Nameserver Nodes
  hosts: ddi-nameserver-nodes
  become: true
#  remote_user: remote
  vars:
    enable_pdns_anycast: false
    install_quagga: false
  roles:
    - role: ansible-quagga
      when: >
            (install_quagga is defined and install_quagga) and
            (enable_pdns_anycast is defined and enable_pdns_anycast)

- name: Configure DDI Services on DDI Nameserver Nodes
  hosts: ddi-nameserver-nodes
  become: true
#  remote_user: remote
  vars:
    install_dhcp: true
    install_dns: true
    install_phpipam: true
    install_dhcp: true
  tasks:
    - name: debian | uninstalling ISC-DHCP
      apt:
        name: isc-dhcp-server
        state: absent
      when: install_dhcp is defined and not install_dhcp
  roles:
    - role: ansible-powerdns
      when: install_dns is defined and install_dns
    - role: ansible-phpipam
      when: install_phpipam is defined and install_phpipam
    - role: ansible-isc-dhcp
      tags:
        - config_dhcp
      when: install_dhcp is defined and install_dhcp

- name: Configure DDI Services on DDI Nameserver RO Nodes
  hosts: ddi-nameserver-ro-nodes
  become: true
#  remote_user: remote
  vars:
    install_dns: true
    install_logstash: true
  tasks:
  roles:
    - role: ansible-config-interfaces
      tags:
        - config_interfaces
    - role: ansible-logstash
      when: install_logstash is defined and install_logstash
    - role: ansible-ntp
      tags:
        - config_ntp
    - role: ansible-powerdns
      when: install_dns is defined and install_dns

As you can see from above we have defined all of our vars outside of the roles and within the actual play details.

The above are only a few examples of what I would consider writing good clean Ansible playbooks, tasks and etc.

One last item I would like to share is when writing Ansible handlers. One may assume that handlers can/or only look like the example below.

- name: Example Handler Playbook
  hosts: all
  become: true
  vars:
    are_you_sure: false
    reconfigure_apache2: true
  handlers:
    - name: restart apache2
      service:
        name: apache2
        state: restarted
      when: are_you_sure is defined and are_you_sure
  tasks:
    - name: Reconfigure Apache2
      template:
        src: "etc/apache2/apache2.conf.j2"
        dest: "/etc/apache2/apache2/conf"
        owner: root
        group: root
        mode: 0644
      notify:
        - restart apache2
      when: reconfigure_apache2 is defined and reconfigure_apache2

What is being shown above is that we can add a when condition to a handler as well. This short play would reconfigure Apache2 because we have reconfigure_apache2: true but the handler defined to notify the restart of apache2 will not occur because we have the variable are_you_sure: false.

This is all for now and hope this may find to be useful to others as well as encourage others to share their thoughts or even tips and tricks.

Enjoy!

Leave a comment