I almost missed that Ansible 2.24 will be defaulting INJECT_FACTS_AS_VARS to False in future, meaning that facts like {{ ansible_os_distribution }} will stop working.
Instead, we’ll have to write {{ ansible_facts['distribution'] }}.
As somebody on the Fediverse amusingly wrote:
Happy (careful) search & replace day through all your roles and playbooks to those who celebrate.
The original behavior can be brought back (until that is deprecated) with an ansible.cfg setting:
The good thing is that with ansible-core 2.20+, it will tell you when you run the role/playbook/task file which places use the old way to access facts. So it’s not that hard to find these places. Or at least most of them…
The bad thing is that you have to pay careful attention if you’re using plugins such as community.sops.load_vars, since you should better not change “facts” they created since they will change to real variables once ansible-core finally allows it (hopefully in 2.21). It would have been nicer if the deprecation would have only be done after allowing plugins to set variables (and not just facts)…
Maybe the idea is to make it easier to distinguish between facts and variables. Right now their relationship is quite confusing. (And ansible.builtlin.set_fact’s name implying that it “sets facts” while it usually sets variables is not helping…)
Is there an ansible.builtin.set_var in our future? If I’m going to have to examine all my existing set_facts and make syntax changes, would much rather change to a module that actually does what I want rather than uglyfy the syntax to reference “facts” that aren’t actually facts.
In all my years of Ansible use (and accumulated code), I don’t believe I have a single instance of using set_fact to set a fact rather than a variable.
If I understand the discussion above, I’d strongly encourage the core team to reconsider the timing / ordering of these changes. We need a set_var before the current behavior of set_fact wrt variables is taken away.
ansible.builtin.set_fact is already setting variables, you need to provide cacheable: true to make it also set facts. It does make sense to split up these two things though IMO.
This only deprecates the default value of ‘injection’, so we can turn it off by default.
There is a plan on eventually deprecating the injection itself, but that will come later, after the revamp of register to add projections (probably in next version) and adding a way for actions to cleanly and purposefully set variables.
Long story about set_fact: set_fact was initially named set_var but this was misleading and set_host_scoped_var was … too long. So after many discussions set_fact was the name of compromise, misleading one as it did not ‘set a fact’, but a higher priority hope scoped variable with higher priority than facts. Eventually cacheable was added to allow for it also to be stored in the cache … but what really happens under the hood is that 2 variables get created, the aforementioned host scoped variable with higher precedence AND an actual fact, both set to the same value. The fact, gets cached and has ‘fact level precedence’ once retrieved from the cache, in subsequent runs, as in the ‘current’ run the host scoped variable will still exist and have the higher precedence.
I know it was a typo, but I really like the idea of a “hope scoped variable”. Having seen it, I wonder how the field ever advanced this far without the concept.
@felixfontein There is also secondary problem with this change, because it is executing on all variables in play.
This means that if someone defines their extra var with deprecated name, it will trigger only when this variable is consumed in roles, creating confusion that the role is causing it, not actual user defined variable.
Ansible-lint is also not catching this warning, which is not helping because if we could identify it in vars/facts for our roles, we could at least prevent it from our side, leaving issue only coming from user defined extra vars.
@felixfontein This is only topic about INJECT_FACTS_AS_VARS and 2.20 so I added it here.
Issue I described is specific to 2.20+ and deprecation warning, that is not accurate and it is causing confusion because of variable expansion during runtime.
Issue:
Playbook with 10 different roles, each using input variable __my_domain
Playbook executed with __my_domain: "{{ ansible_domain }}"
Result: 100+ warnings for each task that uses __my_domain
This causes lot of confusion because it appears that roles are problem, not input variable. ansible-lint is also not showing this warning either.
I think this is severely overstating how much potential there is for confusion.
Here’s setting a variable referenced in a role from the command line:
$ ansible-playbook test.yml -e 'foo={{ ansible_fqdn }}'
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************
ok: [localhost]
TASK [foo : debug] ***********************************************************************************************************
[WARNING]: Deprecation warnings can be disabled by setting `deprecation_warnings=False` in ansible.cfg.
[DEPRECATION WARNING]: INJECT_FACTS_AS_VARS default to `True` is deprecated, top-level facts will not be auto injected after the change. This feature will be removed from ansible-core version 2.24.
Origin: <CLI option '-e'>
{{ ansible_fqdn }}
Use `ansible_facts["fact_name"]` (no `ansible_` prefix) instead.
ok: [localhost] =>
msg: ip-10
Vs. setting the variable in the playbook:
[DEPRECATION WARNING]: INJECT_FACTS_AS_VARS default to `True` is deprecated, top-level facts will not be auto injected after the change. This feature will be removed from ansible-core version 2.24.
Origin: /home/testing/test.yml:4:10
2 gather_facts: true
3 vars:
4 foo: "{{ ansible_fqdn }}"
^ column 10
Use `ansible_facts["fact_name"]` (no `ansible_` prefix) instead.
Vs. getting it from a host variable:
[DEPRECATION WARNING]: INJECT_FACTS_AS_VARS default to `True` is deprecated, top-level facts will not be auto injected after the change. This feature will be removed from ansible-core version 2.24.
Origin: /home/testing/group_vars/all.yml:1:6
1 foo: "{{ ansible_fqdn }}"
^ column 6
Use `ansible_facts["fact_name"]` (no `ansible_` prefix) instead.
In each case I tested the warning told me exactly where the issue originated rather than pointing at the role that merely used it, even though I did my best to trick it by referencing the variable indirectly in the role:
- debug:
msg: "{{ bar }}"
vars:
bar: "{{ foo[:5] }}"