Ansible Best Practices: The Essentials

The Ansible Way

When I talk about how to develop automation solutions with Ansible, I begin by highlighting the philosophy behind its design. All Ansible best practices relate back to this thinking in one way or another.

Complexity kills productivity

That’s not just a marketing slogan. We really mean it and believe it. We strive to reduce complexity in how we’ve designed Ansible tools and encourage you to do the same. Strive for simplification in what you automate.

Optimize your Ansible content for readability

If done properly, it can be the documentation of your workflow automation.

Think declaratively

Ansible is a desired state engine by design. If you’re trying to “write code” in your plays and roles, you’re setting yourself up for failure. Our YAML-based playbooks were never meant to be for programming.

Ansible is like the Swiss Army Knife of DevOps

Ansible is capable of handling many powerful automation tasks with the flexibility to adapt to many environments and workflows. Not all approaches are created equal though. Don’t let yours undermine the simplicity and power of Ansible.

Enough philosophy though. Let’s get down to brass tacks. Here I’ll cover some of the most important and impactful best practices you can apply to your work developing automation solutions with Ansible.

1. “Name” Your Plays and Tasks

Always name your plays and tasks. Adding name with a human meaningful description better communicates the intent to users when running a play. Consider this example play and its standard Ansible output.

Without knowing what’s in the play, another user can see the play is doing something with yum and something with service, but what exactly? They aren’t even sure what the purpose of the play is except for maybe the filename or some external documentation source. Let’s look at that same example with name declared on the play and all of its tasks.

Much better. It is now much more apparent what this playbook is doing right in the output of your playbook run. It also aids the usage of the --list-tasks switch in ansible-playbook.

2. Use Prefixes and Human Meaningful Names with Variables

Ansible has a powerful variable processing system that collects metadata from various sources and manages their merge and context as a play runs on your hosts. A lot of effort goes into making that power as easy and transparent as possible to users.

Variable scoping and namespacing was intentionally limited and shallow by design to make it easier to understand and debug.

A limitation to this approach is that you run the risk of variable collisions if two different pieces of information, like a port number, are stored in the same variable name. (Having a web server listen to port 22 is not the sort of fun you want to have.)

As a best practice, we recommend prefixing variables with the source or target of the data it represents. Prefixing variables is particularly vital with developing reusable and portable roles.

apache_max_keepalive: 25apache_port: 80tomcat_port: 8080

You can also dramatically improve the readability of your plays for a bit of extra verbosity with using human-meaningful names that communicate their purpose and usage to others or even yourself at a later date.

3. Use Native YAML Syntax

At its core, the Ansible playbook runner is a YAML parser with added logic such as commandline key=value pairs shorthand. While convenient when cranking out a quick playbook or a docs example, that style of formatting reduces readability. We recommend you refrain from using that shorthand (even with YAML folded style) as a best practice.

Native YAML has more lines; however, those lines are shorter, reducing horizontal scrolling and line wrapping. It lets the eyes scan straight down the play. The task parameters are stacked and easily distinguished from the next. Native YAML syntax also has the benefit of improved syntax highlighting in virtually any modern text editor out there. Being native YAML, editors such as vim and Atom will highlight YAML keys (module names, directives, parameter names) from their values further aiding the readability of your content. Many of our own docs use this shorthand for legacy reasons though we’re progressively changing that. (Documentation pull requests accepted.)

4. Use Modules Before Run Commands

Run commands are what we collectively call the command, shell, raw and script modules that enable users to do commandline operations in different ways. They’re a great catch all mechanism for getting things done, but they should be used sparingly and as a last resort. The reasons are many and varied.

The overuse of run commands is often a symptom of TL;DR in Ansible and common amongst those just becoming familiar with Ansible for automating their work. They use shell to fire off a bash command they already know without stopping to look at the Ansible docs. That works well enough initially, but it undermines the value of automating with Ansible and sets things up for problems down the road.

The most important thing to consider is that these run commands have little logic to them and no concept of desired state like a typical Ansible module.

That shell that succeeded the first time you ran your play may fail the next time when something already exists. That’s unless you ignore_errors on that task. But how do you catch a real error like wrong permissions? Now you have to register the result of that first command and follow it with another task that implements conditional logic to check if an error occurred in the first and handle it.

Starting to get confused? Sounding complex? Right.

When you’re starting to write code in your Ansible play, then your descent into code hell is underway. As a best practice, always check the hundreds of Ansible shipping modules for what you need and use those first and run commands as a last resort.

Modules abstract users away from the complexity of implementation details and benefit from having full access to a proper programming language where they can do whatever heavy lifting is needed.

5. Clean Up Your Debugging Messages

When developing Ansible content, it can be useful to drop in a debug task to display the content of a variable while your play runs. That’s not so nice, even downright disconcerting, when your Playbook goes into production and some unwitting ops manager runs a play and sees your debugging messages on their screen.

That’s why it is a best practice to clean up those debug statement in your Ansible content.

In the past, you had to delete or comment out your debug tasks. That was a suboptimal approach though.

Starting in version 2.1, the Ansible debug module supports a verbosity parameter that suppresses output unless the play is being run with a sufficiently high enough verbosity level.

Timothy Appnel is a Principal Product Manager, with over 25 years of IT experience. Tim is an old-timer in the Ansible community that has been contributing since version v0.5. The synchronize module in Ansible is all his fault.