3. Best Practices

3.1. General

  • Version control Ansible content

  • Use single source of truth

  • Automation should be all or nothing (completely remove manual changes to servers!)

  • Use the run command modules as a last resort (command module is safer than shell)

  • shell can evaluate variables, command cannot

3.2. Readability

  • Complexity kills (simplify, optimize for readability, think declarative)

  • Try to use one file if lines are less than 100 (do not start with several files)

  • If you have more lines, then split

  • Try to use roles as much as possible, but keep them independent and reusable

  • YAML is not a programming language

  • If it's too complex (i.e. struggling with escaping quotes), you're doing something wrong

  • Vertical reading is easier (split long lines)

  • Prefer more explicit yaml values)

  • Be explicit (more verbose) i.e. always define state

  • Try other callback plugins i.e. yaml (for human readable error messages)

  • Keep plays and playbooks focused

  • Multiple simple ones are better than having a huge file full of conditionals

  • Separate provisioning from deployment and configuration tasks

3.3. Performance

  • Profile with callback plugin to see which roles and playbooks takes too much time

  • CI is useless if slow

    • Disable gather_facts if not needed (save few seconds per server)

    • Check forks config - by default its 5, update accordingly to number of your servers

    • package - pass a list to name instead of a loop (to execute one command)

    • copy - only for single files or small dirs, else use synchronize module

    • lineinfile try to switch to template instead of looping on one file (or use blockinfile

3.4. Inventory

  • Give inventory nodes human-meaningful names rather than IPs or DNS hostnames

  • If you change inventory file frequently (one or two times a month) use dynamic inventory files

  • If it's a static environment (new servers are added rarely) use static inventory

  • Dynamic inventory files are quite easy

3.5. Variables

  • Use descriptive unique human-meaningful variable names

  • Prefer flat variables over nested

    Listing 91. Nested variables
    apache:
        startservers: 2
        maxclients: 2
    
    Listing 92. Flat variables
    apache_startservers: 2
    apache_maxclients: 2
    

3.6. Templates

  • Ansible uses Jinja2

  • Jinja2 is powerful. Don't have to use all of it

  • Templates should be simple

  • Avoid variable substitution

  • Avoid setting variables in template

  • As few Conditionals as possible (do not nest)

  • Avoid conditional logic based on hostnames

  • Simple control structures/iterations

  • Design for your usecase (do not generalize)

  • Avoid complex iteration conditions

  • Label template output files as being generated by Ansible (warn not to edit manually)

  • Consider using ansible_managed** variable with the comment filter (it will put date and some other info)

{{ ansible_managed | comment }}

3.7. Roles

  • Keep roles purpose and function focus

  • Used a roles/ subdirectory for roles developed for organizational clarity in a single project

  • Follow the Ansible Galaxy pattern for roles that are to be shared beyond a single project

  • Start your roles with ansible-galaxy init (it will generate directory structure)

  • Remove unneeded directories and stub files (after ansible-galaxy init)

  • ansible-galaxy can point to your internal private repo

  • Use ansible-galaxy to install your roles -- even private ones

  • Manage your roles in your applications repo

  • As a part of build process, "push" role to artifact repository

  • Use ansible-galaxy to install role from artifact repository

3.8. Scaling

  • Coordination across a distributed organization

  • Controlling access to credentials

  • Track, audit and report Ansible usage

  • Provide self-service or delegation

  • Integrate Ansible with enterprise systems

  • Try Ansible Tower