Ansible Facts
Introduction
Ansible Facts are pieces of information about remote systems that Ansible automatically discovers during playbook execution. These facts include a wide range of system details such as IP addresses, operating system information, hardware specifications, network configurations, and more. Facts provide a powerful way to make your playbooks adaptable and dynamic by allowing you to write conditional logic based on the target system's characteristics.
Think of Ansible Facts as a comprehensive system inventory that's automatically generated and made available to your playbooks without requiring any additional scripts or commands.
How Ansible Facts Work
When Ansible connects to a remote host, it automatically runs a special module called setup
that collects system information. This information is stored as variables that can be used throughout your playbooks.
Viewing Ansible Facts
Before diving into using facts in playbooks, let's explore how to view the facts that Ansible collects about your systems.
Using the setup module directly
You can use the setup
module with the ansible
command to view all facts:
ansible hostname -m setup
This command returns a large JSON structure containing all facts about the specified host. Here's a shortened example of what you might see:
{
"ansible_facts": {
"ansible_architecture": "x86_64",
"ansible_distribution": "Ubuntu",
"ansible_distribution_major_version": "22",
"ansible_distribution_release": "jammy",
"ansible_distribution_version": "22.04",
"ansible_hostname": "webserver01",
"ansible_memtotal_mb": 8024,
"ansible_processor_cores": 4,
"ansible_interfaces": [
"lo",
"eth0"
],
"ansible_eth0": {
"ipv4": {
"address": "192.168.1.100",
"netmask": "255.255.255.0"
}
}
}
}
Filtering facts
You can filter facts to see only specific information using the filter
parameter:
ansible hostname -m setup -a "filter=ansible_distribution*"
This command returns only facts that start with "ansible_distribution".
Using Facts in Playbooks
Now that we understand what facts are and how to view them, let's explore how to use them in playbooks.
Basic fact usage
Here's a simple playbook that uses facts to display information about the target system:
---
- name: Display System Information
hosts: all
tasks:
- name: Print system information
debug:
msg: "The system {{ ansible_hostname }} is running {{ ansible_distribution }} {{ ansible_distribution_version }}"
When executed, this playbook might output:
TASK [Print system information] *************************************************
ok: [webserver01] => {
"msg": "The system webserver01 is running Ubuntu 22.04"
}
Conditional tasks based on facts
One of the most powerful ways to use facts is to create conditional tasks that execute only on specific systems:
---
- name: Install appropriate web server package
hosts: webservers
tasks:
- name: Install Apache on Debian/Ubuntu
apt:
name: apache2
state: present
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Install Apache on RHEL/CentOS
yum:
name: httpd
state: present
when: ansible_distribution in ['RedHat', 'CentOS', 'Fedora']
This playbook installs the appropriate web server package based on the Linux distribution of the target system.
Using facts with templates
Facts are especially useful with Jinja2 templates:
---
- name: Configure web server
hosts: webservers
tasks:
- name: Create custom index.html
template:
src: index.html.j2
dest: /var/www/html/index.html
And your template file (index.html.j2
) might look like:
<!DOCTYPE html>
<html>
<head>
<title>System Information</title>
</head>
<body>
<h1>Welcome to {{ ansible_hostname }}</h1>
<p>This server is running {{ ansible_distribution }} {{ ansible_distribution_version }}</p>
<p>It has {{ ansible_processor_cores }} CPU cores and {{ ansible_memtotal_mb }} MB of RAM</p>
</body>
</html>
Creating Custom Facts
In addition to the built-in facts, Ansible allows you to create custom facts specific to your environment.
Static custom facts
You can create static custom facts by placing files in the /etc/ansible/facts.d
directory on the target host. These files can be in INI, JSON, or executable script formats.
Here's an example of a static custom fact in INI format (/etc/ansible/facts.d/application.fact
):
[application]
name=inventory_app
version=1.2.3
env=production
Dynamic custom facts
For dynamic custom facts, you can create executable scripts that output facts in JSON format. The script must be placed in the /etc/ansible/facts.d
directory and have executable permissions.
Here's an example of a dynamic custom fact script (/etc/ansible/facts.d/app_status.fact
):
#!/bin/bash
echo '{"application_status": "running", "last_updated": "'$(date +%s)'"}'
Accessing custom facts
Custom facts are accessible under the ansible_local
variable:
---
- name: Use custom facts
hosts: application_servers
tasks:
- name: Show application information
debug:
msg: "Running {{ ansible_local.application.name }} version {{ ansible_local.application.version }} in {{ ansible_local.application.env }} environment"
Creating Facts in Playbooks
You can also set facts within a playbook using the set_fact
module:
---
- name: Set and use custom facts
hosts: all
tasks:
- name: Set a fact based on another fact
set_fact:
is_ubuntu_server: "{{ ansible_distribution == 'Ubuntu' and ansible_distribution_version is version('20.04', '>=') }}"
- name: Take action based on custom fact
debug:
msg: "This is a modern Ubuntu server supporting our application"
when: is_ubuntu_server | bool
Caching Facts
For large infrastructures, fact gathering can be time-consuming. Ansible provides fact caching to improve performance:
# In ansible.cfg
[defaults]
fact_caching = jsonfile
fact_caching_connection = /path/to/cache/directory
fact_caching_timeout = 86400 # seconds (24 hours)
With this configuration, Ansible will cache facts for 24 hours before gathering them again.
Disabling Fact Gathering
In some scenarios, you might want to disable fact gathering to improve playbook execution time:
---
- name: Playbook without facts
hosts: all
gather_facts: no
tasks:
- name: Simple task that doesn't need facts
debug:
msg: "Facts gathering disabled for speed"
Practical Examples
Example 1: System Report Generation
This playbook creates a comprehensive system report using facts:
---
- name: Generate System Report
hosts: all
tasks:
- name: Create reports directory
file:
path: /tmp/system_reports
state: directory
delegate_to: localhost
run_once: true
- name: Generate system report
template:
src: system_report.j2
dest: "/tmp/system_reports/{{ ansible_hostname }}_report.md"
delegate_to: localhost
With a template file (system_report.j2
):
# System Report for {{ ansible_hostname }}
## System Information
- Hostname: {{ ansible_hostname }}
- FQDN: {{ ansible_fqdn }}
- Operating System: {{ ansible_distribution }} {{ ansible_distribution_version }}
- Kernel: {{ ansible_kernel }}
- Architecture: {{ ansible_architecture }}
## Hardware
- Processor: {{ ansible_processor[2] }}
- Processor Cores: {{ ansible_processor_cores }}
- Memory: {{ (ansible_memtotal_mb / 1024) | round(2) }} GB
## Network
{% for interface in ansible_interfaces %}
{% if interface != 'lo' %}
### Interface: {{ interface }}
{% if ansible_facts[interface] is defined and ansible_facts[interface].ipv4 is defined %}
- IPv4 Address: {{ ansible_facts[interface].ipv4.address }}
- IPv4 Netmask: {{ ansible_facts[interface].ipv4.netmask }}
{% endif %}
{% endif %}
{% endfor %}
## Disk Usage
{% for mount in ansible_mounts %}
- Mount: {{ mount.mount }} ({{ mount.device }})
- Size: {{ (mount.size_total / 1073741824) | round(2) }} GB
- Used: {{ ((mount.size_total - mount.size_available) / 1073741824) | round(2) }} GB
- Available: {{ (mount.size_available / 1073741824) | round(2) }} GB
- Usage: {{ ((mount.size_total - mount.size_available) / mount.size_total * 100) | round(2) }}%
{% endfor %}
Example 2: Dynamic Configuration Management
This playbook uses facts to configure a service differently based on system characteristics:
---
- name: Configure application service
hosts: app_servers
vars:
base_memory_mb: 512
tasks:
- name: Calculate optimal Java heap size
set_fact:
java_heap_size: "{{ (ansible_memtotal_mb * 0.6) | int }}m"
java_heap_min: "{{ base_memory_mb }}m"
- name: Create Java service configuration
template:
src: java_service.j2
dest: /etc/sysconfig/java_app
With a template (java_service.j2
):
# Generated by Ansible on {{ ansible_date_time.iso8601 }}
# System-specific optimal configuration
JAVA_OPTS="-Xms{{ java_heap_min }} -Xmx{{ java_heap_size }}"
{% if ansible_processor_cores > 2 %}
PARALLEL_GC_THREADS={{ ansible_processor_cores }}
{% else %}
PARALLEL_GC_THREADS=2
{% endif %}
{% if ansible_distribution == 'RedHat' %}
RHEL_SPECIFIC_OPTION="enabled"
{% endif %}
Summary
Ansible Facts provide a powerful way to gather information about your infrastructure and use it to create dynamic, adaptable playbooks. By automatically discovering system details, facts allow you to write conditional logic, customize configurations, and make informed decisions about how tasks should be executed on different systems.
Key takeaways from this guide:
- Facts are automatically collected when Ansible connects to remote hosts
- Facts can be accessed as variables in playbooks and templates
- You can create conditional tasks based on system characteristics
- Custom facts can extend Ansible's built-in fact gathering
- Fact caching can improve performance in large environments
By leveraging Ansible Facts effectively, you can create more intelligent automation that adapts to the specific needs of each system in your infrastructure.
Additional Resources and Exercises
Exercises
-
Basic Fact Exploration: Write a playbook that gathers facts from your servers and outputs the operating system, memory, and IP address information.
-
Conditional Deployment: Create a playbook that installs different packages depending on the distribution and version of the target system.
-
Custom Fact Creation: Write a custom fact that detects if a specific application is installed and make a playbook that uses this fact.
-
System Capacity Planning: Create a playbook that checks if systems meet minimum requirements (CPU, RAM, disk space) for an application deployment and reports systems that need upgrades.
Further Learning
- Explore the complete list of available facts by running the
setup
module against different types of systems in your infrastructure. - Learn how to combine facts with Jinja2 filters for advanced data manipulation in templates.
- Experiment with fact caching mechanisms to improve performance in large environments.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)