Skip to main content
Ansible Configuration
CHAPTER 06

Working with Variables and Facts

Updated: May 15, 2026
25 min read

# CHAPTER 6

Working with Variables and Facts

1. Introduction

Hardcoding values into a playbook is dangerous and rigid. If you hardcode a software version (e.g., version: 1.12) into your tasks, you have to manually edit the code every time an update is released. To build scalable, dynamic automation, we must introduce Variables (data we supply to the playbook) and Facts (data the playbook automatically discovers about the remote server). In this chapter, we will learn how to abstract our data, use Jinja2 templating syntax, and utilize Ansible Facts to write intelligent playbooks that adapt to their environment.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Define variables at the Play level (vars block).
  • Access variable values using Jinja2 {{ }} syntax.
  • Understand what Ansible Facts are and how they are gathered.
  • Use conditional logic (when) based on Facts to execute OS-specific tasks.
  • Register task outputs into variables for later use.

3. Beginner-Friendly Explanation

Imagine sending a tailor to make a suit for someone.
  • Variables: The instructions you give the tailor before they leave. "Make the suit Blue, and make the lining Silk." The tailor writes this down.
  • Facts: The information the tailor gathers when they arrive. They measure the person's height, weight, and arm length. You didn't give them this information; they *gathered* it upon arrival.

The tailor then uses the Variables (Blue, Silk) and combines them with the Facts (Height: 6ft, Arm: 34in) to make the perfect, custom-fitted suit.

4. Defining and Using Variables

You can define variables directly inside a Play using the vars: block. To use the variable later in a task, wrap the variable name in double curly braces {{ }}.
yaml
123456789101112131415161718192021
---
- name: Deploy dynamic web application
  hosts: webservers
  
  vars:
    # Defining our variables
    app_version: "v2.5.1"
    install_path: "/opt/myapp"

  tasks:
    - name: Ensure installation directory exists
      file:
        # Using the variable with Jinja2 syntax!
        path: "{{ install_path }}" 
        state: directory

    - name: Download application version
      get_url:
        # We can mix strings and variables seamlessly
        url: "http://myrepo.com/app_{{ app_version }}.tar.gz"
        dest: "/tmp/app.tar.gz"

5. Ansible Facts (The Setup Module)

Whenever you run a playbook, you might notice the first thing it does is print: TASK [Gathering Facts]. Ansible logs into the remote server and runs a massive Python script (the setup module) that discovers hundreds of data points about the server: its IP address, its operating system, how much RAM it has, and its CPU architecture. These are stored in a massive JSON object called ansible_facts.

Using Facts for Intelligent Logic: You can use these facts to make decisions. Let's install a web server. If the server is Ubuntu, we need to use apt to install apache2. If the server is CentOS, we need to use yum to install httpd.

yaml
1234567891011121314
  tasks:
    - name: Install Apache on Ubuntu/Debian
      apt:
        name: apache2
        state: present
      # Only run this task IF the gathered Fact matches!
      when: ansible_facts['os_family'] == "Debian"

    - name: Install Apache on CentOS/RedHat
      yum:
        name: httpd
        state: present
      # Only run this task IF the gathered Fact matches!
      when: ansible_facts['os_family'] == "RedHat"

6. Mini Project: Build Dynamic Server Configuration

Let's use the register keyword. This allows us to run a command, capture its output, save it as a variable, and use it in the very next task.

Step-by-Step Walkthrough:

  1. 1. Create a playbook named dynamic_logic.yml.
  1. 2. Paste the following YAML:

yaml
1234567891011121314
---
- name: Register and Debug Example
  hosts: all
  
  tasks:
    - name: Check how much free disk space is available
      shell: "df -h / | tail -1 | awk '{print $4}'"
      # Save the terminal output into a variable named 'disk_space'
      register: disk_space 

    - name: Print the result to the screen
      debug:
        # We access the 'stdout' property of the registered variable
        msg: "This server currently has {{ disk_space.stdout }} of free space left!"

*Run this playbook. Ansible doesn't change anything on the server, but it dynamically retrieves the disk space and prints a custom message to your terminal for every server in the inventory.*

7. Real-World Scenarios

A company had a fleet of 500 servers. Half were running Ubuntu 18.04, and half were running Ubuntu 22.04. An old application required a specific library, but the package name changed between the two OS versions. The manual administrators were maintaining two completely separate bash scripts to configure the servers. The DevOps engineer threw away the bash scripts and wrote one Ansible Playbook utilizing ansible_facts['distribution_version']. The playbook automatically detected the OS version upon connection and conditionally installed the correct package name. The code maintenance effort was cut in half, and the deployments became bulletproof.

8. Best Practices

  • Variable Precedence: Variables can be defined in many places: the vars block, the inventory file (group_vars), or passed via the command line (-e "app_version=v3"). Ansible has strict rules on which variable "wins" if they conflict. Command-line variables (-e or --extra-vars) always override everything else. Use them for passing temporary, run-time data (like a specific build number from a CI/CD pipeline).

9. Security Recommendations

  • Turn Off Facts if Unneeded: Fact gathering takes a few seconds per server. If you have 5,000 servers, gathering facts can add 10 minutes to your playbook execution time. Furthermore, it exposes sensitive system data into the Ansible engine's memory. If your playbook does not use any when conditionals based on OS or IPs, you should disable it for security and speed by adding gather_facts: no at the top of your Play.

10. Troubleshooting Tips

  • Jinja2 Syntax Error: If your playbook crashes with a syntax error on a variable line, check your quotes. If a variable is the very first thing on a line, YAML gets confused.
  • *Wrong:* path: {{ install_path }}/config
  • *Right:* path: "{{ install_path }}/config" (Always wrap the entire string in quotes if it starts with a bracket).

11. Exercises

  1. 1. What is the operational difference between defining a variable in the vars: block versus using the register: keyword in a task?
  1. 2. Explain how the when: statement utilizes Ansible Facts to create intelligent, multi-OS playbooks.

12. FAQs

Q: Can I see exactly what Facts Ansible gathers about my server? A: Yes! You can run an ad-hoc command to dump the entire massive JSON object to your terminal: ansible webservers -m setup

13. Interview Questions

  • Q: Describe the data flow of the register keyword. How do you extract standard output (stdout) from a registered variable to use in a subsequent debug task?
  • Q: You are tasked with writing a playbook that provisions an application across a highly heterogeneous environment containing both RedHat and Debian systems. How do you structure your playbook tasks to ensure idempotency and success across both operating systems?

14. Summary

In Chapter 6, we evolved our Playbooks from rigid scripts into intelligent, adaptive automation. We introduced Variables, allowing us to parameterize our code for reusability. We discovered Ansible Facts, the powerful mechanism that allows the Control Node to interrogate remote servers and make conditional decisions based on their live configuration. Finally, we mastered the register and debug modules, giving us the ability to capture dynamic execution data and construct complex, multi-step logical workflows.

15. Next Chapter Recommendation

Our playbook is getting huge. It has 100 tasks and is 500 lines long. It's becoming impossible to read, and we want to share the "Install Apache" part with another team. How do we break it up? Proceed to Chapter 7: Ansible Roles and Project Structure.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·