Ansible has the ability to use group _vars and host _vars and is very flexible in the way they can be defined. However, there is often a need to store data globally. This is particularly true in a multi-play Ansible playbook. A global variable is one that can be stored in one play and used in another play. This article will show how the concept of global variables can be achieved in Ansible.
The following playbook demonstrates how variables can be used between two playbooks, essentially emulating the global variable concept:
---
- name: "PLAY 01 --- collect inputs and create global vars"
hosts: localhost
gather_facts: no
vars_prompt:
- name: 'router_username'
prompt: "Please enter router username"
private: no
- name: 'router_password'
prompt: "please enter router password"
private: yes
tasks:
- name: Save credentials as global vars
set_fact:
router_username: "{{ router_username }}"
router_password: "{{ router_password }}"
no_log: true
- name: "PLAY 02 --- consume global vars"
hosts: cisco_routers
connection: network_cli
gather_facts: no
tasks:
- name: fetch global vars from PLAY 01
set_fact:
ansible_user: "{{ hostvars['localhost']['router_username'] }}"
ansible_ssh_pass: "{{ hostvars['localhost']['router_password'] }}"
no_log: true
- name: send show run to Cisco devices
cli_command:
command: show running-config
register: show_run
- name: print show_run
debug:
msg: "{{ show_run.stdout_lines }}"
Here is what happens within the playbook: (i) first it prompts for a username and password, (ii) the credentials are stored as host _vars
of localhost, (iii) the same credentials are used in the second play (which runs against different group of hosts) to connect to the Cisco Devices. With this code we effectively use localhost host vars
as a container for our global vars. Please notice the set_fact task in PLAY 01. At first, it may seem redundant but the vars whose content is received through the vars_prompt
are not stored in hostvars[‘locahost’][var_name]
. We do that manually with set_fact.
In PLAY02, cisco_routers is used and not localhost. Let’s have a brief look at our inventory file:
[cisco_routers]
R1 ansible_host="192.168.0.101" device_role="PE"
R2 ansible_host="192.168.0.102" device_role="PE"
[cisco_routers:vars]
ansible_network_os="ios"
For each host in that group, we first create host vars ansible_user
and ansible_ssh_pass
from global vars. Then we are ready to proceed to the rest of the business logic in the playbook.
In both plays no_log is set to true. This is to disable Ansible from printing sensitive data in stdout
if the task fails. We certainly do not want this data to be ever printed in the Ansible output. One important thing to note here is that the data is saved in hostvars
as clear text. So, if you ever do something like debug: var=hostvars
the sensitive data will be printed there.
Sometimes you may want to not only set global vars, but also change them over the course of a multi-play playbook.
Let’s extend our playbook to demonstrate this:
---
- name: "PLAY 01 --- collect inputs and create global vars"
hosts: localhost
gather_facts: no
vars_prompt:
- name: 'router_username'
prompt: "Please enter router username"
private: no
- name: 'router_password'
prompt: "please enter router password"
private: yes
tasks:
- name: Save credentials as global vars
set_fact:
router_username: "{{ router_username }}"
router_password: "{{ router_password }}"
show_config_sent: not yet
no_log: true
- name: print show_config_sent
debug:
msg: "{{ show_config_sent }}"
- name: "PLAY 02 --- consume global vars"
hosts: cisco_routers
connection: network_cli
gather_facts: no
tasks:
- name: fetch global vars from PLAY 01
set_fact:
ansible_user: "{{ hostvars['localhost']['router_username'] }}"
ansible_ssh_pass: "{{ hostvars['localhost']['router_password'] }}"
no_log: true
- name: send show run to Cisco devices
cli_command:
command: show running-config
register: show_run
- name: print show_run
debug:
msg: "{{ show_run.stdout_lines }}"
- name: update global variable
set_fact:
show_config_sent: yes of course
delegate_to: localhost
delegate_facts: true
run_once: true
- name: "PLAY 03 --- read updated global vars from localhost"
hosts: localhost
gather_facts: no
tasks:
- name: print show_config_sent
debug:
msg: "{{ show_config_sent }}"
In PLAY02, in the update global variable task, we instruct Ansible to set the value of the localhost hostvar show_config_sent
. Setting delegate_to: localhost
and delegate_facts: true
are required to achieve that.
Please notice that in PLAY03 we get the variable show_config_sent
as a host variable bound to the localhost.