What's the difference between defaults and vars in an Ansible role?

Ansible

Ansible Problem Overview


When creating a new Ansible role, the template creates both a vars and a defaults directory with an empty main.yml file. When defining my role, I can place variable definitions in either of these, and they will be available in my tasks.

What's the difference between putting the definitions into defaults and vars? What should go into defaults, and what should to into vars? Does it make sense to use both for the same data?

I know that there's a difference in precedence/priority between the two, but I would like to understand what should go where.

Let's say that my role would create a list of directories on the target system. I would like to provide a list of default directories to be created, but would like to allow the user to override them when using the role.

Here's what this would look like:

---
- directories:
  - foo
  - bar
  - baz

I could place this either into the defaults/main.yml or in the vars/main.yml, from an execution perspective, it wouldn't make any difference - but where should it go?

Ansible Solutions


Solution 1 - Ansible

The Ansible documentation on variable precedence summarizes this nicely:

> If multiple variables of the same name are defined in different places, they win in a certain order, which is: > > * 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.

So suppose you have a "tomcat" role that you use to install Tomcat on a bunch of webhosts, but you need different versions of tomcat on a couple hosts, need it to run as different users in other cases, etc. The defaults/main.yml file might look something like this:

tomcat_version: 7.0.56
tomcat_user: tomcat

Since those are just default values it means they'll be used if those variables aren't defined anywhere else for the host in question. You could override these via extra-vars, via facts in your inventory file, etc. to specify different values for these variables.

Edit: Note that the above list is for Ansible 1.x. In Ansible 2.x the list has been expanded on. As always, the Ansible Documentation provides a detailed description of variable precedence for 2.x.

Solution 2 - Ansible

Role variables defined in var have a very high precedence - they can only be overwritten by passing them on the command line, in the specific task or in a block. Therefore, almost all your variables should be defined in defaults.

In the article "Variable Precedence - Where To Put Your Role Vars" the author gives one example of what to put in vars: System-specific constants that don't change much. So you can have vars/debian.yml and vars/centos.yml with the same variable names but different values and include them conditionally.

Solution 3 - Ansible

IMHO it is impractical and not sensible that Ansible places such high priority on configuration in vars of roles. Configuration in vars/main.yml and defaults/main.yml should be low and probably the same priority.

Are there any real life examples of cases where we want this type of behavior?

There are examples that we dont' want this.

The point to make here is that configuration in defaults/main.yml cannot be dynamic. Configuration in vars/main.yml can. So for example you can include configuration for specific OS and version dynamically as shown in geerlingguy.postgresql

But because precedence is so strange and impractical in Ansible geerlingguy needs to introduce pseudo variables as can be seen in variables.yml

- name: Define postgresql_packages.
  set_fact:
    postgresql_packages: "{{ __postgresql_packages | list }}"
  when: postgresql_packages is not defined

This is a concrete real life example that demonstrates that the precedence is impractical.

Another point to make here is that we want roles to be configurable. Roles can be external, managed by someone else. As a general rule you don't want configuration in roles to have high priority.

Solution 4 - Ansible

Basically, anything that goes into “role defaults” (the defaults folder inside the role) is the most malleable and easily overridden. Anything in the vars directory of the role overrides previous versions of that variable in namespace. The idea here to follow is that the more explicit you get in scope, the more precedence it takes with command line -e extra vars always winning. Host and/or inventory variables can win over role defaults, but not explicit includes like the vars directory or an include_vars task. doc

Solution 5 - Ansible

Variables and defaults walk hand in hand. here's an example

-name: install package
 yum: name=xyz{{package_version}} state=present

in your defaults file you would have something like:

package_version: 123

What ansible will do is, it's gonna take the value of package_version and put it next to the package name so it will read somewhere as:

-name: install package
 yum: name=xyz123 state=present

This way it will install xyz123 and not xyz123.4 or whatever is in the great repository of xyz's.

At the end it will do yum install -y xyz123

So basically the defaults are the values present, if you do not set a specific value for the variables, cause that space can't stay empty.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnwinklerView Question on Stackoverflow
Solution 1 - AnsibleBruce PView Answer on Stackoverflow
Solution 2 - AnsiblechiborgView Answer on Stackoverflow
Solution 3 - AnsibleonknowsView Answer on Stackoverflow
Solution 4 - AnsiblepjosolsView Answer on Stackoverflow
Solution 5 - AnsiblequbsupView Answer on Stackoverflow