# Undefined variables

**URL:** <https://forum.ansible.com/t/undefined-variables/12963>\
**Category:** Ansible Project\
**Created:** [June 14, 2013, 8:40pm UTC](https://forum.ansible.com/t/undefined-variables/12963 "2013-06-14T20:40:10Z")\
**Posts on this page:** 12\
**Page:** 1

<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 14, 2013, 8:40pm UTC](https://forum.ansible.com/t/undefined-variables/12963/1 "2013-06-14T20:40:10Z")

</div>

Hello,

When Ansible encounters an undefined variable, it just uses the  
variable instance verbatim, e.g.:

&nbsp;&nbsp;% ansible yellow.virt.local -m debug -a 'msg=x${foo}x'  
&nbsp;&nbsp;yellow.virt.local | success \>\> {  
&nbsp;&nbsp;&nbsp;&nbsp;"msg": "x${foo}x"  
&nbsp;&nbsp;}

Is it possible to disable this behaviour? I really expect that  
message to be just "xx", don't you?

Or is there another way to achieve what I want, which I am not  
seeing?

Thanks,

---

<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 14, 2013, 8:55pm UTC](https://forum.ansible.com/t/undefined-variables/12963/2 "2013-06-14T20:55:50Z")

</div>

Ansible only substitutes variables it knows about, since you can have ${var} in shell/command/raw/etc and not have it be an ansible var.

---

<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 14, 2013, 11:05pm UTC](https://forum.ansible.com/t/undefined-variables/12963/3 "2013-06-14T23:05:16Z")

</div>

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

> Ansible only substitutes variables it knows about, since you can have  
> ${var} in shell/command/raw/etc and not have it be an ansible var.

Of course, but no shell/command/raw/environment/whatever variable  
could ever make it into a playbook and be interpolated there.

The behaviour in Ansible is unique, to my knowledge, and as such  
confusing and difficult to deal with. Every other software or tool  
I know will either expand undefined variables with nothing, or emit  
an error. Simply not expanding the variable and leaving the sentinel  
in place is new to me.

We recently picked up the topic of variable precedence again[0], and  
many have since suggested what David proposes in

&nbsp;&nbsp;[https://groups.google.com/d/msg/ansible-project/Gd\_7P3Ei\_Vs/vqTB1BvrqEUJ](https://groups.google.com/d/msg/ansible-project/Gd_7P3Ei_Vs/vqTB1BvrqEUJ)

which is to treat roles as imperative constructs and to pass  
variables to them, like so:

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

Now, if $my\_url isn't defined anywhere, the role will get "$my\_url"  
verbatim, which makes it rather hard to catch an error and alert the  
user about forgetting to specify this "required" parameter.

This is even worse in case when you want to support optional  
parameters, e.g. imagine a role assembling /etc/motd from parts. You  
might want to provide a way for a single host to override the entire  
file with a message, e.g.

&nbsp;&nbsp;{ role: motd, override: $motd\_override }

In this case, it wouldn't be an error to leave the variable  
$motd\_override undefined, but the result would be… yes:

&nbsp;&nbsp;echo \$motd\_override \> /etc/motd

which is surely not what anyone wants.

Let's recap:

&nbsp;&nbsp;1. The only way to override defaults set in roles in vars/main.yml  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;is by passing parameters to the role, like above;

&nbsp;&nbsp;2. If a non-existent variable is referenced, the reference itself  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;is echoed, e.g. "$motd\_override" in the above;

&nbsp;&nbsp;3. Therefore it is not possible to export optional parameters from  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;roles, which would be tested inside the role or jinja2  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;template, e.g. {% if override is defined %} — this will always  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield true, because if $motd\_override is undefined, the role  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parameter 'override' will end up being defined to be verbatim  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"$motd\_override".

Michael, is this also behaviour in Ansible that some people want and  
thus you won't change?

---

<div class="post-metadata">

**Author:** ![Lorin\_Hochstein1](https://avatars.discourse-cdn.com/v4/letter/l/ce73a5/32.png) [@Lorin\_Hochstein1](https://forum.ansible.com/u/Lorin_Hochstein1)\
**Post date:** [June 14, 2013, 11:16pm UTC](https://forum.ansible.com/t/undefined-variables/12963/4 "2013-06-14T23:16:12Z")

</div>

I have a pull request in to fail on undefined errors:

​[https://github.com/ansible/ansible/pull/3066](https://github.com/ansible/ansible/pull/3066)

​Lorin

---

<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 15, 2013, 8:16am UTC](https://forum.ansible.com/t/undefined-variables/12963/5 "2013-06-15T08:16:14Z")

</div>

also sprach Lorin Hochstein \<lorin@nimbisservices.com\> [2013.06.15.0116 +0200]:

> I have a pull request in to fail on undefined errors:
> 
> [https://github.com/ansible/ansible/pull/3066](https://github.com/ansible/ansible/pull/3066)

Oh, good to know!

However, I am still wondering how to properly parametrise roles.

One could use a role's vars/main.yml only for variables that, if  
needed to be overridden, will need to be specifically declared by  
the role user, e.g.

&nbsp;&nbsp;[…]  
&nbsp;&nbsp;roles  
&nbsp;&nbsp;&nbsp;&nbsp;- role: postfix  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;postfix\_port: 26

In this example, the postfix role author was smart enough not to  
hardcode the SMTP port, but put it into a variable. Most users don't  
need to worry about that, but if you really need to, you can change  
the internals of the role like this.

Optional parameters of a role would always need to handle the case  
in which they are not defined in the global namespace, e.g.

&nbsp;&nbsp;smtpd\_banner = {{ postfix\_smtpd\_banner | default $postfix\_default\_smtpd\_banner }}  
&nbsp;&nbsp;# $postfix\_default\_smtpd\_banner would be define in vars/main.yml

And anything that the role author expects the user to provide (in  
the global namespace), i.e. parameters for which there are no  
sensible default, would require Lorin's patch to get included in  
mainline, because there is no way to test a whether a variable value  
starts with '$' in Jinja, and it's not a reliable test anyway.

Apart from Lorin's patch not being in mainline yet, I can identify  
at least the following two shortcomings with this approach:

&nbsp;&nbsp;1. It expects the user and all role authors to share global  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;namespace, and it's questionable whether a convention like  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a 'rolename\_' prefix will be adopted by everyone;

&nbsp;&nbsp;2. It's potentially confusing to the user, who has to understand  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;precisely what's going on to be able to chose whether to pass  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a variable to a role in the playbook, or whether to rely on the  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;global namespace being used, and all conventions abided.

All in all, I think roles are a great concept, and Lorin's patch  
necessary to make the variable behaviour of Ansible follow the  
principle of least surprise. However, I don't think roles in 1.2 are  
ready to be called "final" and we should take some time to polish  
what there is right now.

---

<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 15, 2013, 1:42pm UTC](https://forum.ansible.com/t/undefined-variables/12963/6 "2013-06-15T13:42:13Z")

</div>

@martin, on your own example on the thread with roles and postfix, this would break.

I’m not saying it should not be added as an option, just that it is not as simple as it seems and that the consequences reach much father than one would think. The original reason for not doing this is still valid, its hard for the parser to know if its a variable or an entry it needs to pass unless the match is exact.

---

<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 15, 2013, 2:31pm UTC](https://forum.ansible.com/t/undefined-variables/12963/7 "2013-06-15T14:31:42Z")

</div>

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

> @martin, on your own example on the thread with roles and postfix,  
> this would break.

What would break?

---

<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 15, 2013, 3:35pm UTC](https://forum.ansible.com/t/undefined-variables/12963/8 "2013-06-15T15:35:17Z")

</div>

From your post:

> smtpd\_banner = {{ postfix\_smtpd\_banner | default $postfix\_default\_smtpd\_banner }}

if ansible ALWAYS subsitutes $ or {{}} vars in this case you cannot use a postfix variable as default would return a blank

2013/6/15 martin f krafft \<[madduck@madduck.net](mailto:madduck@madduck.net)\>

---

<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 15, 2013, 3:35pm UTC](https://forum.ansible.com/t/undefined-variables/12963/9 "2013-06-15T15:35:19Z")

</div>

also sprach martin f krafft \<madduck@madduck.net\> [2013.06.14.2240 +0200]:

> When Ansible encounters an undefined variable, it just uses the  
> variable instance verbatim, e.g.:
> 
> &nbsp;&nbsp;% ansible yellow.virt.local -m debug -a 'msg=x${foo}x'  
> &nbsp;&nbsp;yellow.virt.local | success \>\> {  
> &nbsp;&nbsp;&nbsp;&nbsp;"msg": "x${foo}x"  
> &nbsp;&nbsp;}

Here's an interesting twist to this issue:

The documentation says that from v1.2 onwards, ${style} variables  
are considered "legacy" and Jinja2-style {{variables}} are  
encouraged.

The documentation explicitly states:

&nbsp;&nbsp;If you ever want to do anything complex like uppercasing a string,  
&nbsp;&nbsp;{{ varname }} is best, as it uses the Jinja2 templating engine.

And it works. I can say (quotes required for YAML parser):

&nbsp;&nbsp;'{{ mycolour | upper }}'

suggesting that I can use Jinja2 filters in Playbooks. Yay!

… except that this all fails horribly when variables are undefined.

If there is no 'mycolor' variable defined, the above "expands" to

&nbsp;&nbsp;{{ mycolour | upper }}

Another filter defined by Jinja2 is 'default', and so I thought  
I should be able to guard against this:

&nbsp;&nbsp;'{{ mycolor | default blue }}'

This expands to mycolour, if that is defined, but if mycolour is not  
defined, then… yes, the whole thing is reproduced verbatim.

I doubt that this is intentional.

Erroring when an undefined variable is used is a good steo towards  
more robust playbooks, and I welcome being able to use Jinja2 in  
playbooks, but when the 'default' filter is used, no error should be  
emitted if the variable is undefined.

---

<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 15, 2013, 3:50pm UTC](https://forum.ansible.com/t/undefined-variables/12963/10 "2013-06-15T15:50:42Z")

</div>

This one is tricky, it also doesn’t work when the variable is defined and NULL. Conflicting interests here between trying to be non obtrusive on substitution and the jinja2 expression’s intention. See example below.

> s expands to mycolour, if that is defined, but if mycolour is not  
> defined, then… yes, the whole thing is reproduced verbatim.
> 
> I doubt that this is intentional.

vars:  
foo: null  
tasks:

- debug: msg=“{{foo}}”
- debug: msg=“{{foo | default ‘empty’}}”

ok: [127.0.0.1] =\> {“msg”: “None”}

ok: [127.0.0.1] =\> {“msg”: “{{foo | default ‘empty’}}”}

---

<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 15, 2013, 4:09pm UTC](https://forum.ansible.com/t/undefined-variables/12963/11 "2013-06-15T16:09:22Z")

</div>

Variables in the “vars/” of the role get pulled in because they are useful to pull in common variables and other things.

You also can do:

roles:

- { role: foo, x: 42, y: ‘blarg’ }

Should you want to pass them local parameters.

I’m finding your last paragraph increasingly negative though and we seem to be talking past each other, so I suggest we no longer discuss this subject.

---

<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 15, 2013, 4:18pm UTC](https://forum.ansible.com/t/undefined-variables/12963/12 "2013-06-15T16:18:05Z")

</div>

It’s important that ansible returns the variables you do not expect or has a mode to make them raise failure messages.

We do not want them substituting empty text as that would be a rather confusing thing to everyone.

Being able to see the undefined value in the output allows you to correct it, and may be in many ways less frustrating than having it only get so far as the first error and not like you, especially if something like $x was in a file because, say, it was a perl file or a comment, and not meant to be expanded to a variable.

So, there are pretty good reasons for it working the way it does 🙂
