Skip to content
Bert Van Vreckem edited this page Nov 20, 2015 · 11 revisions

Ansible Coding Style Guide

These are the coding conventions I adhere to when writing Ansible code. Michael De Haan will probably not be a fan of this. His point of view is that Ansible should be low entry, and coding rules heighten the barrier to entry. This is definitely a valid concern. Ansible's Yaml syntax is easy to understand and easy to write, so coding guidelines are unnecessary at first sight.

However, in a larger code base, consistency has its merits. "There is more than one way to do it," and if I do stuff in different ways within the same role (or over several roles that are combined into a single project), it will certainly confuse future me, and probably others that read my code.

So ever since starting to use Ansible, learning more about it, and acquiring experience in writing playbooks, I gathered a few 'best practices' that I will enumerate here. So, this is an opinionated list, and I'm not trying to impose it on anyone. I do hope it's of some value to someone, though, and feedback is greatly appreciated.

In what follows, ${ANSIBLE_HOME} refers to the Ansible configuration root directory. On a server system, this would typically be /etc/ansible. However, if you run Ansible from your own machine, it could be any directory.

Contents

Directory layout

  • The directory layout adheres to the Ansible Playbook Best Practices.

  • All files have an appropriate extension, e.g. .yml, .j2.

    # bad
    ${ANSIBLE_HOME}/group_vars/all
    # good
    ${ANSIBLE_HOME}/group_vars/all.yml
    
    # bad
    ${ANSIBLE_HOME}/templates/some_file.conf
    # good
    ${ANSIBLE_HOME}/templates/some_file.conf.j2
    
  • The main playbook is called site.yml, and is located in ${ANSIBLE_HOME}. site.yml manages the configuration of all hosts.

  • Other playbooks that are not used to manage system configuration, but rather use Ansible as an orchestration tool, are stored in a directory ${ANSIBLE_HOME}/playbooks/.

  • Template files encode the target location in their name. In the path name, '/' is substituted with '_'

    # bad
    ${ANSIBLE_HOME}/templates/ssl.conf.j2
    
    # good
    ${ANSIBLE_HOME}/templates/etc_httpd_conf.d_ssl.conf.j2

Source code layout

  • All Yaml files start with a comment line with the file path relative to ${ANSIBLE_HOME}, an optional comment on the purpose of the file, followed by the --- marker

    # roles/httpd/tasks/main.yml
    # Main playbook of the httpd role
    ---
  • Use two spaces per indentation level, no tabs.

  • Avoid trailing whitespace

  • Tasks within a role should be tagged with the name of the role

    # bad
    - name: Ensure the service is started
      service:
        name: httpd
        state: started
    
    # good
    - name: Ensure the service is started
      service:
        name: httpd
        state: started
      tags: httpd

Syntax

  • Playbooks are written in valid Yaml instead of the default Ansible key=value notation. Ansible's key=value notation confuses text editors (syntax colouring) and really long lines (which are common) become hard to read.

    # bad
    - name: Install mod_ssl configuration file
      template: src=ssl.conf.j2 dest=/etc/httpd/conf.d/ssl.conf owner=root group=root setype=httpd_config_t mode='0644'
      notify: restart httpd
      tags: httpd
    
    # good
    - name: Install mod_ssl configuration file
      template:
        src: ssl.conf.j2
        dest: /etc/httpd/conf.d/ssl.conf
        owner: root
        group: root
        setype: httpd_config_t
        mode: '0644'
      notify: restart httpd
      tags: httpd
  • Don't use quotes, unless you have to

  • Prefer single quotes over double quotes

Variables

  • All role variables should start with the name of the role, followed by an underscore. This avoids name conflicts with variables defined in other roles.

    # bad
    port_number: 80
    
    # good
    httpd_port: 80
  • Variable defaults should not be specified in templates, but in defaults/main.yml. This makes the template more readable (less clutter). It is also more consistent to put all default values for role variables in a single place.

    # bad (templates/some_template.j2)
    listen-port={{ role_listen_port|default('1234') }}
    
    # good
    # templates/some_template.j2:
    listen-port={{ role_listen_port }}
    
    # defaults/main.yml
    role_listen_port: 1234