In our last article, we discussed what: Ansible playbooks are and how to create and run tasks through playbooks. In this article, we’ll take it one step further and learn how to use variables in Ansible playbooks.
What are Ansible variables?
Variables are basic concepts in all programming languages that are used to store data and used later in the code. Similarly, ansible has variables that store some values, and these are used later in the playbook for various processing.
Ansible supports declaring variables in multiple places such as playbooks, host_vars & group_vars files and command line arguments.
In the next few paragraphs, we’ll discuss how to define variables in different places and learn about the priority of variables.
Variable – key, value assignment
Variables can be defined at the play and task level using the “vars
keyword. After the keyword vars, the variable with key and value is given. These variables are accessible to all tasks in the game.
I’ve created a task that uses the debug module to print a message using variable values. The variables must be enclosed in double braces {{ variable }}I
Using Debug Module in Ansible Playbooks
- name: Print vars hosts: m1 gather_facts: false vars: os_name: "PoP!_OS Desktop" version: "21.10" tasks: - name: Task1 - Substitute variables debug: msg: "My current desktop OS is {{ os_name }} - {{ version }}" Key-value variable Mapping
Now run the following command in the terminal to run the playbook.
$ ansible-playbook playbook_name.yml
In the image below you can see that both variables are replaced by their values when the message is printed to stdout.
Variable – list of elements
You can create a list of elements in a variable. Looking at the definition below, I created a list variable in two ways.
- Standard YAML way to define a list of items/elements. Here I have given the variable names as “top_linux_desktops”I
- Python syntax for creating a list of items/elements. Here I have given the variable names as “top_desktops”I
vars: top_linux_desktops: - MXlinux - pop-os - Linux Mint - Manjaro - Fedora top_desktops: [MXLinux, pop-os, Linux Mint, Manjaro, Fedora]
You can print all elements from the list using the variable name. I have created two tasks. The first job will print elements from the top_linux_desktops variable and the second task will print elements from the top_desktops variable.
tasks: - name: Task1 - List of elements debug: msg: "My fav linux desktops are {{ top_linux_desktops }}" - name: Task2 - List of elements using Python syntax debug: msg: "My fav linux desktops are {{ top_desktops }}"

In list, you can also access the individual element using the Index position, similar to how you access elements from a python list. There are two ways to do it.
- Using dot notation (variable.index)
- Python notation (variable[index]I
I have created two tasks. The first job uses dot notation and the second job uses python notation to print the element from the first and second position.
tasks: - name: Task3 - Accessing List element using dot notation debug: msg: "My fav linux desktops are {{ top_linux_desktops.1 }} and {{ top_desktops[2] }}" - name: Task4 - Accessing List element using python notation(list[i]) debug: msg: "My fav linux desktops are {{ top_linux_desktops[1] }} and {{ top_desktops[2] }}"

Variable – Dictionary
You can create a dictionary object and assign it to the variable. This is similar to python dictionaries. Dictionaries can be created in two ways.
- standard YAML syntax to define dictionary
- Python dictionary notation
If you can see the playbook snippet below, I created two dictionary variables. The first variable “release_info” follows the YAML syntax approach and the second variable “new_release_info” follows the Python dictionary syntax.
vars: release_info: name: PoP!_OS Desktop version: 22.04 release_month: April, 2022 new_release_info: { name: PoP!_OS Desktop, version: 22.04, release_month: "April, 2022"}
You can extract all values from the variable or a particular dictionary element using its key. As with the list, the dictionary also follows dot and python notation to print the value of a key.
I created two tasks, the first task uses the dot notation to grab the element from the dictionary and the second task will use python notation grab the element.
tasks: - name: Task1 - Accessing dictionary values using its key with dot notation(dict.key) debug: msg: "{{ release_info.name }} version {{ release_info.version }} is released on {{ release_info.release_month }}" - name: Task2 - Accessing dictionary values using its key with python notation(dict['key']) debug: msg: "{{ new_release_info['name'] }} version {{ new_release_info['version'] }} is released on {{ new_release_info['release_month'] }}"

Play Priority vs Task Level
Variables can be defined at the task level as well as at the game level, but variables defined at the task level have a higher priority over the variables defined at the game level.
In the example below, I have created vars at both play level and task level with the same variable names. Now when I use the playbook, task variable names are used.
vars: os_name: "PoP!_OS Desktop" version: "21.10" tasks: - name: Task1 - Substitute variables vars: os_name: "Linux Mint" version: "20.03" debug: msg: "My current desktop OS is {{ os_name }} - {{ version }}"

Playbook vs Command Line Argument Precedence
You can override the variable passed in the playbook with -e
flag. Higher priority is given to the passed variables -e
flag.
I run the same playbook from the previous section again and overwrite the variables with the -e
flag.
$ ansible-playbook 4_var_precedence.yml -e "os_name=fedora" -e "version=35"

You can pass variables to -e
flag in Json
I YAML
or ini
format.
# INI FORMAT $ ansible-playbook 4_var_precedence.yml -e "os_name=fedora" -e "version=35" # JSON FORMAT $ ansible-playbook 4_var_precedence.yml -e '{"os_name": "fedora"}' -e '{"version": 35}' # YAML FORMAT $ ansible-playbook 4_var_precedence.yml -e "{os_name: fedora}" -e "{version: 35}"
You can also create a separate file for variable and pass it in -e
flag. The syntax will be as follows. Here I have created a file called vars.yml
and grouped all my variables together. Now when the file is passed to the -e
mark all variables are imported into the playbook.
$ ansible-playbook 4_var_precedence.yml -e @vars.yml
Variable File
Instead of defining the variable in the playbook, you can create a file and declare all the variables in the file. I created a file called vars.yml
and grouped together all the variables we discussed in previous sections in this file.

Instead of using the keyword varsyou should use… vars_files In the playbook and pass the file name. Now when you run the playbook, ansible chooses the variables from the file. The file can be in any path.


Host and group variables
You can define host and group level variables in the inventory file. You can refer to the following article where we briefly discussed how to create host and group level variables.
Ansible inventory and configuration files
As a best practice, you should not define the variables in the inventory file, but you can create folders for: host_vars
and group_vars
and ansible automatically chooses the files in the folder. Create a folder named host_vars
I
$ mkdir host_vars
Inside host_vars
directory, you can define host-level variables, ie you can create an INI, YAML or JSON file and store variables for a particular host. If you look at my inventory file below, I have two hosts called “ubuntu” and “rocky” and created the variable file for each host.
NB: The files must have the same name as the host/alias name in your inventory file.
# Inventory file [m1] ubuntu ansible_host=ubuntu.anslab.com [m2] rocky ansible_host=rocky.anslab.com
$ mkdir host_vars/ubuntu.yml $ echo "message: This variable is read from host_vars/ubuntu.yml file" > host_vars/ubuntu.yml" $ mkdir host_vars/rocky.yml $ echo "message: This variable is read from host_vars/rocky.yml file" > host_vars/rocky.yml
I added a variable called Imessage
I in both variable files. Now when I use my playbook, variables are retrieved from these two files.

Similarly, you can also create variable files for groups. Create a folder Igroup_vars
I and create a file with the group name according to the inventory file.
[m1] ubuntu ansible_host=ubuntu.anslab.com [m2] rocky ansible_host=rocky.anslab.com [servers:children] m1 m2
I created a child group called “server”so i make the filename as servers.yml
I
$ mkdir group_vars $ mkdir group_vars/servers.yml $ echo "message: This variable is read from group_vars/servers.yml file" > group_vars/servers.yml
Now when I use the playbook it reads the servers.yml
by means of group_varsI

NB: If you have both host_vars
and group_vars
will ansible first search for host_vars
definitions, and if it’s not found, it goes to group_vars
I
Magic Variables
Ansible provides some internal variables where the state of the variable is defined when you run the playbook. We can use these variables through the playbook. To get the list of available special variables, you can refer to the following link.
Ansible special variables
For example, there is a variable called inventory_dir
which stores the absolute path for the inventory file used by the playbook.
- name: Magic Variables - Get inventory directory path debug: msg: "{{ inventory_dir }}"

An important magic variable is hostvars
† This variable is going to print a collection of some magic variables in json format.
- name: Magic Variables - hostvars debug: msg: "{{ hostvars }}"

The output contains information in str, list and dictionary format. Let’s say I want to check which group my ubuntu host is in, then I can get it in the following way. I’m using Python’s syntax notation here.
- name: Magic Variables debug: msg: "{{ hostvars['ubuntu']['group_names'] }}"

Not all magic variables can be used in day-to-day business operations. Check out all the magic variables and see which one best suits your use.
Facts as variable
When you run the playbook, ansible uses the installer module and collects facts from the target hosts and stores the output in memory so that it can be used in the playbook. Facts are also known as playbook variables.
First get acquainted with the output of facts so that you can grab certain attributes. Run the following command which collects the facts output and saves it to a file.
$ ansible all -m setup --tree /tmp/facts_result
A separate output file is created for each host. If you look at the output, it’s nothing but JSON format output.

The attributes in the fact output are in List, Dictionary, and AnsibleUnsafeText format. Below are some examples of different types.
- name: Facts output - AnsibleUnsafeText debug: msg: "{{ discovered_interpreter_python }}" - name: Facts output - List debug: msg: "{{ ansible_all_ipv4_addresses }}" - name: Facts output - Dictionary debug: msg: "{{ ansible_python }}"

There is masses of information gathered by facts, so take the time to go through the output and see what fits your need.
Conclusion
In this article, we discussed what an ansible variable is and how to declare variables in different places. The priority of variables is very important when declaring variables and that is covered in this article. We also discussed magic variables and their usage scenarios. Finally, we discussed what facts are and how the output of facts can be used as variables.