Hello everyone. This is part 6 of the Ansible 101 guide. In this part I would like to take a look at the so-called facts with you. These are not difficult to understand, but can make our lives easier for many tasks with Ansible.
What are facts?
You may have noticed this part at the beginning of every playbook run:
TASK [Gathering Facts] *****************************************************************************
ok: [ansible-guide-1]
This is a task that Ansible performs by default when fact collection is enabled. Facts are variables that Ansible automatically sets at the start of a playbook. They contain all sorts of information about the current play, its target systems and the current environment.
Here are a few examples:
ansible_hostname: contains the hostname of the current host as determined by Ansible
inventory_hostname: contains the hostname of the current host as defined in the inventory
ansible_default_ipv4.address: Contains the first primary IPv4 address found by Ansible.
ansible_distribution: Contains the name of the target host's OS distribution, e.g. 'Ubuntu', 'Debian' or 'Suse'.
We can display all available facts about a system with a small ad-hoc command:
ansible ansible-guide-1 -i inventory.txt -m setup
The output is a very large block in JSON (JavaScript Object Notation) format, which I will not include here for the sake of clarity. Try it out for yourself!
Enable/Disable Fact Gathering
By default fact gathering is enabled for each playbook. However it takes its time and can be disabled if you are not using any facts:
- hosts: all
gather_facts: no # <-- disable fact gathering
tasks:
- name: task 1
ansible.builtin.debug:
msg: "fact gathering is off"
My recommendation is to activate the fact collection only when we really need to access it. This can significantly reduce the runtime of a playbook.
Accessing facts in a playbook
Since facts are ultimately variables, accessing them is identical. For example, we can display the current distribution and OS family:
- hosts: all
tasks:
- name: print current distribution
ansible.builtin.debug:
msg: "My distribution is {{ ansible_distribution }}
- name: print current os family
ansible.builtin.debug:
msg: "My os family is {{ ansible_os_family }}"
In addition to the facts provided by Ansible, we can also define our own. This is useful whenever we want to set our own variables at runtime.
In addition to the facts provided by Ansible, we can also define our own. This is always useful when we want to set our own variables at runtime.
We use the set_fact module to do this:
- hosts: ansible-guide-1
gather_facts: true
tasks:
- name: set facts
ansible.builtin.set_fact:
my_custom_fact_1: "First fact set at runtime"
my_custom_fact_2: "Second fact set at runtime"
another_custom_fact: "Hello World"
- name: Output
ansible.builtin.debug:
msg: "{{ my_custom_fact_1 }}"
We can use the "set_fact" module to manually define one or more facts. When defining, we can also access facts or variables again:
- hosts: ansible-guide-1
gather_facts: true
tasks:
- name: set facts
ansible.builtin.set_fact:
my_custom_fact: "My Distribution: {% raw %}{{ ansible_distribution }}{% endraw %}"
- name: Output
ansible.builtin.debug:
msg: "{{ my_custom_fact }}"
PLAY [localhost] ****************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [ansible-guide-1]
TASK [set facts] ****************************************************************************
ok: [ansible-guide-1]
TASK [Output] ****************************************************************************
ok: [ok: [ansible-guide-1]] => {
"msg": "My Distirubtion: Ubuntu"
}
PLAY RECAP ****************************************************************************
ok: [ansible-guide-1] : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The facts defined with the "set_fact" module are only available AFTER the task has been executed. This means that we cannot access facts defined in the same task:
- hosts: ansible-guide-1
tasks:
- name: This is NOT working!
ansible.builtin.set_fact:
fact_1: "I am fact_1"
fact_2: "fact_1: {{ fact_1 }}"
PLAY [ansible-guide-1] ****************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [ansible-guide-1]
TASK [This is NOT working!] ****************************************************************************
fatal: [ansible-guide-1]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'fact_1' is undefined\n\nThe error appears to be in '/home/sseibold/blog/facts.yml': line 5, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Das hier funktioniert NICHT!\n ^ here\n"}
PLAY RECAP ****************************************************************************
ansible-guide-1 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
The task fails because the fact_1 variable is not yet available. This is only the case after the task is run. So the playbook should look like this:
- hosts: ansible-guide-1
tasks:
- name: This works!
ansible.builtin.set_fact:
fact_1: "I am fact_1"
- name: fact_2 in new Task
ansible.builtin.set_fact:
fact_2: "fact_1: {% raw %}{{ fact_1 }}"
- name: Output
ansible.builtin.debug:
msg: "{% raw %}{{ fact_2 }}{% endraw %}"
PLAY [ansible-guide-1] ****************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [ansible-guide-1]
TASK [Das hier funktioniert] ****************************************************************************
ok: [ansible-guide-1]
TASK [fact_2 in neuem Task definieren] ****************************************************************************
ok: [ansible-guide-1]
TASK [Ausgabe] ****************************************************************************
ok: [ansible-guide-1] => {
"msg": "fact_1 lautet: ich bin fact_1"
}
PLAY RECAP ****************************************************************************
ansible-guide-1 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Now that the fact "fact_1" has been defined in a previous task, we can access it in the next task.
Conclusion
This brings us to the end of this short chapter in which we have learned what facts are and how to access them. We will be using facts a lot in practical examples throughout this guide.
What happens next?
In the next part of this series, we will look at return values. As always, I hope this guide has been helpful to you so far.
I would love to hear your feedback. Just leave a comment here or send me an email!
See you soon!
Mow