Ansible is one of the most widely-used automation tools in the IT industry, helping DevOps professionals manage systems and automate tasks with ease. One of the fundamental concepts that you must grasp when using Ansible is the use of variables and facts. Both of these components play a crucial role in making your playbooks dynamic, flexible, and reusable. In this article, we will dive deep into what variables and facts are, how they are used in Ansible, and best practices for working with them.
What Are Variables in Ansible?
Variables in Ansible are used to store values that can be used throughout playbooks and roles. These values can be anything from strings, numbers, lists, or dictionaries. Variables allow you to make your playbooks reusable and adaptable to different environments by abstracting values that might change between executions, like hostnames, IP addresses, or package versions.
Types of Variables in Ansible
Ansible supports several types of variables, which can be sourced from different places:
Playbook Variables: Variables defined directly within the playbook, often under a
vars
section.
- hosts: webservers
vars:
apache_port: 80
tasks:
- name: Install Apache
yum:
name: httpd
state: present
- Inventory Variables: Variables that are defined in the Ansible inventory file for a particular host or group of hosts.
[webservers]
web1 ansible_host=192.168.1.10 apache_port=8080
web2 ansible_host=192.168.1.11 apache_port=9090
- Command-line Variables: Variables can also be defined on the command line using the
-e
or--extra-vars
option when running Ansible commands.
ansible-playbook site.yml -e "apache_port=8080"
Role Variables: Variables that are defined in the
defaults/main.yml
orvars/main.yml
files within Ansible roles.Facts: System properties collected by Ansible that can be used as variables.
External Variables: Variables defined outside of the playbook in external files, such as JSON or YAML files.
Using Variables in Ansible Playbooks
Here’s an example that shows how to use variables in a playbook to make it more flexible:
- name: Install and configure Apache
hosts: all
vars:
apache_package: httpd
apache_port: 80
tasks:
- name: Install Apache
yum:
name: "{{ apache_package }}"
state: present
- name: Open Apache port in firewall
firewalld:
service: http
permanent: yes
state: enabled
immediate: yes
port: "{{ apache_port }}/tcp"
In this example:
- The
apache_package
andapache_port
variables make it easy to customize the playbook’s behavior across different environments or servers. - The variable values are referenced within the tasks using the
{{ variable_name }}
syntax.
What Are Facts in Ansible?
In Ansible, facts are system properties automatically gathered by Ansible when it connects to a target host. These facts provide information about the target system such as its operating system, network interfaces, IP addresses, and hardware specifications. Facts are stored in a dictionary-like structure and can be accessed as variables in playbooks.
How Ansible Gathers Facts
By default, Ansible collects a wide variety of facts about the host system using the setup
module. This module gathers facts about the system’s environment, including:
- System architecture (e.g., x86_64)
- OS type and version (e.g., Ubuntu 20.04)
- Memory and CPU information
- IP addresses and network interfaces
- File system details
- Mounted devices
- Environment variables
For example, you can access the IP address of a target host using the fact ansible_default_ipv4.address
:
- name: Get default IP address
hosts: all
tasks:
- name: Print the default IP address
debug:
msg: "The default IP is {{ ansible_default_ipv4.address }}"
How to Disable Fact Gathering
If you don’t need all the facts or want to speed up your playbook execution, you can disable fact gathering by using the gather_facts
directive:
- hosts: all
gather_facts: no
tasks:
- name: Custom task without fact gathering
debug:
msg: "Facts gathering is disabled for this playbook"
Using Facts in Ansible Playbooks
Facts are extremely useful for making playbooks dynamic. They allow you to target specific system attributes, like OS type or architecture, without hardcoding values into your playbook.
For example, the following playbook installs different packages based on the operating system:
- name: Install packages based on OS
hosts: all
tasks:
- name: Install Apache on Debian-based systems
apt:
name: apache2
state: present
when: ansible_facts['os_family'] == "Debian"
- name: Install Apache on RedHat-based systems
yum:
name: httpd
state: present
when: ansible_facts['os_family'] == "RedHat"
Listing All Available Facts
You can use the setup
module to explicitly gather and list all available facts:
- name: Gather facts and display them
hosts: all
tasks:
- name: Display all facts
setup:
- name: Show gathered facts
debug:
var: ansible_facts
This will output all the collected facts about the system, which can be extremely useful for debugging or understanding the available data.
Best Practices for Using Variables and Facts in Ansible
1. Use Variables for Dynamic Playbooks
Instead of hardcoding values in your playbooks, use variables to make them more flexible and reusable. This ensures that your playbooks can be used across different environments with minimal changes.
2. Leverage Facts to Make Decisions
Facts provide valuable information about the target system and should be used to make decisions within your playbooks. Use facts to determine the target operating system, architecture, or IP addresses, and adjust tasks accordingly.
3. Minimize Hardcoding
Avoid hardcoding values like paths, package names, or configurations directly in your playbooks. Instead, define variables or use facts wherever possible to enhance the playbook’s reusability and flexibility.
4. Structure Your Variables Effectively
For large projects, consider organizing your variables into separate files or directories. Ansible allows you to include external variable files and use role-based variable structures. This helps keep your playbooks organized and reduces clutter.
5. Use defaults
and vars
Directories for Roles
If you’re using Ansible roles, it’s a good practice to define default values for variables in the defaults/main.yml
file and environment-specific or overriding variables in the vars/main.yml
file.
6. Be Mindful of Variable Precedence
Ansible has a specific variable precedence order. When a variable is defined in multiple places, Ansible will use the one with the highest precedence. Understanding this order can help avoid conflicts.