## 1. How Facts Work
“`text
[Control Node] — gather_facts —> [Managed Host]
<— returns JSON —
“`
Ansible runs the **setup module** at the start of every play (unless disabled) and stores the results as variables accessible via `ansible_facts.*`.
—
## 2. Viewing Facts
“`bash
# View all facts for a host
ansible web1 -m setup
# Filter facts by key pattern
ansible web1 -m setup -a “filter=ansible_os*”
ansible web1 -m setup -a “filter=ansible_memory*”
“`
—
## 3. Commonly Used Facts
“`yaml
# OS and Distribution
ansible_facts[‘os_family’] # RedHat, Debian, etc.
ansible_facts[‘distribution’] # CentOS, Ubuntu, etc.
ansible_facts[‘distribution_version’] # 22.04, 8.5, etc.
# Network
ansible_facts[‘default_ipv4’][‘address’] # Primary IP
ansible_facts[‘hostname’] # Short hostname
ansible_facts[‘fqdn’] # Fully qualified name
# Hardware
ansible_facts[‘processor_count’] # Number of CPUs
ansible_facts[‘memtotal_mb’] # Total RAM in MB
ansible_facts[‘mounts’] # List of mount points
# System
ansible_facts[‘env’][‘HOME’] # Environment variable
ansible_facts[‘date_time’][‘date’] # Current date
ansible_facts[‘pkg_mgr’] # yum, apt, dnf, etc.
“`
—
## 4. Code Laboratory: Practical Playbooks
### Example 1: Printing All Available Facts
This playbook gathers and displays the entire JSON schema of facts available on the target machine.
“`yaml
—
– name: Print All System Facts
hosts: all
gather_facts: yes
become: no
tasks:
– name: Display entire ansible_facts dictionary
ansible.builtin.debug:
var: ansible_facts
“`
### Example 2: Cross-Platform Apache Installation (Ubuntu & RedHat)
This playbook uses `gather_facts: yes` to detect the OS family and choose correct packages dynamically.
“`yaml
—
– name: Install and Configure Apache on Ubuntu and RedHat
hosts: localhost
connection: local
gather_facts: yes
become: yes # Required to install packages and modify services
tasks:
– name: 1. Set OS-specific variables dynamically
ansible.builtin.set_fact:
apache_package: “{{ ‘apache2’ if ansible_facts[‘os_family’] == ‘Debian’ else ‘httpd’ }}”
apache_service: “{{ ‘apache2’ if ansible_facts[‘os_family’] == ‘Debian’ else ‘httpd’ }}”
– name: 2. Install Apache Package
ansible.builtin.package:
name: “{{ apache_package }}”
state: present
– name: 3. Create a basic index.html file
ansible.builtin.copy:
dest: /var/www/html/index.html
content: “<h1>Hello World from {{ ansible_facts[‘distribution’] }}!</h1>”
mode: ‘0644’
– name: 4. Ensure Apache service is started and enabled
ansible.builtin.service:
name: “{{ apache_service }}”
state: started
enabled: yes
“`
—
## 5. Disabling Fact Gathering
“`yaml
—
– name: Fast playbook (no facts needed)
hosts: all
gather_facts: false # Skip setup module execution
tasks:
– name: Simple network ping
ansible.builtin.ping:
“`
> 💡 **When to disable:** Use this on large server inventories when your tasks do not require system metadata. It speeds up playbook execution significantly.
—
## 6. Caching Facts
To avoid the time penalty of gathering facts on every single play, configure fact caching.
“`ini
# Location: /etc/ansible/ansible.cfg
[defaults]
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 86400 # Re-use facts for 24 hours
“`
—
## 7. Custom Local Facts
You can seed static, custom facts directly onto your managed targets.
**Step 1:** Create an `.fact` file on the managed target node:
“`bash
mkdir -p /etc/ansible/facts.d
cat > /etc/ansible/facts.d/app.fact << ‘EOF’
[application]
version=2.5
environment=production
owner=devops-team
EOF
“`
**Step 2:** Access the custom properties inside your playbook:
“`yaml
– name: Read local custom facts
ansible.builtin.debug:
msg: “App version is {{ ansible_local[‘app’][‘application’][‘version’] }}”
“`
> 💡 Custom local facts are always exposed under the `ansible_local` namespace variable.
—
## 8. The `setup` Module: Filtering & Subsets
If you only need specific metrics, optimize your workflow by targeting subsets.
“`yaml
– name: Gather only network and hardware data
ansible.builtin.setup:
gather_subset:
– network
– hardware
– name: Filter out specific network interface keys
ansible.builtin.setup:
filter: “ansible_eth*”
“`
> **Available Subsets:** `all`, `min`, `hardware`, `network`, `virtual`, `ohai`, `facter`
“`yaml
—
– name: demo
become: yes
gather_facts: yes
hosts: nodes
tasks:
– name: 1. Set OS-specific variables dynamically
ansible.builtin.set_fact:
apache_package: “{{ ‘apache2’ if ansible_facts[‘ansible_distribution’] == ‘ubuntu’ else ‘httpd’ }}”
apache_service: “{{ ‘apache2’ if ansible_facts[‘os_family’] == ‘Debian’ else ‘httpd’ }}”
service_owner: “{{ ‘www-data’ if ansible_facts[‘os_family’] == ‘Debian’ else ‘apache’ }}”
service_group: “{{ ‘apache’ if ansible_facts[‘os_family’] == ‘redhat’ else ‘www-data’ }}”
– ansible.builtin.debug:
msg: “Installing tree package”
– name: 2. Install Apache Package
ansible.builtin.package:
name: “{{ apache_package }}”
state: present
notify: restart service apcahe
– name: Install unzip
ansible.builtin.package:
name: unzip
state: present
– name: Download gaming html
ansible.builtin.get_url:
url: https://templatemo.com/tm-zip-files-2020/templatemo_589_lugx_gaming.zip
dest: /tmp/templatemo_589_lugx_gaming.zip
mode: ‘0755’
notify: restart service apcahe
when:
– name: unzip templated in apache2 html
ansible.builtin.unarchive:
src: /tmp/templatemo_589_lugx_gaming.zip
dest: /tmp/
remote_src: yes
– name: copy files
ansible.builtin.copy:
src: /tmp/templatemo_589_lugx_gaming/
dest: /var/www/html/
remote_src: yes
owner: “{{ service_owner }}”
group: “{{ service_group }}”
mode: ‘0755’
notify: restart service apcahe
when: ansible_distribution == ‘Ubuntu’
– ansible.builtin.debug:
msg: “completed tasks”
handlers:
– name: restart service apcahe
ansible.builtin.systemd_service:
name: “{{ apache_service }}”
enabled: yes
state: restarted
“`
