If a variable exist, but contains undefined value. Its considered undefined.
In the past this would create a error.
Is this a bug?
and if it works as intended is there a way to get the old behavior?
problem
For example:
test.j2:
{% if a is defined %}
{{ a[0] }}
{% endif %}
a: [5,{{b}}]
If you run this with template module:
In the past this gave a error AnsibleUndefinedVariable for b
Now, it results in a empty file. because it considers variable a undefined.
Now if someone makes one mistake when changing a variable.
whole blocks of configuration are dropped, without a error.
We had a example were most of our firewall rules got silently dropped.
Have not yet found out which version of ansible still gave the error.
But i have confirmed it at least gave error in past. by trying it in centos7 container
in ansible-core 2.14 an expression {{ defined_variable or undefined_variable }} does not fail on undefined_variable if the first part of or is evaluated to True as it is not needed to evaluate the second part. One particular case of a change in behavior to note is the task below which uses the undefined test. Prior to version 2.14 this would result in a fatal error trying to access the undefined value in the dictionary
Personally, I would have expected the current behavior, and the old behavior would have surprised me, and I’ve been using Ansible since 2.8.
Perhaps it might be a better idea to go over what you’re actually doing, and maybe we can help with structuring your Ansible playbook/role? Good practices with Ansible is to try and provide default() values for any var that might not always be “defined” and then determine what to do when those vars are defined/undefined/default(). A “partially defined” var should be an undefined var, like what you’re seeing now in your template, the jinja2 condition evaluates to false, and nothing is written (rather than erroring because why should “variable is defined” ever return an error?)
I appreciate the problem you’ve stumbled into, and the “solution”.
However, if you’re going to define variables in terms of other variables, I think you’d do well to express reasonable defaults wherever that happens. For example (and this stretches the notional of “reasonable”):
# rather than
a: [5, '{{ b }}']
# do this instead
a: [5, '{{ b | default("Noooooo!") }}']
@Denney-tech that might be a good plan, I am still considering to create a gitlab issue/bug-report.
But if there are nice solutions for the problems that i have. that might not be needed.
example
A example where we noticed this change was in our role that mange’s iptables
that manages iptables.
This roles uses variables like:
iptables_common_rules_inbound
iptables_application_rules_inbound
iptables_common_rules_outbound
iptables_application_rules_outbound
And this implements by create iptables configuration file
{% iptables_common_rules_inbound is defined %}
{% for rule in iptables_common_rules_inbound %}
# {{ rule.description }}
{{ rule.iptables_rule }}
{% endfor %}
{% endif %}
But i think most problems are resolved if this becomes:
{% for rule in iptables_common_rules_inbound %}
# {{ rule.description }}
{{ rule.iptables_rule }}
{% endfor %}