Chapter 4: Managing Variables and Facts

Exercise: Managing facts

You will print a host's facts, pick out specific ones, then create a custom fact file on servera and write a play that decides what to install and start purely from that file.

Managing facts

  • Gather facts from a host.
  • Create tasks that use the gathered facts, including custom facts.

Prerequisites: Complete control-node setup and the preceding lessons in this chapter. Run commands as student on workstation unless a step names another machine.

From the project directory, run the relevant home-lab check. For an exercise with several checkpoints, grade each state before changing or removing it:

lab grade data-facts

The grader reads project files and host state. A passing report covers the listed checks; also perform the task’s independent connection, repeat-run, and reboot checks where requested.

Change a custom fact to request a stopped service. Gather facts again and predict the resulting service state.

Record the published result first. The grader checks the original requirements, so a changed name or value may intentionally fail those checks.

Make servera's web configuration depend on gathered facts rather than package and service names hardcoded in tasks.

  • display_facts.yml must report all facts; display_specific_facts.yml must report FQDN, Python version, processor count, memory, and local custom facts.
  • /etc/ansible/facts.d/custom.fact must describe the web package and service, requested service state, and enabled setting.
  • playbook.yml must apply those custom values. check_httpd.yml must demonstrate the service state before and after configuration.
  • Explain why editing a fact file requires gathering facts again before using the new values.

Move into the project directory

[student@workstation ~]$ cd ~/data-facts
[student@workstation data-facts]$

Print every fact

Create display_facts.yml for the webserver host and run it. Scroll through the output and find a few facts you recognise.

---
- name: Display ansible_facts
  hosts: webserver
  tasks:
    - name: Display facts
      ansible.builtin.debug:
        var: ansible_facts
[student@workstation data-facts]$ ansible-navigator run \
> -m stdout display_facts.yml
...output omitted...
        "system": "Linux",
        "system_vendor": "Red Hat",
        "user_dir": "/root",
...output omitted...

The user_* facts describe the account that ran setup. Here they show root, which means this project escalates privileges.

Print specific facts

Create display_specific_facts.yml that prints one sentence built from four facts: the FQDN, the Python version, the processor count and the total memory in MiB.

---
- name: Display specific ansible_facts
  hosts: webserver
  tasks:
    - name: Display specific facts
      ansible.builtin.debug:
        msg: >
          Host "{{ ansible_facts['fqdn'] }}" with Python
          version "{{ ansible_facts['python_version'] }}" has
          "{{ ansible_facts['processor_count'] }}" processors and
          "{{ ansible_facts['memtotal_mb'] }}" MiB of total system memory.
ok: [servera.lab.example.com] => {
    "msg": "Host \"servera.lab.example.com\" with Python version \"3.9.10\" has
            \"1\" processors and \"960\" MiB of total system memory.\n"
}

Check for custom facts

Add a second task to display_specific_facts.yml that prints ansible_facts['ansible_local'], and run it again. It is empty, because no custom facts exist yet.

    - name: Display ansible_local variable
      ansible.builtin.debug:
        msg: The ansible_local variable is set to "{{ ansible_facts['ansible_local'] }}"
    "msg": "The ansible_local variable is set to \"{}\""

Create a custom fact file on servera

Log in to servera, create the facts directory, and write custom.fact describing what the host should run.

[student@workstation data-facts]$ ssh servera
[student@servera ~]$ sudo mkdir -p /etc/ansible/facts.d
[sudo] password for student: student
[student@servera ~]$ sudo vim /etc/ansible/facts.d/custom.fact
[general]
package = httpd
service = httpd
state = started
enabled = true

Log out of servera when done.

Write a play driven by custom facts

Create playbook.yml with a play, Install Apache and starts the service, for the webserver group. It has two tasks:

  1. install the latest version of the package named by the custom fact package;
  2. manage the service named by service, using the state and enabled custom facts.
---
- name: Install Apache and starts the service
  hosts: webserver

  tasks:
    - name: Install the required package
      ansible.builtin.dnf:
        name: "{{ ansible_facts['ansible_local']['custom']['general']['package'] }}"
        state: latest

    - name: Start the service
      ansible.builtin.service:
        name: "{{ ansible_facts['ansible_local']['custom']['general']['service'] }}"
        state: "{{ ansible_facts['ansible_local']['custom']['general']['state'] }}"
        enabled: "{{ ansible_facts['ansible_local']['custom']['general']['enabled'] }}"

Nothing in the playbook says "httpd": change the fact file on a host and the same playbook installs something else there.

Check the syntax

[student@workstation data-facts]$ ansible-navigator run \
> -m stdout playbook.yml --syntax-check
playbook: /home/student/data-facts/playbook.yml

Confirm httpd is not running yet

Create check_httpd.yml, which runs systemctl status httpd, registers the result and prints it. Run it: it fails, because httpd is not installed yet.

---
- name: Check httpd status
  hosts: webserver
  tasks:
    - name: Check httpd status
      ansible.builtin.command: systemctl status httpd
      register: result

    - name: Display http status
      ansible.builtin.debug:
        var: result
fatal: [servera.lab.example.com]: FAILED! => {"changed": false, "msg": "Could not
find the requested service httpd: host"}

Run the fact-driven playbook

[student@workstation data-facts]$ ansible-navigator run -m stdout playbook.yml
...output omitted...
TASK [Install the required package] ********************************************
changed: [servera.lab.example.com]

TASK [Start the service] *******************************************************
changed: [servera.lab.example.com]

PLAY RECAP *********************************************************************
servera.lab.example.com    : ok=3    changed=2    unreachable=0    failed=0  ...

Check again

Run check_httpd.yml once more. The registered result now shows the service loaded, enabled and active (running).

        "stdout_lines": [
            "● httpd.service - The Apache HTTP Server",
            "     Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; ...)",
            "     Active: active (running) since ...",
...output omitted...

Finish

Check your work with lab grade data-facts, then put the exercise away with lab finish data-facts.