# Var precedence

**URL:** <https://forum.ansible.com/t/var-precedence/12865>\
**Category:** Ansible Project\
**Tags:** ansible-project\
**Created:** [May 24, 2013, 9:01pm UTC](https://forum.ansible.com/t/var-precedence/12865 "2013-05-24T21:01:56Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [May 24, 2013, 9:01pm UTC](https://forum.ansible.com/t/var-precedence/12865/1 "2013-05-24T21:01:56Z")

</div>

There are plenty of ways to set defaults.

You can put your systems into a group, and put the variables into group\_vars/.

This has the lowest precedence of anything in Ansible, and you can even have defaults for those in group\_vars/.

Yes, things included in your playbook absolutely are used. You may say it’s backwards but for other people it’s quite logical and important, and we’re not changing it 🙂 If you read a variable in a playbook, you _KNOW_ it is being set.

Finally, you can just do basic stuff like so:

- hosts: blarg  
vars\_files:

- defaults.yml

- blarg.yml

- hosts: glorp  
vars\_files:

- defaults.yml

- glorp.yml

Which isn’t using group\_vars (IMHO you should use group\_vars), but effectively does the same thing a bit more explicitly.

---

<div class="post-metadata">

**Author:** ![martin\_f\_krafft](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@martin\_f\_krafft](https://forum.ansible.com/u/martin_f_krafft)\
**Post date:** [June 11, 2013, 1:39pm UTC](https://forum.ansible.com/t/var-precedence/12865/2 "2013-06-11T13:39:22Z")

</div>

also sprach Michael DeHaan \<michael@ansibleworks.com\> [2013.05.24.2301 +0200]:

> Yes, things included in your playbook absolutely are used. You  
> may say it's backwards but for other people it's quite logical and  
> important, and we're not changing it 🙂

Please show me a use-case where the existing behaviour makes sense.  
I really don't think there is a way to look at it "forwards".

Roles exist to encourage code reuse. I think they should work on the  
"least assumptions" principle. If I add a role to my playbook,  
I expect it to just work. Then I can start tweaking parameters  
exported by the role.

Look at your NTP example in the docs: the NTP server URI is a good  
example of such a parameter.

The author of the NTP role does not want to expect users to have to  
define an NTP server, so s/he provides a default.

The user, however, will want to change that default based on groups  
and hostnames. Conveniently, a look into roles/ntp/vars/main.yml  
suffices to know which parameters exist and what their defaults are.  
The user hence chooses to overwrite $ntp\_server like so:

&nbsp;&nbsp;1. group\_vars/all sets e.g. $ntp\_server to pool.ntp.org  
&nbsp;&nbsp;2. group\_vars/de sets $ntp\_server to de.pool.ntp.org  
&nbsp;&nbsp;3. host\_vars/home-router sets $ntp\_server to my DCF77 clock at  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;home.

However, ansible doesn't work that way.

Instead, the author needs to define $default\_ntp\_server in  
vars/main.yml and always remember to use

&nbsp;&nbsp;{{ $ntp\_server | default $default\_ntp\_server }}

in jinja2 templates, or whatever construct is necessary to give  
precedence to a variable that may or may not exist, before resorting  
to the default. This is extra work, and makes templates and code and  
task lists more complex than they need to be.

The user now cannot copy-paste from vars/main.yml anymore, but needs  
to remember to strip the "default\_" prefix from variables s/he wants  
to override.

On the other hand, it is possible to do things like this in  
a playbook:

---

<div class="post-metadata">

**Author:** ![Brice\_Burgess](https://avatars.discourse-cdn.com/v4/letter/b/d2c977/32.png) [@Brice\_Burgess](https://forum.ansible.com/u/Brice_Burgess)\
**Post date:** [June 11, 2013, 3:15pm UTC](https://forum.ansible.com/t/var-precedence/12865/3 "2013-06-11T15:15:07Z")

</div>

Martin,

I haven’t had time to play with ansible 1.2 yet (but soon will now that it’s released!); but would something like this work:

1. role vars / set $default\_ntp\_server to [pool.ntp.org](http://pool.ntp.org)
2. group\_vars/all sets e.g. $ntp\_server to [{{ $default\_ntp\_server | default pool.ntp.org }}](http://pool.ntp.org)
3. group\_vars/de sets $ntp\_server to [de.pool.ntp.org](http://de.pool.ntp.org)
4. host\_vars/home-router sets $ntp\_server to my DCF77 clock at  
home.

Basically is uses your example but the global group variables will look for the role’s default\_ntp\_server and use that before falling back to its own default for $ntp\_server

---

<div class="post-metadata">

**Author:** ![martin\_f\_krafft](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@martin\_f\_krafft](https://forum.ansible.com/u/martin_f_krafft)\
**Post date:** [June 11, 2013, 3:41pm UTC](https://forum.ansible.com/t/var-precedence/12865/4 "2013-06-11T15:41:27Z")

</div>

also sprach Brice Burgess \<briceburg@gmail.com\> [2013.06.11.1715 +0200]:

> I haven't had time to play with ansible 1.2 yet (but soon will now that  
> it's released!); but would something like this work:
> 
> &nbsp;&nbsp;1. role vars / set $default\_ntp\_server to pool.ntp.org  
> &nbsp;&nbsp;2. group\_vars/all sets e.g. $ntp\_server to {{ $default\_ntp\_server |  
> default pool.ntp.org }} \<[http://pool.ntp.org](http://pool.ntp.org)\>  
> &nbsp;&nbsp;3. group\_vars/de sets $ntp\_server to de.pool.ntp.org  
> &nbsp;&nbsp;4. host\_vars/home-router sets $ntp\_server to my DCF77 clock at  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;home.

Maybe this will work, maybe not. In my opinion, we should not be  
expecting (re-)users of roles to jump through such hoops.

---

<div class="post-metadata">

**Author:** ![GW1](https://avatars.discourse-cdn.com/v4/letter/g/c89c15/32.png) [@GW1](https://forum.ansible.com/u/GW1)\
**Post date:** [June 11, 2013, 7:44pm UTC](https://forum.ansible.com/t/var-precedence/12865/5 "2013-06-11T19:44:37Z")

</div>

```auto
I also think Ansible doesn't do it right, but that is the unintuitive way how you have to deal with variables here.

```

```auto

```

```auto
Greetings,
   gw

```

---

<div class="post-metadata">

**Author:** ![martin\_f\_krafft](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@martin\_f\_krafft](https://forum.ansible.com/u/martin_f_krafft)\
**Post date:** [June 11, 2013, 9:15pm UTC](https://forum.ansible.com/t/var-precedence/12865/6 "2013-06-11T21:15:38Z")

</div>

also sprach GW \<gw.2013@tnode.com\> [2013.06.11.2144 +0200]:

> I also think Ansible doesn't do it right, but that is the  
> unintuitive way how you have to deal with variables here.

As Michael said, it's only unintuitive to me. I would really like to  
hear someone to whom the current way is intuitive, and be able to  
learn.

---

<div class="post-metadata">

**Author:** ![Brian\_Coca1](https://avatars.discourse-cdn.com/v4/letter/b/278dde/32.png) [@Brian\_Coca1](https://forum.ansible.com/u/Brian_Coca1)\
**Post date:** [June 11, 2013, 10:27pm UTC](https://forum.ansible.com/t/var-precedence/12865/7 "2013-06-11T22:27:59Z")

</div>

Works for me, but I use single inventory and use group vars to handle envs.

Brian Coca

---

<div class="post-metadata">

**Author:** ![martin\_f\_krafft](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@martin\_f\_krafft](https://forum.ansible.com/u/martin_f_krafft)\
**Post date:** [June 12, 2013, 6:26am UTC](https://forum.ansible.com/t/var-precedence/12865/8 "2013-06-12T06:26:49Z")

</div>

also sprach Brian Coca \<briancoca@gmail.com\> [2013.06.12.0027 +0200]:

> Works for me, but I use single inventory and use group vars to handle envs.

You are not using roles, right? I think var precedence is okay in  
Ansible, but the way roles are handled is not yet.

---

<div class="post-metadata">

**Author:** ![Ernest0x](https://avatars.discourse-cdn.com/v4/letter/e/ed655f/32.png) [@Ernest0x](https://forum.ansible.com/u/Ernest0x)\
**Post date:** [June 12, 2013, 8:19am UTC](https://forum.ansible.com/t/var-precedence/12865/9 "2013-06-12T08:19:35Z")

</div>

Maybe it helps to make the following thoughts:

If you want playbooks / roles / tasks to be parametrized with variables  
from the inventory then you use inside them variables that are meant to  
be set in the inventory.

Reusing a role in your specific context involves including it in your  
playbook as well as:  
1) Adding the variables needed for that role in your inventory, in which  
case your playbook becomes also reusable, since inventory vars can  
always be organised as needed.  
or  
2) Passing variable overrides at the 'include' step in your playbook, in  
which case the playbook may become less reusable, if the passed  
overrides are too specific.

---

<div class="post-metadata">

**Author:** ![martin\_f\_krafft](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@martin\_f\_krafft](https://forum.ansible.com/u/martin_f_krafft)\
**Post date:** [June 12, 2013, 9:05am UTC](https://forum.ansible.com/t/var-precedence/12865/10 "2013-06-12T09:05:52Z")

</div>

also sprach Petros Moisiadis \<ernest0x@yahoo.gr\> [2013.06.12.1019 +0200]:

> If you want playbooks / roles / tasks to be parametrized with  
> variables from the inventory then you use inside them variables  
> that are meant to be set in the inventory.

Right, but doesn't this mean that I have to set all variables  
exported by all roles in the inventory, unless I can assume that  
every role can handle the absence of a variable and provide sensible  
defaults.

This requires role authors to maintain a documentation about which  
variables are set, which is extra work and could easily get out of  
sync. If all variables are initialised to defaults in vars/main.yml,  
then this file itself serves as reference for which parameters can  
be set.

But also, look at it from another angle:

If variables set within a role always override variables from the  
inventory, why would the role author even care setting a variable?  
Yes, of course, it's better to parametrise your source code's  
tunables just like you might factor out e.g. NUM\_PLAYERS to the top  
of a C source code file for a game, instead of hard-coding the  
number of players in the code. But if that cannot be overridden by  
the user of the code, then it's not a variable but a constant.

So while I still think that role variables should have lower  
precedence than inventory variables (which should have lower  
precedence than variables set in playbooks and include parameters),  
if Michael and others disagree, then maybe vars/main.yml should be  
renamed to constants/main.yml?

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [June 12, 2013, 11:01am UTC](https://forum.ansible.com/t/var-precedence/12865/11 "2013-06-12T11:01:32Z")

</div>

As I posted on May 24th, “group\_vars” is a great place to stick defaults, and as of 1.2, they can also live in your playbook directory.

Variables in roles/foo/main.yml do get loaded. It’s true, they will be used.

However, there’s nothing to say you can’t set parameters to the role in your playbook, like so, too… and is a great way to pass required parameters:

roles:

- { role: foo, x: 42, y: [asdf.example.com](http://asdf.example.com) }

We’re not going to be changing the order of inventory precedence.

---

<div class="post-metadata">

**Author:** ![martin\_f\_krafft](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@martin\_f\_krafft](https://forum.ansible.com/u/martin_f_krafft)\
**Post date:** [June 12, 2013, 12:57pm UTC](https://forum.ansible.com/t/var-precedence/12865/12 "2013-06-12T12:57:55Z")

</div>

also sprach Michael DeHaan \<michael@ansibleworks.com\> [2013.06.12.1301 +0200]:

> As I posted on May 24th, "group\_vars" is a great place to stick  
> defaults, and as of 1.2, they can also live in your playbook  
> directory.

But neither of those are under control of the author of a role, and  
since roles are supposed to encourage reuse of work, they need to be  
self-contained.

> However, there's nothing to say you can't set parameters to the role in  
> your playbook, like so, too... and is a great way to pass required  
> parameters:
> 
> roles:  
> &nbsp;&nbsp;&nbsp;&nbsp;- { role: foo, x: 42, y: asdf.example.com }

Yes, I am aware of this, although I wonder about the difference in  
syntax between this and the way includes get parametrised.

Using this approach, it is possible to override role parameters  
per-playbook, and hence for the group(s) to which the playbook  
applies.

However, if the playbook applies to two groups and you want to  
specify different parameters for each group, or you want to be able  
to use host-specific parameters, you have to resort to something  
like

&nbsp;&nbsp;- { role: foo, x: 42, ntp\_server: $my\_url }

and define $my\_url in group\_vars/host\_vars, rather than just  
defining $ntp\_server there. That's an extra level of indirection and  
hence an extra source of error and confusion…

> We're not going to be changing the order of inventory precedence.

I am not asking you to do that, especially not what is documented  
already. However, roles are a new concept, and they aren't even  
included in the docs on variable precedence yet. Therefore, I don't  
think we are looking to change existing behaviour, but rather  
discussing a new feature. And I think that the way this feature  
currently interacts with variable precedence is non-intuitive and  
suboptimal.

Am I not succeeding in describing why, and what could be done  
instead?

---

<div class="post-metadata">

**Author:** ![Brian\_Coca1](https://avatars.discourse-cdn.com/v4/letter/b/278dde/32.png) [@Brian\_Coca1](https://forum.ansible.com/u/Brian_Coca1)\
**Post date:** [June 12, 2013, 3:54pm UTC](https://forum.ansible.com/t/var-precedence/12865/13 "2013-06-12T15:54:27Z")

</div>

roles can have ‘requried’ values and still be reusable, I don’t see the need of having all needed values populated in the role when it makes more sense to pass them as input.

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [June 12, 2013, 8:32pm UTC](https://forum.ansible.com/t/var-precedence/12865/14 "2013-06-12T20:32:56Z")

</div>

Yep, if the parameter is required, don’t have a default 🙂

---

<div class="post-metadata">

**Author:** ![martin\_f\_krafft](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@martin\_f\_krafft](https://forum.ansible.com/u/martin_f_krafft)\
**Post date:** [June 13, 2013, 6:37am UTC](https://forum.ansible.com/t/var-precedence/12865/15 "2013-06-13T06:37:42Z")

</div>

also sprach Brian Coca \<briancoca@gmail.com\> [2013.06.12.1754 +0200]:

> roles can have 'requried' values and still be reusable, I don't  
> see the need of having all needed values populated in the role  
> when it makes more sense to pass them as input.

Agreed, but then what is vars/main.yml to be used for anyway? Am  
I right in assuming that it's really constants/main.yml?

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [June 13, 2013, 11:09am UTC](https://forum.ansible.com/t/var-precedence/12865/16 "2013-06-13T11:09:27Z")

</div>

It’s for defining any variables you want the role to pull in to vars\_files.

There is no slash “constants/”, it’s called “vars/” and you can put whatever you like in there.

---

<div class="post-metadata">

**Author:** ![David\_Golden](https://avatars.discourse-cdn.com/v4/letter/d/d2c977/32.png) [@David\_Golden](https://forum.ansible.com/u/David_Golden)\
**Post date:** [June 13, 2013, 12:43pm UTC](https://forum.ansible.com/t/var-precedence/12865/17 "2013-06-13T12:43:17Z")

</div>

I'm late to this thread and new-ish to roles, but let me articulate  
what I would find least surprising:

(1) role main.yml variables provide defaults

(2) parameters passed to a role override the variables in main.yml

(3) to the extent that I want the role variable to be dynamic by  
host/group, I have a way to pass a variable as a parameter value to  
the role, e.g. something like:

&nbsp;&nbsp;&nbsp;{ role: foo, x: 42, ntp\_server: $my\_url }

(I'm not actually sure if Ansible works this way or if #3 actually works or not)

In other words, I want to be able to reason about roles like a  
subroutine call. "Execute yourself with these parameters". Global  
variables are bad.

I'm actually not entirely sure how ansible scopes variables. What if  
two roles use the same parameter name? What if a role uses a  
parameter and a host or group var of the same name is used elsewhere?  
If everything is globally scoped, then re-usability is harder because  
anything might stomp on the variable name of something else.

How do I write ordinary (not Ansible) programs with external config?  
I load external config into variables (or a single data structure,  
whatever). I then pass those to functions as needed.

Ansible is pretty much a procedural language and it should encourage  
good variable scoping practices, just as we would for "real" programs.

David

---

<div class="post-metadata">

**Author:** ![martin\_f\_krafft](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@martin\_f\_krafft](https://forum.ansible.com/u/martin_f_krafft)\
**Post date:** [June 13, 2013, 2:04pm UTC](https://forum.ansible.com/t/var-precedence/12865/18 "2013-06-13T14:04:53Z")

</div>

also sprach Michael DeHaan \<michael@ansibleworks.com\> [2013.06.13.1309 +0200]:

> It's for defining any variables you want the role to pull in to  
> vars\_files.

There's a bit of light at the end of the tunnel. Are you suggesting  
that just like roles can be used to factor out tasks and handlers  
from playbooks, vars/main.yml in the role will essentially be  
appended to the vars\_files list of the playbook?

How does this interact with an existing vars\_files list in the  
playbook? Are the role var\_files prepended or appended to the  
existing list?

What happens when multiple roles are used?

Thanks,

---

<div class="post-metadata">

**Author:** ![martin\_f\_krafft](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@martin\_f\_krafft](https://forum.ansible.com/u/martin_f_krafft)\
**Post date:** [June 13, 2013, 2:17pm UTC](https://forum.ansible.com/t/var-precedence/12865/19 "2013-06-13T14:17:10Z")

</div>

also sprach David Golden \<xdg@xdg.me\> [2013.06.13.1443 +0200]:

> (1) role main.yml variables provide defaults  
> (2) parameters passed to a role override the variables in main.yml  
> (3) to the extent that I want the role variable to be dynamic by  
> host/group, I have a way to pass a variable as a parameter value to  
> the role, e.g. something like:
> 
> &nbsp;&nbsp;&nbsp;{ role: foo, x: 42, ntp\_server: $my\_url }

I think this is precisely how things currently work.

> In other words, I want to be able to reason about roles like  
> a subroutine call. "Execute yourself with these parameters".  
> Global variables are bad.

This is a good point! However, it would require proper scoping, but…

> I'm actually not entirely sure how ansible scopes variables.

It does not. If, in the example above, \*any\* role defines $my\_url,  
it will override the value if that was set earlier by a group\_vars  
or host\_vars file or statement.

> What if two roles use the same parameter name?

In that case, one will overwrite the other. I assume that the one  
that's loaded later will overwrite the former.

Maybe the real solution is to scope all variables within Ansible.

&nbsp;&nbsp;- Role variables should be available only within a role;  
&nbsp;&nbsp;- Playbook variables should only be available to that playbook.  
&nbsp;&nbsp;&nbsp;&nbsp;I wouldn't even make them available to included playbooks then.  
&nbsp;&nbsp;- Group and Host variables are available all the time, but maybe  
&nbsp;&nbsp;&nbsp;&nbsp;they can be scoped to group\_vars:: and host\_vars:: respectively.  
&nbsp;&nbsp;- Ansible facts should probably also be scoped, although they  
&nbsp;&nbsp;&nbsp;&nbsp;already are in some ways through the $ansible\_ convention. There  
&nbsp;&nbsp;&nbsp;&nbsp;ought to be a transitional period with DeprecationWarnings,  
&nbsp;&nbsp;&nbsp;&nbsp;IMHO.

---

<div class="post-metadata">

**Author:** ![Michael\_DeHaan2](https://avatars.discourse-cdn.com/v4/letter/m/da6949/32.png) [@Michael\_DeHaan2](https://forum.ansible.com/u/Michael_DeHaan2)\
**Post date:** [June 13, 2013, 3:19pm UTC](https://forum.ansible.com/t/var-precedence/12865/20 "2013-06-13T15:19:09Z")

</div>

Ansible intentionally lacks vars\_scoping.

It’s all part of the simplicity of things to not have to have conversations about scoping.

If you want something named foo\_port, don’t name the variable port, etc.

I recognize that this is different from what some people want, but at the same time being _exactly_ what some people want.

–Michael

[Next page](https://forum.ansible.com/t/var-precedence/12865.md?page=2)
