Variable Precedence - Where To Put Your Role Vars?


In this video we are going to take a look at the joyous sounding subject of Ansible's Variable Precedence.

No, please, come back... it's not that boring, I promise!

The topic sounds dull though, so let's quickly explain what the heck I am on about:

In Ansible, as we have seen in previous videos, you can declare / define variables in numerous places:

  • playbooks
  • roles - both the defaults and the vars directories
  • group_vars and host_vars directories

And that's not covering passing extra variables via command line, inventory variables, or system facts.

Yikes.

Even at a quick glance, there's a good few places we can create our variables. And it becomes important to know which ones will override the others. This is especially true if you are creating and using Roles, which you really should be, if you aren't already.

I have shameless stolen this section from the Ansible docs, as it's important for reference, and isn't really useful for me to spend time re-typing it:

Ansible Variable Precendence

  • extra vars (-e in the command line) always win
  • then comes connection variables defined in inventory (ansible_ssh_user, etc)
  • then comes "most everything else" (command line switches, vars in play, included vars, role vars, etc)
  • then comes the rest of the variables defined in inventory
  • then comes facts discovered about a system
  • then "role defaults", which are the most "defaulty" and lose in priority to everything.

The Role Vars Controversy

If you've been following along with this tutorial series, in the Roles video I showed you how I use ansible-galaxy init to create a template role (which we called roles/__template__), which gives us a Ansible standardised directory structure for creating our Role within.

One of the subdirectories from our __template__ directory is __template__/vars, which contains a single file - main.yml.

The big gotcha is that vars/main.yml is likely not the place you want to put your variables.

Well, not all of them.

Before I go on, I will say the place you should put your variables is - in the majority - the defaults/main.yml file.

The reasoning for this is that we can override the contents of defaults/main.yml very easily, whereas variables in vars/main.yml are pretty hard to override.

If you're creating a new role, let's say a role for installing MySQL, the chances are that you don't want to hard code the database name (or names) that are created by your role. Our roles should be re-usable, and that means our variables should be easily interchangable.

Both the vars/main.yml and defaults/main.yml files work similarly, only the order in which they are given priority by Ansible differs.

Should I Use The vars Directory or the defaults Directory?

This is my opinion.

I use vars directory for system specific variables.

Let's say you are in a large organisation and you have to support CentOS and Debian-based servers.

You need to install Apache on both. The projects are different. The gist is the same.

We need Apache on both types of servers and we don't want to create two roles - roles/debian-mysql and roles/centos-mysql or some other such sillyness. Keep our Ansible DRY!

As seasoned Apache users, we are aware that on CentOS our Apache user will run as apache but just to create confusion, on Debian (and as a result, Ubuntu), our Apache user will be www-data.

It's very unlikely these user names are going to be changing frequently.

Therefore, we would likely want to create a structure something like this:

# roles/apache/vars/centos.yml
apache_user: apache

and

# roles/apache/vars/debian.yml
apache_user: www-data

We can then use conditionals to determine which vars file we should use.

This way, we still get access to our variable inside our templates or playbooks tasks ({{ apache_user }}), but we don't need to worry about getting our user to pass in the apache_user when configuring their usage of this role.

So When Should I Use Role defaults?

Everything else - all the standard, shared variables we would need, and anything we want the user of our Role to override should go in defaults/main.yml.

Pretty easy.

Careful Now

One of the best ways to learn how to create your own Roles and dive deeper into Ansible is to look at other people's Roles.

A fantastic way to do this is via the awesome Ansible Galaxy.

On some of the bigger packages - MySQL, Apache, what have you - you will find tons of Roles fulfilling the same task in often subtly different ways.

One of the things that caught me out when I was learning this way was that some people do use the vars directory as they likely intended as their role defaults. This threw me for a good while, but as you've read this now you won't suffer the same fate.

*[DRY]: Don't Repeat Yourself

Code For This Course

Get the code for this course.

Episodes