ansible

Ansible Best Practices

Ansible is a tool that automates configuration management through code written in YAML. It is extremely popular, partly thanks to the fact that it works without clients and daemons: all it needs is Python and an SSH connection!

Since the YAML syntax is quite user-friendly, it is fairly easy to get started with Ansible. However, without a thorough introduction to the formats and structures of Ansible, the initial result is usually “bad” code: it might work, but it is prone to yield errors in the future, be inefficient or simply unreadable.

In this article, we want to present a few of the Ansible best practices that we deem especially important. This is a taste of what we discuss during our Ansible trainings at ATIX. If you are interested in learning Ansible from the ground up with a hands-on approach, do not hesitate to contact us for more information about our trainings.

Tuesday, April 30, 11:00 – 11:45 a.m. CEST
Webinar: Driving Efficiency with Event-Driven Ansible: An Introduction

Unravel the theoretical foundations of Event-Driven Ansible alongside a practical demonstration.
Presented by our blog author Dr. Ottavia Balducci.

YAML

  • Use true instead of yes and false instead of no

  • Insert two blank spaces per indentation level (not four and NEVER tabs!)

  • Avoid inline lists and dictionaries

  • Break lines that are longer than 80 characters:

    1 a_string: > # transforms linebreaks to spaces
    2 R0lGO2313DdhDQAIAIAAAAAAANn
    3 Z2SwAAAAAsdasfDQAIAAACF4SDGQ
    4 ar3xxbJ9p0qa7R3fasdAS0YxwzaFME
    5 1IAADs=
    6
    7 # this string doesn't have spaces
    8 a_folded_string_without: "R0lGO2313DdhDQAIAIAAAAAAANn
    9 Z2SwAAAAAsdasfDQAIAAACF4SDGQ
    10 ar3xxbJ9p0qa7R3fasdAS0YxwzaFME
    11 1IAADs="

Playbook

  • Task names:

    • Always give a name to each task

    • Start the name with a capital letter

    • Ensure the name describes the task appropriately; for example “Install Apache” instead of “Install stuff”

  • Use dictionary style in playbooks, for example:

    1 # avoid this
    2 - name: Install Apache packages
    3 yum: name="{{ packages }}" state=present
    4 vars:
    5 packages:
    6 - httpd
    7 - httpd-devel
    8
    9 # use this instead
    10 - name: Install Apache packages
    11 yum:
    12 name: "{{ packages }}"
    13 state: present
    14 vars:
    15 packages:
    16 - httpd
    17 - httpd-devel
  • Use the fully qualified collection name (FQCN), e.g., ansible.builtin.debug instead of debug

  • Apply privilege escalation at task level, not at play level

  • Try to avoid the modules ansible.builtin.command, ansible.builtin.shell and ansible.windows.win_shell: these modules are not idempotent! If you have no other option, try to work with changed_when, failed_when and (if applicable) creates and removes to make your tasl as idempotent as possible

Vaults

  • Encrypt all sensitive variables/files using ansible-vault

  • Store the vault password securely, for example as a Jenkins secret or by using an external secret manager, such as HashiCorp’s vault

  • If you encrypt a whole file, you may forget the names of your variables => add layer of indirection for readability, for example

    1 ---
    2 # vault.yaml
    3 vaulted_password: this_will_be_encrypted
    1 ---
    2 # vars.yaml
    3 password: "{{ vaulted_password }}"

Reusing Ansible Code

  • Separate your tasks into files and then import/include them in your playbook

  • As there are some important differences between import_tasks and include_tasks (see this table), try to use import_tasks when possible

  • In general, go for import_*, especially because

    • --list-tasks also shows the imported tasks (and --start-at-task works as intended)

    • --list-tags also shows the tags of the imported tasks

    • notify of imported tasks works (careful: you should notify one of the imported tasks, not the import_tasks task itself!)

    • the options of the import_* are passed to ALL child tasks

  • You should use include_* when

    • you want to loop over tasks

    • the name of the file to be included depends on a variable, for example {{ ansible_os_family }}.yaml

    • you want to apply an option ONLY to the include_* task

Roles

  • Collect Ansible content meant for reuse in future projects into roles

  • Use one of the two options to define variables within a role—defaults and vars:

    • vars refers to variables that are not supposed to be changed by the user

    • defaults refers to variables that can be easily overridden by the user

  • Add a prefix to the variables in a role to indicate to which role they belong, for example postgresql_version for a variable defined in a postgresql role (this is especially useful when you are working with multiple roles at once)

  • If you use ansible-galaxy init my-role to initialize a role, don’t forget to remove all unused directories in the end

Templates

Include a line at the beginning of your templates to show which role generated these files. For this purpose, the variable ansible_roleis very useful; for example, your template could start like this:

1 # this file was created from the role: {{ansible_role}}
2 ***here comes your actual template***
  • Use the ansible_managedfeature if you want to make it clear to other users that a specific file is generated by Ansible: you can define a variable named ansible_managed in the Ansible configuration (ansible.cfg)—see this minimal example of such an ansible.cfg:

    1 [defaults]
    2 ansible_managed: Ansible managed: {file} modified
    3 on %Y-%m-%d %H:%M:%S by {uid} on {host}
  • o make sure your template includes information about who generated it at what time, write this variable at the beginning of your templates:

    1 # {{ansible_managed}}
    2 # this file was created from the role: {{ansible_role}}
    3 ***your actual template***

Conclusion

As you can see, Ansible covers a wide range of topics. There are many places where you have to be careful in order to write elegant code and save yourself more work down the road.

We hope that this serves as a good reference and can help you in your future Ansible endeavours.

If you have any questions or would like more guidance on any of these specific topics, do not hesitate to contact us.

ansible

Ansible Training

ATIX offers Ansible training courses for beginners and advanced learners, where participants learn how to use Ansible as a configuration management tool. This allows them to manage their infrastructure more efficiently with Ansible roles, and find out how to best create, use, and maintain Ansible roles, inventories, and playbooks.

The following two tabs change content below.

Ottavia Balducci

Latest posts by Ottavia Balducci (see all)