How to Work with Ansible Variables and Facts – Part 8

We’ve mentioned variables in this Ansible series and just to jog your mind a little. A variable, just like in many programming languages, is essentially a key that represents a value.

What Constitutes a Valid Variable Name?

A variable name includes letters, numbers, underscores or a mix of either 2 or all of them. However, bear in mind that a variable name must always begin with a letter and should not contain spaces.

Let’s take a look a few examples of valid and unacceptable variable names:

Valid Variable Name Examples:

football 
foot_ball
football20 
foot_ball20

Non-valid Variable Name Examples:

foot ball
20 
foot-ball

Let’s discuss the variable types:

1. Playbook Variables

Playbook variables are quite easy and straightforward. To define a variable in a playbook, simply use the keyword vars before writing your variables with indentation.

To access the value of the variable, place it between the double curly braces enclosed with quotation marks.

Here’s a simple playbook example:

- hosts: all
  vars:
    greeting: Hello world! 

  tasks:
  - name: Ansible Basic Variable Example
    debug:
      msg: "{{ greeting }}"

In the above playbook, the greeting variable is substituted by the value Hello world! when the playbook is run. The playbook simply prints the message Hello world! when executed.

Playbook Variables in Ansible
Playbook Variables in Ansible

Additionally, you can have a list or an array of variables as shown:

The playbook below shows a variable called continents. The variable holds 5 different values – continent names. Each of these values can easily be accessed using index 0 as the first variable.

The example of the playbook below retrieves and displays Asia (Index 1).

- hosts: all
  vars:
    continents:
      - Africa
      - Asia
      - South America
      - North America
      - Europe
      
  tasks:
  - name: Ansible List variable Example
    debug:
      msg: "{{ continents [1] }}"
Array of Variables in Ansible
Array of Variables in Ansible

The variable list can similarly be structured as shown:

vars:
    Continents: [Africa, Asia, South America, North America, Europe]

To list all the items on the list, use the with_items module. This will loop through all the values in the array.

- hosts: all
  vars:
    continents: [Africa, Asia, South America, North America, Europe]

  tasks:
  - name: Ansible array variables example
    debug: 
      msg: "{{ item }}"
    with_items:
      - "{{ continents }}"
List Ansible Array Variables
List Ansible Array Variables

Another type of Ansible variable is the dictionary variable.

Dictionary variables are additionally supported in the playbook. To define the dictionary variable, simply ident the key-value pair just below the dictionary variable name.

hosts: switch_f01

vars:
   http_port: 8080
   default_gateway: 10.200.50.1
   vlans:
       id: 10
       port: 2

In the example above, vlans is the dictionary variable while id and port are the key-value pairs.

hosts: switch_f01

vars:
   http_port: 8080
   default_gateway: 
   vlans:
      id: 10
      port: 20

 tasks:
   name: Configure default gateway
   system_configs:
   default_gateway_ip: “{{ default_gateway  }}“


   name: Label port on vlan 10
   vlan_config:
	vlan_id: “{{ vlans[‘id’]  }}“
     port_id: 1/1/ {{ vlans[‘port’]  }}

For port_id, since we are starting the value with text and not the variable, quotation marks are not necessary to surround the curly braces.

2. Special Variables

Ansible provides a list of predefined variables that can be referenced in Jinja2 templates and playbooks but cannot be altered or defined by the user.

Collectively, the list of Ansible predefined variables is referred to as Ansible facts and these are gathered when a playbook is executed.

To get a list of all the Ansible variables, use the setup module in the Ansible ad-hoc command as shown below:

# ansible -m setup hostname

This displays the output in JSON format as shown:

# ansible -m setup localhost
List Ansible Variables
List Ansible Variables

From the output, we can see that some of the examples of Ansible special variables include:

ansible_architecture
ansible_bios_date
ansible_bios_version
ansible_date_time
ansible_machine
ansible_memefree_mb
ansible_os_family
ansible_selinux

There are many other Ansible special variables these are just a few examples.

These variables can be used in a Jinja2 template as shown:

<html>
<center>
   <h1> The hostname of this webserver is {{ ansible_hostname }}</h1>
   <h3> It is running on {{ ansible_os_family}}system </h3>
</center>
</html>

3. Inventory Variables

Lastly, on the list, we have Ansible inventory variables. An inventory is a file in INI format that contains all the hosts to be managed by Ansible.

In inventories, you can assign a variable to a host system and later use it in a playbook.

[web_servers]

web_server_1 ansible_user=centos http_port=80
web_server_2 ansible_user=ubuntu http_port=8080

The above can be represented in a playbook YAML file as shown:

---
   web_servers:
     web_server_1:
        ansible_user=centos
	   http_port=80

web_server_2:
        ansible_user=ubuntu
	   http_port=8080

If the host systems share the same variables, you can define another group in the inventory file to make it less cumbersome and avoid unnecessary repetition.

For example:

[web_servers]

web_server_1 ansible_user=centos http_port=80
web_server_2 ansible_user=centos http_port=80

The above can be structured as:

[web_servers]
web_server_1
web_server_2


[web_servers:vars]
ansible_user=centos
http_port=80

And in the playbook YAML file, this will be defined as shown:

---
   web_servers:
    
     hosts: 
       web_server_1:
	  web_server_2:

     vars: 
        ansible_user=centos
   http_port=80

Ansible Facts

When running playbooks, the first task that Ansible does is the execution of setup task. I’m pretty sure that you must have come across the output:

TASK:  [Gathering facts] *********

Ansible facts are nothing but system properties or pieces of information about remote nodes that you have connected to. This information includes the System architecture, the OS version, BIOS information, system time and date, system uptime, IP address, and hardware information to mention just a few.

To get the facts about any system simply use the setup module as shown in the command below:

# ansible -m setup hostname

For example:

# ansible -m setup database_server

This prints out a large set of data in JSON format as shown:

Ansible Get System Facts
Ansible Get System Facts

Ansible facts are handy in helping the system administrators which operations to carry out, for instance, depending on the operating system, they are able to know which software packages need to be installed, and how they are to be configured, etc.

Custom Facts

Did you also know that you can create your own custom facts that can be gathered by Ansible? Yes, you can. So how do you go about it? Let’s shift gears and see how.

The first step is to create a /etc/ansible/facts.d directory on the managed or remote node.

Inside this directory, create a file(s) with a .fact extension. This file(s) will return JSON data when the playbook is run on the Ansible control node, which is inclusive of the other facts that Ansible retrieves after a playbook run.

Here’s an example of a custom fact file called date_time.fact that retrieves date and time.

# mkdir -p /etc/ansible/facts.d
# vim /etc/ansible/facts.d/date_time.fact

Add the following lines in it.

#!/bin/bash
DATE=`date`
echo "{\"date\" : \"${DATE}\"}"

Save and exit the file.

Now assign the execute permissions:

# chmod +x /etc/ansible/facts.d/date_time.fact

Now, I created a playbook on Ansible control node called check_date.yml.

---

- hosts: webservers

  tasks:
   - name: Get custom facts
     debug:
      msg: The custom fact is {{ansible_local.date_time}}

Append the fact file to the ansible_local variable. The ansible_local stores all the custom facts.

Now run the playbook and observe Ansible retrieving information saved on the fact file:

# ansible_playbook check_date.yml
Create Ansible Custom Facts
Create Ansible Custom Facts
Conclusion

This brings us to the end of this tutorial on working with Ansible variables and facts.

Hey TecMint readers,

Exciting news! Every month, our top blog commenters will have the chance to win fantastic rewards, like free Linux eBooks such as RHCE, RHCSA, LFCS, Learn Linux, and Awk, each worth $20!

Learn more about the contest and stand a chance to win by sharing your thoughts below!

James Kiarie
This is James, a certified Linux administrator and a tech enthusiast who loves keeping in touch with emerging trends in the tech world. When I'm not running commands on the terminal, I'm taking listening to some cool music. taking a casual stroll or watching a nice movie.

Each tutorial at TecMint is created by a team of experienced Linux system administrators so that it meets our high-quality standards.

Join the TecMint Weekly Newsletter (More Than 156,129 Linux Enthusiasts Have Subscribed)
Was this article helpful? Please add a comment or buy me a coffee to show your appreciation.

5 Comments

Leave a Reply
  1. Is the YAML file formatting correct throughout the example of the ‘Inventory Variables‘ section?

    The difference in the relative indentation of webserver1 vs webserver2 doesn’t seem too consistent with the initial presentation:

    [web_servers]
    
    web_server_1 ansible_user=centos http_port=80
    web_server_2 ansible_user=ubuntu http_port=8080
    
    Reply
  2. Hello Jeff,

    I tried a super simple playbook to read a dict variable:

    ---    
    hosts: instance-01
    vars:  
      vlans:
        id: 10
            
    tasks: 
      - name: Read dict variable
        debug:
          msg: “{{ vlans[‘id’]  }}“   
    

    Output:

    ansible-playbook test_dict.yml 
    [WARNING]: Skipping unexpected key (servers) in group (local), only "vars", "children" and "hosts" are valid
    ERROR! A playbook must be a list of plays, got a  instead
    

    The error appears to be in ‘/home/student/Ansible/klooien/test_dict.yml’: line 2, column 1, but may
    be elsewhere in the file depending on the exact syntax problem.

    The offending line appears to be:


    hosts: instance-01
    ^ here

    Help!

    Reply
  3. TASK [Install the new facts] *********************************************************************************************************************
    An exception occurred during task execution. To see the full traceback, use -vvv. The error was: If you are using a module and expect the file to exist on the remote, see the remote_src option
    fatal: [host2]: FAILED! => {“changed”: false, “msg”: “Could not find or access ‘custom.facts’\nSearched in:\n\t/rh294/files/custom.facts\n\t/rh294/custom.facts\n\t/rh294/files/custom.facts\n\t/rh294/custom.facts on the Ansible Controller.\nIf you are using a module and expect the file to exist on the remote, see the remote_src option”}

    Reply
  4. TASK [Get custom facts] **************************************************************************************************************************
    fatal: [host2]: FAILED! => {“msg”: “The task includes an option with an undefined variable. The error was: ‘dict object’ has no attribute ‘date_time’\n\nThe error appears to be in ‘/rh294/chap4_setup_facts.yaml’: line 11, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n – name: Get custom facts\n ^ here\n”}

    I am getting this error when i run the above custom fact. I am using ansible;

    [root@control-node rh294]# ansible –version
    ansible 2.9.6
    config file = /rh294/ansible.cfg
    configured module search path = [u’/root/.ansible/plugins/modules’, u’/usr/share/ansible/plugins/modules’]
    ansible python module location = /usr/lib/python2.7/site-packages/ansible
    executable location = /usr/bin/ansible
    python version = 2.7.5 (default, Aug 4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]

    Reply

Got Something to Say? Join the Discussion...

Thank you for taking the time to share your thoughts with us. We appreciate your decision to leave a comment and value your contribution to the discussion. It's important to note that we moderate all comments in accordance with our comment policy to ensure a respectful and constructive conversation.

Rest assured that your email address will remain private and will not be published or shared with anyone. We prioritize the privacy and security of our users.