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.
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] }}"
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 }}"
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
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 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
Conclusion
This brings us to the end of this tutorial on working with Ansible variables and facts.
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:
Hello Jeff,
I tried a super simple playbook to read a dict variable:
Output:
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!
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”}
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)]
Hey Jeff, let me have a look at the playbook you are running to verify if the syntax is correct.