How to make a variable available across multiple ansible playbooks? - ansible

In my first playbook, I am asking user for a value and storing in a variable. I would like that variable to be accessible in other playbooks. There is only one host in the inventory btw.
My first playbook:
---
- name: Get the name of the city from the user
hosts: all
gather_facts: yes
vars_prompt:
- name: my_city
prompt: "Enter the name of city: "
private: no
tasks:
- name: Set fact for city
set_fact:
city: "{{ my_city }}"
cacheable: yes
In another playbook, when I try to print the variable I set in the previous one, I get an error:
---
- name: Print a fact
hosts: all
gather_facts: yes
tasks:
- name: Print ansible_facts['city'] variable
debug:
msg: "Value of city variable is {{ ansible_facts['city'] }}"
Error:
fatal: [testing]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'city'\n\nThe error appears to be in '/home/user/ansible_tasks/print_fact.yml': line 6, column 7, 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: Print ansible_facts['city'] variable\n ^ here\n"}

Enable the cache if you want to use it. For example,
shell> grep fact_caching ansible.cfg
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_cache
fact_caching_prefix = ansible_facts_
fact_caching_timeout = 86400
Then the playbook below
shell> cat pb1.yml
- hosts: localhost
gather_facts: false
tasks:
- set_fact:
city: my_city
cacheable: true
will store the variable city in the cache
shell> cat /tmp/ansible_cache/ansible_facts_localhost
{
"city": "my_city"
}
The next playbook
shell> cat pb2.yml
- hosts: localhost
gather_facts: false
tasks:
- debug:
var: city
will read the cache
shell> ansible-playbook pb2.yml
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
city: my_city
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
If you want to cache the same variable in multiple hosts, for example
shell> cat hosts
host_1
host_2
host_3
it is sufficient to run the module set_fact once. The playbook
shell> cat pb3.yml
- hosts: all
gather_facts: false
tasks:
- set_fact:
city: my_city
cacheable: true
run_once: true
will store the variable city in the cache of all hosts
shell> grep -r city /tmp/ansible_cache/
/tmp/ansible_cache/ansible_facts_host_3: "city": "my_city"
/tmp/ansible_cache/ansible_facts_host_1: "city": "my_city"
/tmp/ansible_cache/ansible_facts_host_2: "city": "my_city"
The next playbook
shell> cat pb4.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: city
will read the cache
shell> ansible-playbook pb4.yml
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host_1] =>
city: my_city
ok: [host_2] =>
city: my_city
ok: [host_3] =>
city: my_city
PLAY RECAP ***********************************************************************************
host_1: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host_2: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host_3: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Related

How can I call a playbook multiple times?

I have a main login that is as bellow:
- name: Register
hosts: "{{ host }}"
tasks:
- include_role:
name: ROLE_NAME
vars:
service: "{{ service }}"
I want to call my main login multiple time something like bellow:
- name: call main logic
tasks:
- import_playbook:
name: main-logic.yml
loop:
- host: "1"
service: "s1"
- host: "2"
service: "s2"
I didn't find any solution for that, is it possible? HOW?
If you for whatever reason have to run the playbook(s) sequentially create a batch. However, if you want to execute the playbook on all hosts sequentially one after another, use serial. See Setting the batch size with serial. The difference is that you'll see PLAY RECAP once. In the first case, you'll see PLAY RECAP three times.
Create batch
For example,
shell> cat create_batch.yml
- hosts: localhost
vars:
my_batch:
- {host: host1, service: s1}
- {host: host2, service: s2}
- {host: host3, service: s3}
tasks:
- command: which bash
register: which_bash
- copy:
dest: "{{ playbook_dir }}/batch.sh"
mode: "a+x"
content: |
{{ '#!' }}{{ which_bash.stdout }}
{% for i in my_batch %}
ansible-playbook main-logic.yml -e "host={{ i.host }}" -e "service={{ i.service }}"
{% endfor %}
gives
shell> cat batch.sh
#!/usr/bin/bash
ansible-playbook main-logic.yml -e "host=host1" -e "service=s1"
ansible-playbook main-logic.yml -e "host=host2" -e "service=s2"
ansible-playbook main-logic.yml -e "host=host3" -e "service=s3"
Given the project tree for testing
shell> tree .
.
├── ansible.cfg
├── batch.sh
├── create_batch.yml
├── hosts
└── main-logic.yml
0 directories, 5 files
shell> cat ansible.cfg
[defaults]
gathering = explicit
inventory = $PWD/hosts
roles_path = $PWD/roles
remote_tmp = ~/.ansible/tmp
retry_files_enabled = false
stdout_callback = yaml
shell> cat hosts
host1
host2
host3
shell> cat main-logic.yml
- hosts: "{{ host }}"
tasks:
- debug:
msg: "{{ inventory_hostname }} {{ service }}"
The batch gives
shell> ./batch.sh
PLAY [host1] **************************************************************************************
TASK [debug] **************************************************************************************
ok: [host1] =>
msg: host1 s1
PLAY RECAP ****************************************************************************************
host1: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAY [host2] **************************************************************************************
TASK [debug] **************************************************************************************
ok: [host2] =>
msg: host2 s2
PLAY RECAP ****************************************************************************************
host2: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAY [host3] **************************************************************************************
TASK [debug] **************************************************************************************
ok: [host3] =>
msg: host3 s3
PLAY RECAP ****************************************************************************************
host3: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Setting the batch size with serial
Declare the below variables. For example, in group_vars/all
shell> cat group_vars/all/my_batch.yml
my_batch:
- {host: host1, service: s1}
- {host: host2, service: s2}
- {host: host3, service: s3}
my_batch_host_sevice: "{{ my_batch|
items2dict(key_name='host',
value_name='service') }}"
service: "{{ my_batch_host_sevice[inventory_hostname] }}"
Set serial: 1. The playbook will serve the remote hosts one after another
shell> cat main-logic.yml
- hosts: "{{ host }}"
serial: 1
tasks:
- debug:
var: ansible_play_batch
- debug:
msg: "{{ inventory_hostname }} {{ service }}"
gives
shell> ansible-playbook main-logic.yml -e host='host1:host2:host3'
PLAY [host1:host2:host3] **************************************************************************
TASK [debug] **************************************************************************************
ok: [host1] =>
ansible_play_batch:
- host1
TASK [debug] **************************************************************************************
ok: [host1] =>
msg: host1 s1
PLAY [host1:host2:host3] **************************************************************************
TASK [debug] **************************************************************************************
ok: [host2] =>
ansible_play_batch:
- host2
TASK [debug] **************************************************************************************
ok: [host2] =>
msg: host2 s2
PLAY [host1:host2:host3] **************************************************************************
TASK [debug] **************************************************************************************
ok: [host3] =>
ansible_play_batch:
- host3
TASK [debug] **************************************************************************************
ok: [host3] =>
msg: host3 s3
PLAY RECAP ****************************************************************************************
host1: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host2: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host3: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible vars_prompt and msg usage prints hello world

Based on input from Zeitounator I have updated the original question. The following works
root#devenv:~/scripts/uia# cat addVlanTest.yml
---
- name: PLAY 111 Add Vlan ;
hosts: all
vars_prompt:
- name: vlanIdToAdd
prompt: vlan ID to add
private: no
default: "50"
tasks:
- name: "Debug task Take 1"
ansible.builtin.debug:
var: vlanIdToAdd
- name: "Debug task Take 2"
ansible.builtin.debug:
msg: "{{ vlanIdToAdd }}"
- name: "Debug task Take 3"
ansible.builtin.debug:
msg: "Creating vlan {{ vlanIdToAdd }}"
Correct:
msg: "Creating vlan {{ vlanIdToAdd }}"
Incorrect:
msg:"Creating vlan {{ vlanIdToAdd }}"
What is the scope ? Variables do not survive after plays. If you need to use it in a different play save it.
root#devenv:~/scripts/uia# cat addVlanTest.yml
---
- name: PLAY 111 Add Vlan ;
hosts: all
vars_prompt:
- name: vlanIdToAdd
prompt: vlan ID to add
private: no
default: "50"
tasks:
- name: "Debug vars Take 1"
debug:
var=vlanIdToAdd
- name: "Debug vars Take 1"
set_fact:
new_vlan_id="{{ vlanIdToAdd }}"
- name: PLAY 112 Add Vlan - IOS;
hosts: ios
tasks:
- name: "Debug vars Take 2"
debug:
var=new_vlan_id
As reported in my comment, vars_prompt are only available inside a given play as this is their scope. This is explained in plabooks variable documentation
From my suggestion, you have tried to use set_fact. As you will see in the same documentation, those vars are scoped to host and are only available while playing a task on host having the given fact. Meanwhile, you can access facts from other host using the hostvars magic variable
Here is one way to work arround this kind of chicken-egg problem. It might not be exactly what you are looking for but will hopefully give you some ideas for your best solution. I tried to make it self explanatory.
The following test.yml playbook:
---
- name: Play dedicated to vars_prompt and storing facts in localhost
hosts: localhost
gather_facts: false
vars_prompt:
- name: my_prompt
prompt: enter a value
private: no
tasks:
- name: store prompt in fact
set_fact:
my_prompt: "{{ my_prompt }}"
- name: Use fact on host a by directly calling it in a task
hosts: a
gather_facts: false
tasks:
- name: display the fact
debug:
msg: "{{ hostvars['localhost'].my_prompt }}"
- name: Use the fact on host b by first assigning to a play var
hosts: b
gather_facts: false
vars:
my_prompt: "{{ hostvars['localhost'].my_prompt }}"
tasks:
- name: display the fact
debug:
msg: "{{ my_prompt }}"
Gives:
$ ansible-playbook -i a,b, test.yml
enter a value: toto
PLAY [Play dedicated to vars_prompt and storing facts in localhost] ************************************************************************************************************************************************
TASK [store prompt in fact] ***************************************************************************************************************************************************************************************
ok: [localhost]
PLAY [Use fact on host a by directly calling it in a task] ********************************************************************************************************************************************************
TASK [display the fact] *******************************************************************************************************************************************************************************************
ok: [a] => {
"msg": "toto"
}
PLAY [Use the fact on host b by first assigning to a play var] ****************************************************************************************************************************************************
TASK [display the fact] *******************************************************************************************************************************************************************************************
ok: [b] => {
"msg": "toto"
}
PLAY RECAP ********************************************************************************************************************************************************************************************************
a : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
b : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Meanwhile, if this is possible in your scenario, you can as well set the fact on all hosts in your inventory so as to reuse it anywhere, this makes a much simpler playbook:
---
- name: Play dedicated to vars_prompt and storing facts on all host (except implicit localhost)
hosts: all
gather_facts: false
vars_prompt:
- name: my_prompt
prompt: enter a value
private: no
tasks:
- name: store prompt in fact
set_fact:
my_prompt: "{{ my_prompt }}"
- name: Use fact on host a
hosts: a
gather_facts: false
tasks:
- name: display the fact
debug:
msg: "{{ my_prompt }}"
- name: Use the fact on host b
hosts: b
gather_facts: false
tasks:
- name: display the fact
debug:
msg: "{{ my_prompt }}"
- name: Simple demo implicit localhost is not part of the all group and does not have the fact
hosts: localhost
gather_facts: false
tasks:
- name: show fact is undefined on localhost
debug:
var: my_prompt
Gives:
$ ansible-playbook -i a,b, test.yml
enter a value: titi
PLAY [Play dedicated to vars_prompt and storing facts on all host (except implicit localhost)] ********************************************************************************************************************
TASK [store prompt in fact] ***************************************************************************************************************************************************************************************
ok: [a]
ok: [b]
PLAY [Use fact on host a] *****************************************************************************************************************************************************************************************
TASK [display the fact] *******************************************************************************************************************************************************************************************
ok: [a] => {
"msg": "titi"
}
PLAY [Use the fact on host b] *************************************************************************************************************************************************************************************
TASK [display the fact] *******************************************************************************************************************************************************************************************
ok: [b] => {
"msg": "titi"
}
PLAY [Simple demo implicit localhost is not part of the all group and does not have the fact] *********************************************************************************************************************
TASK [show fact is undefined on localhost] ************************************************************************************************************************************************************************
ok: [localhost] => {
"my_prompt": "VARIABLE IS NOT DEFINED!"
}
PLAY RECAP ********************************************************************************************************************************************************************************************************
a : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
b : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible hosts to be set to a substring of a passed variable

I have a play like this:
- name: Perform an action on a Runtime
hosts: all
roles:
- role: mule_action_on_Runtime
A variable at invocation (--extra-vars 'mule_runtime=MuleS01-3.7.3-Testing') has a prefix of the host needed (MuleS01). I want to set hosts: MuleS01. How do I do this?
Given that your pattern is always PartIWant-PartIDonCareAbout-AnotherPartAfterOtherDash you could use the split method of Python, then get the first item of the list via the Jinja filter first.
Here is full working playbook as example:
- hosts: local
gather_facts: no
tasks:
- debug:
msg: "{{ mule_runtime.split('-') | first }}"
This yield the recap:
play.yml --extra-vars 'mule_runtime=MuleS01-3.7.3-Testing'
PLAY [local] *******************************************************************
TASK [debug] *******************************************************************
ok: [local] => {
"msg": "MuleS01"
}
PLAY RECAP *********************************************************************
local : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
With the inventory
shell> cat hosts
MuleS01
MuleS02
MuleS03
this playbook
shell> cat pb.yml
- hosts: all
tasks:
- debug:
msg: Set {{ mule_runtime }}
when: mule_runtime.split('-').0 == inventory_hostname
gives
skipping: [MuleS02]
ok: [MuleS01] => {
"msg": "Set MuleS01-3.7.3-Testing"
}
skipping: [MuleS03]

Issue adding duplicate name with different ansible_user to add_host dynamic inventory

Here is my playbook that builds a dynamic inventory using add_host:
---
- name: "Play 1"
hosts: localhost
gather_facts: no
tasks:
- name: "Search database"
command: > mysql --user=root --password=p#ssword deployment
--host=localhost -Ns -e "SELECT dest_ip,username FROM deploy_dets"
register: command_result
- name: Add hosts
add_host:
name: "{{ item.split('\t')[0] }}"
ansible_user: "{{ item.split('\t')[1] }}"
groups: dest_nodes
with_items: "{{ command_result.stdout_lines }}"
- hosts: dest_nodes
gather_facts: false
tasks:
- debug:
msg: Run the shell script with the arguments `{{ ansible_user }}` here"
The Output is good and as expected when the 'name:' attribute of add_host are of different values IPs viz '10.9.0.100' & '10.8.2.144'
$ ansible-playbook duplicate_hosts.yml
PLAY [Play 1] ***********************************************************************************************************************************************
TASK [Search database] **************************************************************************************************************************************
changed: [localhost]
TASK [Add hosts] ********************************************************************************************************************************************
changed: [localhost] => (item=10.9.0.100 user1)
changed: [localhost] => (item=10.8.2.144 user2)
PLAY [dest_nodes] *******************************************************************************************************************************************
TASK [debug] ************************************************************************************************************************************************
ok: [10.9.0.100] => {
"msg": "Run the shell script with the arguments `user1` here\""
}
ok: [10.8.2.144] => {
"msg": "Run the shell script with the arguments `user2` here\""
}
PLAY RECAP **************************************************************************************************************************************************
10.8.2.144 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.9.0.100 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The problem is when the 'name:' attribute for add_host gets duplicate entry say 10.8.2.144 despite having unique 'ansible_user' value the play ignores the first name, ansible_user entry and runs only once with the latest final entry.
$ ansible-playbook duplicate_hosts.yml
PLAY [Play 1] ***********************************************************************************************************************************************
TASK [Search database] **************************************************************************************************************************************
changed: [localhost]
TASK [Add hosts] ********************************************************************************************************************************************
changed: [localhost] => (item=10.8.2.144 user1)
changed: [localhost] => (item=10.8.2.144 user2)
PLAY [dest_nodes] *******************************************************************************************************************************************
TASK [debug] ************************************************************************************************************************************************
ok: [10.8.2.144] => {
"msg": "Run the shell script with the arguments `user2` here\""
}
PLAY RECAP **************************************************************************************************************************************************
10.8.2.144 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Interestingly the debug shows two entries for add_host name: 10.8.2.144 with the different ansible_users i.e 'user1' and 'user2' but when we run the group it runs just the single and latest name entry and seen in the output above.
I'm on the latest version of ansible.
Can you please provide some solution where i can run the play for every unique 'ansible_user' on the same host ?
In summary: I wish to run multiple tasks on the same host first with 'user1' and then with 'user2'
You can add a alias as inventory hostname. Here I have given the username as hostname(alias).
Please try this, I have not tested it.
- name: Add hosts
add_host:
hostname: "{{ item.split('\t')[1] }}"
ansible_host: "{{ item.split('\t')[0] }}"
ansible_user: "{{ item.split('\t')[1] }}"
groups: dest_nodes
with_items: "{{ command_result.stdout_lines }}"

Passing/updating global variables (e.g. group vars) from within the playbook?

I there any way to pass/update group variables from within the playbook task?
I need to define variables based on results of some commands from one host to use them for other roles and tasks. I know about set_fact but it stores variable as local variable so that I need to address specific host to get it but hostname/address of this host can vary.
Googling and reading docs.ansible.com doesn't help still.
UPD: there is 2 different roles that playing tasks one after another and I need to pass variables between plays.
An option would be to use ansible modules lineinfile, blockinfile, template, and ini_file to update group variables.
For example the play below
- hosts: test_jails
gather_facts: false
vars:
my_groupvar_file: "{{ inventory_dir }}/group_vars/test_jails.yml"
tasks:
- debug:
var: my_last_run
- block:
- command: date "+%F %T"
register: result
- lineinfile:
path: "{{ my_groupvar_file }}"
regexp: "^my_last_run: "
line: "my_last_run: {{ result.stdout }}"
backup: yes
delegate_to: localhost
run_once: true
with group variables group_vars/test_jails.yml
my_last_run: 2019-04-19 11:51:00
gives (abridged):
> ansible-playbook test1.yml
PLAY [test_jails]
TASK [debug]
ok: [test_01] => {
"my_last_run": "2019-04-19 11:51:00"
}
ok: [test_03] => {
"my_last_run": "2019-04-19 11:51:00"
}
ok: [test_02] => {
"my_last_run": "2019-04-19 11:51:00"
}
TASK [command]
changed: [test_01]
TASK [lineinfile]
changed: [test_01 -> localhost]
PLAY RECAP
test_01 : ok=3 changed=2 unreachable=0 failed=0
test_02 : ok=1 changed=0 unreachable=0 failed=0
test_03 : ok=1 changed=0 unreachable=0 failed=0
> cat group_vars/test_jails.yml
my_last_run: 2019-04-19 11:56:51

Resources