Hello and welcome back to the Ansible Starter Guide. In this fifth part of the series, I want to show you how variables work and how we can use them to deal with differences between different systems.
The simplest use of variables is to store multiple values (such as file paths) in one variable.
Let's start with an example. Let's say we want to serve an index.html and a style.css to our web server. For the sake of clarity, I'm going to put the whole thing in its own playbook. You can also just extend the webservers.yml from the last part.
The further we build our playbook, the more we will need to use the "/var/www/html" path. It would be great if we could just put this in a variable. So let's do that:
This is the simplest definition of a variable. We have simply defined the variable "my_docroot" in the playbook and can now access it throughout the whole "Playing with Variables" piece. To use variables we use the following format:
"{{ variable_name }}"
It is important to note that we must enclose the entire string in quotes ("). Variables can also be defined at task level. This would look like this:
- name: Play mit Variablen auf Taskebene
hosts: webservers
tasks:
- name: copy index.html
ansible.builtin.copy:
src: files/index.html
dest: "{{ my_docroot }}/index.html"
become: true
vars:
my_docroot: /var/www/html
- name: copy style.css
ansible.builtin.copy:
src: files/style.css
dest: "{{ my_docroot }}/style.css"
become: true
vars:
my_docroot: /var/www/html
However, this only makes sense in very special cases, when only one specific task actually requires that variable.
In the examples above, we use a variable of type "string", which is a simple string of characters. However, there are other types of variables that we will discuss in more detail later in this guide.
Variable types
# Strings
my_string: Hello World!
# Numbers
number_of_files: 8
# Float - we always use this when floating point numbers are involved
my_float: 2.5
# Lists - Lists containing one or more entries
files_to_copy:
- /path/to/file1.txt
- /path/to/file2.txt
# Dictionary - List with structured data
files_to_copy:
- name: first file
path: /path/to/file1.txt
copy_to: /new/file/path1.txt
- name: second file
path: /path/to/file2.txt
copy_to: /new/file/path2.txt
# Boolean - True ore False
enable_service: true
overwrite_config: false
Host and Group Variables
The variable defined in the first example now applies to all hosts with the same value. But what do we do if we want to define different values for each host? Let's return to our example scenario. Let's assume that the content of index.html of our two web servers should be different for each host, so that the first one outputs "I am webserver1" and the second one outputs "I am webserver2".
To do this, we use host variables (host_vars). To use them, we create a new directory in our example setup:
cd ~/ansible-guide
mkdir -p host_vars/ansible-guide-1
mkdir -p host_vars/ansible-guide-2
In each of the created directories we add a file "main.yml".
When we start our playbook, Ansible will automatically read all the files under "host_vars" and assign the variable values to the appropriate hosts. It is important that the names of the directories match the names of the hosts in our inventory.
To test this, we need to make a small adjustment to our web server playbook. Using the "copy" module, we can define the desired contents of the target file directly, instead of using a source file. It will then look like this:
Notice the change in the "copy index.html" task. Instead of defining a source file for the copy module, we enter the desired content of the target file directly, using our variable "my_welcome_text".
Then let's run the playbook:
cd ~/ansible-guide
ansible-playbook -i inventory.txt webservers.yml
PLAY [webserver setup] ****************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [ansible-guide-1]
ok: [ansible-guide-2]
TASK [Apache2 Setup] ****************************************************************************
ok: [ansible-guide-1]
ok: [ansible-guide-2]
TASK [start and enable apache2] ****************************************************************************
ok: [ansible-guide-1]
ok: [ansible-guide-2]
TASK [copy index.html] ****************************************************************************
changed: [ansible-guide-1]
changed: [ansible-guide-2]
PLAY RECAP ****************************************************************************
ansible-guide-1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-guide-2 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The "copy index.html" task now has the status "modified" on both hosts, and has thus adopted the new content for index.html. We can now check again with curl or the browser to see if we have achieved the desired effect:
curl http://192.168.0.11
<h1>I am webserver1!</h1>
curl http://192.168.0.12
<h1>I am webserver2!</h1>
BAM! Looks good! So we successfully used host-specific variables.
The whole thing works with groups as well. For this we use group_vars. We also create a new playbook for this and use the useful debug module to test the variables.
In part 4 of this guide, we created an inventory with two different groups, "webservers" and "db". So we will create a playbook that uses exactly those two.
The debug module is very useful when we are creating playbooks and want to test them in between. For example, we can use it to display the values of variables. In our case, we define a parameter for the module:
msg: Any string that will be output during the play.
Now we just need to define the variable "test_text". Differently for each group. We create another directory on the same level as "host_vars":
mkdir ~/ansible-guide/group_vars
mkdir ~/ansible-guide/group_vars/webservers
mkdir ~/ansible-guide/group_vars/db
In both directories we add a "main.yml":
So we have done the same as with the host_vars, only this time at group level. Now let's run the new playbook:
cd ~/ansible-guide
ansible-playbook -i inventory.txt groupvars-test.yml
ASK [Gathering Facts] ****************************************************************************
ok: [ansible-guide-1]
ok: [ansible-guide-2]
ok: [ansible-guide-3]
TASK [Debug Ausgabe] ****************************************************************************
ok: [ansible-guide-1] => {
"msg": "I am a host in the group 'webservers'!"
}
ok: [ansible-guide-2] => {
"msg": "I am a host in the group 'webservers'!"
}
ok: [ansible-guide-3] => {
"msg": "I am a host in the group 'db'!"
}
PLAY RECAP ****************************************************************************
ansible-guide-1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-guide-2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-guide-3 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
As we can see above, each of the three hosts now pulls the group-specific variable "test_text" and outputs it accordingly.
Summary
In this chapter you have learned what variables are and how we can define them in different places. In the next chapter I will introduce you to facts. These are very similar to variables, but there are some differences!
See you then!
Mow