I’m trying to call out to a task list with include_tasks where I want the task list to set a particular named fact, passed as a variable to the task list. I can set the fact, but it’s not appearing in ansible_facts as I would expect.
Here’s a playbook with five tasks:
Sets the fact according to a fact name passed as a variable
Retrieves the fact, proving it’s set
Gets a fact from ansible_facts, showing how I expect that to work
Gets the fact by name, which doesn’t work as I expect
Gets the fact according to a fact name passed as a variable, what I’m trying to achieve
Can someone explain why step 4 doesn’t work, and how it should be done?
---
- hosts: all
tasks:
- name: Set first fact with dynamic fact name
ansible.builtin.set_fact: {"{{ fact_name }}":"hello world"}
vars:
fact_name: "fact1"
- name: Get first fact as variable - WORKS
ansible.builtin.debug:
var: fact1
- name: Get processor cores from ansible_facts - WORKS
ansible.builtin.debug:
var: ansible_facts["processor_cores"]
- name: Get first fact from ansible_facts - DOESN'T WORK
ansible.builtin.debug:
var: ansible_facts["fact1"]
- name: Get first fact from ansible_facts with dynamic fact name - DOESN'T WORK
ansible.builtin.debug:
var: ansible_facts[fact_name]
vars:
fact_name: "fact1"
The reason is that ansible.builtin.set_fact does not create facts, but variables (more precisely: host-level variables) with a fixed value. The same happens if you don’t use a dynamic fact name, but a static fact name. You can fix that by making it a cacheable fact by setting cacheable=true, which will create a host-level variable and a “real” Ansible fact:
- name: Set first fact with dynamic fact name
ansible.builtin.set_fact:
"{{ fact_name }}": hello world
cacheable: true
vars:
fact_name: "fact1"
Thanks! I saw cacheable: true and could see that it was something important, but I couldn’t make head or tail of the explanation. 7 levels of precedence sounds like acute demonology. I used your example and now I made it all go the way I wanted.
The reason is that ansible.builtin.set_fact does not create facts, but variables
Perhaps it should be called set_variable, and have an option set_fact: true? Or, if that can’t work for demonology-related reasons, perhaps a slightly simpler warning in the docs?
Well, to be honest, I wasn’t really aware of this behavior before myself, since I always used set_fact as a sort of “set variable to a constant value” (as opposed to vars, which sets them to something that’s evaluated on every usage). I agree that set_variable would be a better name, but I guess the naming comes from the early history of Ansible and hasn’t been changed since it’s used everywhere.
Perhaps it should be called set_variable , and have an option set_fact: true ?
Please don’t give them any ideas
Seagulls are called seagulls despite them not being exclusively tied to the sea, humanity moves on and doesn’t need to rewrite every piece of literature in existence for correctness sake. I say this with peace and love, of course!
In my particular case, I wanted a way to set a “thing” (fact, variable, don’t mind) with a name string and retrieve it with the same name string. Having an ansible_vars array similar to ansible_facts would have worked just as well. But I couldn’t find anything like that. Seems like there’s a surprising asymmetry there.