Your playbook fails because ansible_facts are empty or missing critical information. Facts are essential for conditional logic, templates, and inventory decisions. When they don't gather correctly, everything breaks.
Understanding the Problem
Facts gathering failures show up in different ways:
TASK [Gather Facts] ***********************************************************
fatal: [webserver01]: FAILED! => {"msg": "Failed to gather facts"}Or subtler - facts exist but are incomplete:
- debug:
var: ansible_default_ipv4
# Result: undefined or missingOr timeout errors during fact gathering:
fatal: [webserver01]: FAILED! => {"msg": "Timeout (30s) waiting for fact gathering"}Step 1: Test Facts Gathering Directly
First, verify the setup module works:
ansible webserver01 -i inventory -m setupThis should output a large JSON structure. If it fails, the error will tell you why.
For specific facts:
```bash # Get only network facts ansible webserver01 -i inventory -m setup -a "filter=ansible_*ipv4*"
# Get only hardware facts ansible webserver01 -i inventory -m setup -a "gather_subset=hardware" ```
Step 2: Check Python Requirements
The setup module requires Python and certain libraries:
```bash # Check Python is available ansible webserver01 -i inventory -m raw -a "python3 --version"
# Or Python 2 on older systems ansible webserver01 -i inventory -m raw -a "python --version" ```
If Python is missing, install it:
```bash # On Debian/Ubuntu ansible webserver01 -i inventory -b -m raw -a "apt-get update && apt-get install -y python3"
# On RHEL/CentOS ansible webserver01 -i inventory -b -m raw -a "yum install -y python3" ```
Step 3: Verify Python Interpreter
Ansible might be using the wrong Python interpreter:
# Check what Ansible detects
ansible webserver01 -i inventory -m debug -a "var=ansible_python_interpreter"Set it explicitly if needed:
# In inventory
[all:vars]
ansible_python_interpreter=/usr/bin/python3Or in ansible.cfg:
[defaults]
interpreter_python = auto_silentStep 4: Handle Fact Gathering Timeout
Slow fact gathering causes timeouts. Increase the timeout:
# ansible.cfg
[defaults]
gather_timeout = 60Or disable subsets that are slow:
- hosts: all
gather_subset:
- min # Only minimum facts
- network # Add network facts
- "!hardware" # Exclude hardware factsDisable fact gathering entirely when not needed:
- hosts: all
gather_facts: no
tasks:
- name: Quick task
command: echo "done"Then gather facts explicitly later:
```yaml - hosts: all gather_facts: no tasks: - name: Quick task first command: echo "done"
- name: Now gather facts
- setup:
- when: ansible_os_family is not defined
`
Step 5: Fix Network-Related Fact Issues
Network facts can hang on systems with problematic network configurations:
# Check network facts specifically
ansible webserver01 -i inventory -m setup -a "filter=ansible_*network*"If this hangs, exclude network facts:
- hosts: all
gather_subset: "!network"Or skip specific interfaces:
- hosts: all
gather_subset: min
tasks:
- setup:
filter: "ansible_*"
gather_subset: "all"
ignore_errors: yesStep 6: Configure Fact Caching
Fact caching prevents re-gathering facts on each run:
# ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400 # 24 hoursFor Redis caching:
[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 86400
fact_caching_connection = localhost:6379:0Install required Python package:
pip install redisVerify cache:
ansible webserver01 -i inventory -m setup -a "cache=enabled"Step 7: Debug Empty Facts
If facts return but are empty:
- name: Debug all facts
debug:
var: ansible_factsCheck if facts are being set at all:
ansible webserver01 -i inventory -m setup | python3 -m json.tool | head -50If ansible_facts is empty, the setup module might be failing silently. Run with verbosity:
ansible webserver01 -i inventory -m setup -vvvLook for errors in the output.
Step 8: Handle Permission Issues
Facts require certain permissions to gather:
# Test with become
ansible webserver01 -i inventory -b -m setupIf this works but non-become doesn't, certain facts require root:
- hosts: all
become: yes
tasks:
- setup:Or gather without root and accept limited facts:
- hosts: all
gather_subset: min # Works without rootStep 9: Fix Custom Facts
Custom facts in /etc/ansible/facts.d/ might have errors:
```bash # Check custom facts directory ansible webserver01 -i inventory -m shell -a "ls -la /etc/ansible/facts.d/"
# Test custom fact script ansible webserver01 -i inventory -m shell -a "/etc/ansible/facts.d/custom.fact" ```
Custom facts must return valid JSON:
# Should output JSON
ansible webserver01 -i inventory -m shell -a "cat /etc/ansible/facts.d/custom.fact"Example valid custom fact:
#!/bin/bash
echo '{"version": "1.0", "status": "active"}'Make it executable:
ansible webserver01 -i inventory -m file -a "path=/etc/ansible/facts.d/custom.fact mode=0755"Step 10: Handle Virtualization and Container Issues
Containers often have limited facts:
- hosts: all
tasks:
- name: Check if container
set_fact:
is_container: "{{ ansible_virtualization_type == 'docker' }}"If facts are missing in containers, adjust expectations:
- hosts: all
gather_subset: min
tasks:
- name: Use fallback for missing facts
set_fact:
ansible_processor_cores: "{{ ansible_processor_cores | default(1) }}"
ansible_memtotal_mb: "{{ ansible_memtotal_mb | default(512) }}"Step 11: Troubleshoot Specific Fact Issues
**Missing ansible_default_ipv4:**
```bash # Check interfaces ansible webserver01 -i inventory -m setup -a "filter=ansible_interfaces"
# May need to specify interface - set_fact: primary_ip: "{{ ansible_eth0.ipv4.address | default(ansible_enp0s3.ipv4.address) }}" ```
**Missing ansible_distribution:**
- hosts: all
gather_subset: min # May not include distribution
tasks:
- setup:
gather_subset: "!min" # Get all facts
- debug:
var: ansible_distributionMissing mount facts:
- hosts: all
gather_subset: hardware # Include hardware for mount factsQuick Verification
Full facts test:
ansible all -i inventory -m setup --tree /tmp/factsCheck specific facts:
```bash # IP address ansible all -i inventory -m setup -a "filter=ansible_default_ipv4"
# OS info ansible all -i inventory -m setup -a "filter=ansible_distribution*"
# Memory ansible all -i inventory -m setup -a "filter=ansible_mem*" ```
Verify facts in playbook:
- hosts: all
tasks:
- name: Verify facts are gathered
assert:
that:
- ansible_facts is defined
- ansible_os_family is defined
- ansible_default_ipv4 is defined
fail_msg: "Facts gathering failed"
success_msg: "All facts present"Prevention Best Practices
- 1.Use fact caching for performance:
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ~/.ansible/facts
fact_caching_timeout = 7200- 1.Gather only needed facts:
- hosts: all
gather_subset:
- min
- network- 1.Handle missing facts gracefully:
- set_fact:
# Use default if fact missing
primary_ip: "{{ ansible_default_ipv4.address | default('127.0.0.1') }}"- 1.Pre-install Python on targets:
- hosts: all
gather_facts: no
tasks:
- name: Bootstrap Python
raw: |
if command -v python3 >/dev/null 2>&1; then
echo "Python3 installed"
else
apt-get update && apt-get install -y python3 || yum install -y python3
fi
changed_when: falseFact gathering issues usually stem from Python availability, permissions, timeouts, or network problems. Test with the setup module directly, adjust timeouts, and use fact caching to improve reliability.