Include vars from a file for multiple import_playbooks - ansible

This is an error using Ansible 2.7
I am trying to include a files with vars in it into my playbook that has multiple import_playbooks.
I have 3 files.
one with all the vars
one with a playbook and a task
one with an import_playbook
My playbook:
---
- name: Create CPG
hosts: localhost
tasks:
- name: Create CPG "{{ cpg_name }}"
hpe3par_cpg:
storage_system_ip: "{{ storage_system_ip }}"
storage_system_username: "{{ storage_system_username }}"
storage_system_password: "{{ storage_system_password }}"
state: present
cpg_name: "{{ cpg_name }}"
#domain: "{{ domain }}"
growth_increment: "{{ growth_increment }}"
growth_increment_unit: "{{ growth_increment_unit }}"
growth_limit: "{{ growth_limit }}"
growth_limit_unit: "{{ growth_limit_unit }}"
growth_warning: "{{ growth_warning }}"
growth_warning_unit: "{{ growth_warning_unit }}"
raid_type: "{{ raid_type }}"
set_size: "{{ set_size }}"
high_availability: "{{ high_availability }}"
disk_type: "{{ disk_type }}"
The playbook where I will call my tasks and my variables:
---
- name: master
hosts: localhost
- import_playbook: create_CPG.yml
include_vars: properties/variables.yml
I get this error when running "ansible-playbook create_master.yml"
ERROR! Syntax Error while loading YAML.
mapping values are not allowed in this context
The error appears to have been in '/home/simon/Documents/Ansible/create_MasterPlaybook.yml': line 6, column 16, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- import_playbook: create_CPG.yml
include_vars: properties/variables.yml
^ here
Is there a way to call a file with variables for only this import_playbook.
thank you

(Update 14 Jun 2022 for Ansible 2.12)
Q: "Is there a way to call a file with variables for only this import_playbook?"
A: No. It is not. The variables included in a task are visible in the whole playbook to the affected hosts. include_vars is a task. The correct syntax would be
- name: master
hosts: localhost
tasks:
- include_vars: properties/variables.yml
- import_playbook: create_CPG.yml
import_playbook is not a tasks. It is a module that "Includes a file with a list of plays to be executed". You can declare vars in the scope of the imported plays. For example,
- import_playbook: create_CPG.yml
vars:
var1: value of var1
But, you can't declare vars_files at the moment. The import below
- import_playbook: create_CPG.yml
vars_files:
- properties/variables.yml
will fail with the error:
ERROR! 'vars_files' is not a valid attribute for a PlaybookInclude
See Ansible issue Support vars_files when using import_playbook #36806. This will solve your problem when implemented.
Workaround
Put the name of the file into a variable and use vars. For example, given the inventory
shell> cat hosts
host1 var1=1
host2 var1=2
, the file with the play
shell> cat pb-import.yml
- hosts: host1,host2
gather_facts: false
vars_files:
- "{{ my_vars_file|default('vars_file_default.yml') }}"
tasks:
- debug:
msg: |-
var1: {{ var1|d('undef') }}
var2: {{ var2|d('undef') }}
, the playbook
shell> cat pb.yml
- import_playbook: pb-import.yml
- import_playbook: pb-import.yml
vars:
my_vars_file: vars_file_play_A.yml
- import_playbook: pb-import.yml
vars:
my_vars_file: vars_file_play_B.yml
, and the files with the variables for various playbooks
shell> cat vars_file_default.yml
var2: play default
shell> cat vars_file_play_A.yml
var2: play A
shell> cat vars_file_play_B.yml
var2: play B
gives
shell> ansible-playbook pb.yml
PLAY [host1,host2] ***************************************************************************
TASK [debug] *********************************************************************************
ok: [host1] =>
msg: |-
var1: 1
var2: play default
ok: [host2] =>
msg: |-
var1: 2
var2: play default
PLAY [host1,host2] ***************************************************************************
TASK [debug] *********************************************************************************
ok: [host1] =>
msg: |-
var1: 1
var2: play A
ok: [host2] =>
msg: |-
var1: 2
var2: play A
PLAY [host1,host2] ***************************************************************************
TASK [debug] *********************************************************************************
ok: [host1] =>
msg: |-
var1: 1
var2: play B
ok: [host2] =>
msg: |-
var1: 2
var2: play B
PLAY RECAP ***********************************************************************************
host1: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host2: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Scope of the variables
The scope of the variables is more complex. See Scoping variables. There are three main scopes: global, play, and host. For example, given the inventory
shell> cat hosts
host1 var1=1
host2 var1=2
host3 var1=3
, the file
shell> cat my_vars.yml
var4: included in a task
, and the playbook
shell> cat pb.yml
- hosts: host1,host2
gather_facts: false
vars:
var2: play
tasks:
- include_vars: my_vars.yml
- debug:
msg: |-
var1: {{ var1|d('undef') }}
var2: {{ var2|d('undef') }}
var3: {{ var3|d('undef') }}
var4: {{ var4|d('undef') }}
- hosts: host1,host2,host3
gather_facts: false
tasks:
- debug:
msg: |-
var1: {{ var1|d('undef') }}
var2: {{ var2|d('undef') }}
var3: {{ var3|d('undef') }}
var4: {{ var4|d('undef') }}
gives the results below:
Host scope. The variable var1 declared in the inventory is available to the host in the whole playbook
Play scope: The variable var2 declared in the first play is available to all hosts in the first play only. The variable is undefined in the second play.
Global scope. The variable var3 declared as an extra variable at the command line is available globally to all hosts in all plays.
The variable var4* declared in the - include_vars: my_vars.yml is available to the affected hosts only, i.e. hosts in the first play host1,host2. The variable is undefined to host3.
shell> ansible-playbook pb.yml -e var3=global
PLAY [host1,host2] ***************************************************************************
TASK [include_vars] **************************************************************************
ok: [host1]
ok: [host2]
TASK [debug] *********************************************************************************
ok: [host1] =>
msg: |-
var1: 1
var2: play
var3: global
var4: included in a task
ok: [host2] =>
msg: |-
var1: 2
var2: play
var3: global
var4: included in a task
PLAY [host1,host2,host3] *********************************************************************
TASK [debug] *********************************************************************************
ok: [host1] =>
msg: |-
var1: 1
var2: undef
var3: global
var4: included in a task
ok: [host2] =>
msg: |-
var1: 2
var2: undef
var3: global
var4: included in a task
ok: [host3] =>
msg: |-
var1: 3
var2: undef
var3: global
var4: undef
PLAY RECAP ***********************************************************************************
host1: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host2: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host3: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The results are the same if the playbooks are imported. For example, given the file
shell> cat pb-import.yml
- hosts: host1,host2,host3
gather_facts: false
tasks:
- debug:
msg: |-
var1: {{ var1|d('undef') }}
var2: {{ var2|d('undef') }}
var3: {{ var3|d('undef') }}
var4: {{ var4|d('undef') }}
and the imports added to the previous playbook
- import_playbook: pb-import.yml
vars:
var2: play3
- import_playbook: pb-import.yml
give
PLAY [host1,host2,host3] *********************************************************************
TASK [debug] *********************************************************************************
ok: [host1] =>
msg: |-
var1: 1
var2: play3
var3: global
var4: included in a task
ok: [host2] =>
msg: |-
var1: 2
var2: play3
var3: global
var4: included in a task
ok: [host3] =>
msg: |-
var1: 3
var2: play3
var3: global
var4: undef
PLAY [host1,host2,host3] *********************************************************************
TASK [debug] *********************************************************************************
ok: [host1] =>
msg: |-
var1: 1
var2: undef
var3: global
var4: included in a task
ok: [host2] =>
msg: |-
var1: 2
var2: undef
var3: global
var4: included in a task
ok: [host3] =>
msg: |-
var1: 3
var2: undef
var3: global
var4: undef

The import_playbook module doesn't accept - include_vars as an argument.
- import_playbook: create_CPG.yml
- include_vars: properties/variables.yml
For calling include_vars specific you can use the include_vars module inside that playbook

this will be the ideal solution if you want to run import playbook with when condition. where system_ip can defined in properties/variables.yml
- hosts: all
gather_facts: yes
vars_files:
- properties/variables.yml
tasks:
- set_fact:
storage_system_ip: "{{ system_ip }}"
- import_playbook: create_CPG.yml
when: "storage_system_ip == '192.168.0.2' and ansible_architecture == 'x86_64'"

Related

Control ansible play execution inventory hosts

Need assistance how to make one particular task in the playbook run on all hosts.
I have a below task inside a role
---
- name: Task 1
shell: echo "Must be run on one node"
- name: Task 2
shell: echo "Must be run on one node"
- name: Task 3
shell: echo "This must run on all nodes inside inventory"
I am running this task by passing --limit but "Task 3" must run on all hosts in the inventory. I tried using below block but not its not executing
- name: Verify limit is set
debug:
msg: "Must use --limit"
when: ansible_limit is not defined
run_once: true
- name: Print all nodes
debug:
msg: "Running on node {{ item }}"
with_items: "{{ ansible_play_hosts_all }}"
There are many options. For example, create a dictionary that will control the flow
shell> cat group_vars/all
task_hosts:
1: [host_1] # task 1 runs on host_1 only
2: [host_2] # task 2 runs on host_2 only
3: "{{ groups.all }}" # task 3 runs on all hosts
Given the inventory
shell> cat hosts
host_1
host_2
host_3
The playbook
shell> cat pb.yml
- hosts: all
gather_facts: false
tasks:
- name: Task 1
debug:
msg: echo "Must be run on one node"
when: inventory_hostname in task_hosts.1
- name: Task 2
debug:
msg: echo "Must be run on one node"
when: inventory_hostname in task_hosts.2
- name: Task 3
debug:
msg: echo "This must run on all nodes inside inventory"
when: inventory_hostname in task_hosts.3
gives
shell> ansible-playbook pb.yml
PLAY [all] ***********************************************************************************
TASK [Task 1] ********************************************************************************
skipping: [host_2]
ok: [host_1] =>
msg: echo "Must be run on one node"
skipping: [host_3]
TASK [Task 2] ********************************************************************************
skipping: [host_1]
skipping: [host_3]
ok: [host_2] =>
msg: echo "Must be run on one node"
TASK [Task 3] ********************************************************************************
ok: [host_1] =>
msg: echo "This must run on all nodes inside inventory"
ok: [host_2] =>
msg: echo "This must run on all nodes inside inventory"
ok: [host_3] =>
msg: echo "This must run on all nodes inside inventory"
PLAY RECAP ***********************************************************************************
host_1: ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
host_2: ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
host_3: ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
Notes:
Add one more level if there are more plays in the playbook
shell> cat group_vars/all
play_task_hosts:
1: # play 1
1: [host_1] # task 1 runs on host_1 only
2: [host_2] # task 2 runs on host_2 only
3: "{{ groups.all }}" # task 3 runs on all hosts
You might want to automate the updates if there are many tasks in many plays in many playbooks
shell> cat update_pb.yml
- hosts: localhost
vars:
my_dir: "{{ playbook_dir }}"
my_pb: pb.yml
my_flow: play_task_hosts
pb: "{{ lookup('file', my_pb)|from_yaml }}"
_tasks: "{{ pb|json_query('[].tasks') }}"
_update_tasks: |
{% for play in _tasks %}
-
{% set play_index = loop.index %}
{% for task in play %}
{% set condition = ["inventory_hostname in ",
my_flow, ".",
play_index, ".",
loop.index]|join() %}
- {{ task|combine({"when": [condition]}, list_merge='append_rp') }}
{% endfor %}
{% endfor %}
update_tasks: "{{ _update_tasks|from_yaml }}"
_update_pb: |
{% for play in pb %}
- {{ play|combine({"tasks": update_tasks[loop.index0]}) }}
{% endfor %}
update_pb: "{{ _update_pb|from_yaml }}"
tasks:
- block:
- debug:
var: _tasks
- debug:
var: update_tasks
- debug:
var: update_pb
when: debug|d(false)|bool
- copy:
dest: "{{ my_dir }}/{{ my_pb }}.update"
content: |
{{ update_pb|to_nice_yaml }}
Generally, if you have a different task list for different hosts, it begs for having different roles. Basically, the best practice for Ansible is to have plays to control where code is been executed.
If role start to decide which host should have which tasks, it twists Ansible upside down, and make role wig the inventory and plays. Don't do it. Role should do what was told and do not make cross-host decisions.
If it's a single task and you don't want to have two roles, you may pass a variable from inventory/play to the role. It can be either flag (like doo_foo), configured in inventory, which is checked as when: doo_foo, or as a delegation:
- name: Do foo
run_once: true
do: foo
delegate_to: '{{ foo_delegation_host }}'
Note: there is run_once flag, which forces Ansible to do task one, and delegate_to is controlling where it runs. The value foo_delegation_host should come from the play (as role parameter or a play variable).

Ansible: How to check multiple servers for a text file value, to decide which servers to run the script on?

I am trying to ask Ansible to check if a server is passive or active based on the value of a specific file in each server, then Ansible will decide which server it runs the next script on.
For example with 2 servers:
Server1
cat /tmp/currentstate
PASSIVE
Server2
cat /tmp/currentstate
ACTIVE
In Ansible
Trigger next set of jobs on server where the output was ACTIVE.
Once the jobs complete, trigger next set of jobs on server where output was PASSIVE
What I have done so far to grab the state, and output the value to Ansible is
- hosts: "{{ hostname1 | mandatory }}"
gather_facts: no
tasks:
- name: Grab state of first server
shell: |
cat {{ ans_script_path }}currentstate.log
register: state_server1
- debug:
msg: "{{ state_server1.stdout }}"
- hosts: "{{ hostname2 | mandatory }}"
gather_facts: no
tasks:
- name: Grab state of second server
shell: |
cat {{ ans_script_path }}currentstate.log
register: state_server2
- debug:
msg: "{{ state_server2.stdout }}"
What I have done so far to trigger the script
- hosts: "{{ active_hostname | mandatory }}"
tasks:
- name: Run the shutdown on active server first
shell: sh {{ ans_script_path }}stopstart_terracotta_main.sh shutdown
register: run_result
- debug:
msg: "{{ run_result.stdout }}"
- hosts: "{{ passive_hostname | mandatory }}"
tasks:
- name: Run the shutdown on passive server first
shell: sh {{ ans_script_path }}stopstart_terracotta_main.sh shutdown
register: run_result
- debug:
msg: "{{ run_result.stdout }}"
but I don't know how to set the value of active_hostname & passive_hostname based on the value from the script above.
How can I set the Ansible variable of active_hostname & passive_hostname based on the output of the first section?
A better solution came to my mind is to include hosts in new groups according to their state.
This would be more optimal in case there are more than two hosts.
- hosts: all
gather_facts: no
vars:
ans_script_path: /tmp/
tasks:
- name: Grab state of server
shell: |
cat {{ ans_script_path }}currentstate.log
register: server_state
- add_host:
hostname: "{{ item }}"
# every host will be added to a new group according to its state
groups: "{{ 'active' if hostvars[item].server_state.stdout == 'ACTIVE' else 'passive' }}"
# Shorter, but the new groups will be in capital letters
# groups: "{{ hostvars[item].server_state.stdout }}"
loop: "{{ ansible_play_hosts }}"
changed_when: false
- name: show the groups the host(s) are in
debug:
msg: "{{ group_names }}"
- hosts: active
gather_facts: no
tasks:
- name: Run the shutdown on active server first
shell: hostname -f # changed that for debugging
register: run_result
- debug:
msg: "{{ run_result.stdout }}"
- hosts: passive
gather_facts: no
tasks:
- name: Run the shutdown on passive server first
shell: hostname -f
register: run_result
- debug:
msg: "{{ run_result.stdout }}"
test-001 is PASSIVE
test-002 is ACTIVE
PLAY [all] ***************************************************************
TASK [Grab state of server] **********************************************
ok: [test-002]
ok: [test-001]
TASK [add_host] **********************************************************
ok: [test-001] => (item=test-001)
ok: [test-001] => (item=test-002)
TASK [show the groups the host(s) are in] ********************************
ok: [test-001] => {
"msg": [
"passive"
]
}
ok: [test-002] => {
"msg": [
"active"
]
}
PLAY [active] *************************************************************
TASK [Run the shutdown on active server first] ****************************
changed: [test-002]
TASK [debug] **************************************************************
ok: [test-002] => {
"msg": "test-002"
}
PLAY [passive] ************************************************************
TASK [Run the shutdown on passive server first] ****************************
changed: [test-001]
TASK [debug] **************************************************************
ok: [test-001] => {
"msg": "test-001"
}
PLAY RECAP ****************************************************************
test-001 : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test-002 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
For example, given two remote hosts
shell> ssh admin#test_11 cat /tmp/currentstate.log
ACTIVE
shell> ssh admin#test_13 cat /tmp/currentstate.log
PASSIVE
The playbook below reads the files and runs the commands on active and passive servers
shell> cat pb.yml
- hosts: "{{ host1 }},{{ host2 }}"
gather_facts: false
vars:
server_states: "{{ dict(ansible_play_hosts|
zip(ansible_play_hosts|
map('extract', hostvars, ['server_state', 'stdout'])|
list)) }}"
server_active: "{{ server_states|dict2items|
selectattr('value', 'eq', 'ACTIVE')|
map(attribute='key')|list }}"
server_pasive: "{{ server_states|dict2items|
selectattr('value', 'eq', 'PASSIVE')|
map(attribute='key')|list }}"
tasks:
- command: cat /tmp/currentstate.log
register: server_state
- debug:
var: server_state.stdout
- block:
- debug:
var: server_states
- debug:
var: server_active
- debug:
var: server_pasive
run_once: true
- command: echo 'Shutdown active server'
register: out_active
delegate_to: "{{ server_active.0 }}"
- command: echo 'Shutdown passive server'
register: out_pasive
delegate_to: "{{ server_pasive.0 }}"
- debug:
msg: |
{{ server_active.0 }}: [{{ out_active.stdout }}] {{ out_active.start }}
{{ server_pasive.0 }}: [{{ out_pasive.stdout }}] {{ out_pasive.start }}
run_once: true
shell> ansible-playbook pb.yml -e host1=test_11 -e host2=test_13
PLAY [test_11,test_13] ***********************************************************************
TASK [command] *******************************************************************************
changed: [test_13]
changed: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
server_state.stdout: ACTIVE
ok: [test_13] =>
server_state.stdout: PASSIVE
TASK [debug] *********************************************************************************
ok: [test_11] =>
server_states:
test_11: ACTIVE
test_13: PASSIVE
TASK [debug] *********************************************************************************
ok: [test_11] =>
server_active:
- test_11
TASK [debug] *********************************************************************************
ok: [test_11] =>
server_pasive:
- test_13
TASK [command] *******************************************************************************
changed: [test_11]
changed: [test_13 -> test_11]
TASK [command] *******************************************************************************
changed: [test_11 -> test_13]
changed: [test_13]
TASK [debug] *********************************************************************************
ok: [test_11] =>
msg: |-
test_11: [Shutdown active server] 2022-10-27 11:16:00.766309
test_13: [Shutdown passive server] 2022-10-27 11:16:02.501907
PLAY RECAP ***********************************************************************************
test_11: ok=8 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_13: ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
From the description of your use case I understand that you like to perform tasks on certain servers which a have service role installed (annot.: Terracotta Server) and based on a certain service state.
Therefore, I like to recommend an approach with Custom facts.
Depending on if you have control about where the currentstate.log is placed or how it is structured, you could use in example something like
cat /tmp/ansible/service/terracotta.fact
[currentstate]
ACTIVE = true
PASSIVE = false
or add dynamic facts by adding executable scripts to facts.d ...
Means, alternatively, you can add the current service state to your host facts by creating and running a script in facts.d, which would just read the content of /tmp/currentstate.log.
Then, a sample playbook like
---
- hosts: localhost
become: false
gather_facts: true
fact_path: /tmp/ansible/service
gather_subset:
- "!all"
- "!min"
- "local"
tasks:
- name: Show Gathered Facts
debug:
msg: "{{ ansible_facts }}"
when: ansible_local.terracotta.currentstate.active | bool
will result into an output of
TASK [Show Gathered Facts] ******
ok: [localhost] =>
msg:
ansible_local:
terracotta:
currentstate:
active: 'true'
passive: 'false'
gather_subset:
- '!all'
- '!min'
- local
module_setup: true
An other approach is to address How the inventory is build and Group the hosts
[terracotta:children]
terracotta_active
terracotta_passive
[terracotta_active]
terracotta1.example.com
[terracotta_passive]
terracotta2.example.com
You can then just easily and simple define where a playbook or task should run, just by Targeting hosts and groups
ansible-inventory -i hosts--graph
#all:
|--#terracotta:
| |--#terracotta_active:
| | |--terracotta1.example.com
| |--#terracotta_passive:
| | |--terracotta2.example.com
|--#ungrouped:
ansible-inventory -i hosts terracotta_active --graph
#terracotta_active:
|--terracotta1.example.com
or Conditionals based on ansible_facts, in example
when: 'terracotta_active' in group_names
... from my understanding, both would be minimal and simple solutions without re-implementing functionality which seems to be already there.

Display Ansible playbook with lookups interpolated

I have an Ansible playbook that looks, in part, like this:
...
environment:
F2B_DB_PURGE_AGE: "{{ lookup('env','F2B_DB_PURGE_AGE') }}"
F2B_LOG_LEVEL: "{{ lookup('env','F2B_LOG_LEVEL') }}"
SSMTP_HOST: "{{ lookup('env','SSMTP_HOST') }}"
SSMTP_PORT: "{{ lookup('env','SSMTP_PORT') }}"
SSMTP_TLS: "{{ lookup('env','SSMTP_TLS') }}"
...
Is there any way to run ansible-playbook so that it will show the results of the YAML file after replacing the lookups with their values? That is, I would like to be able to run something like ansible-playbook file.yaml --dry-run and see on standard output (assuming the environment variables were set appropriately):
...
environment:
F2B_DB_PURGE_AGE: "20"
F2B_LOG_LEVEL: "debug"
SSMTP_HOST: "smtp.example.com"
SSMTP_PORT: "487"
SSMTP_TLS: "true"
...
Set the environment for testing
shell> cat env.sh
#!/usr/bin/bash
export F2B_DB_PURGE_AGE="20"
export F2B_LOG_LEVEL="debug"
export SSMTP_HOST="smtp.example.com"
export SSMTP_PORT="487"
export SSMTP_TLS="true"
shell> source env.sh
Given the inventory
shell> cat hosts
localhost ansible_connection=local
Q: "Run something like ansible-playbook file.yaml --dry-run and see environment"
A: The below playbook does the job
shell> cat file.yml
- hosts: all
vars:
my_environment:
F2B_DB_PURGE_AGE: "{{ lookup('env','F2B_DB_PURGE_AGE') }}"
F2B_LOG_LEVEL: "{{ lookup('env','F2B_LOG_LEVEL') }}"
SSMTP_HOST: "{{ lookup('env','SSMTP_HOST') }}"
SSMTP_PORT: "{{ lookup('env','SSMTP_PORT') }}"
SSMTP_TLS: "{{ lookup('env','SSMTP_TLS') }}"
tasks:
- block:
- debug:
msg: |
my_environment:
{{ my_environment|to_nice_yaml|indent(2) }}
- meta: end_play
when: dry_run|d(false)|bool
- debug:
msg: Continue ...
Set dry_run=true
shell> ansible-playbook file.yml -e dry_run=true
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg: |-
my_environment:
F2B_DB_PURGE_AGE: '20'
F2B_LOG_LEVEL: debug
SSMTP_HOST: smtp.example.com
SSMTP_PORT: '487'
SSMTP_TLS: 'true'
TASK [meta] **********************************************************************************
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
By default, the playbook will execute tasks
shell> ansible-playbook file.yml
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
skipping: [localhost]
TASK [meta] **********************************************************************************
skipping: [localhost]
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg: Continue ...
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Optionally, let the playbook gather facts and use the dictionary ansible_env. Use the filer ansible.utils.keep_keys to select your variables
- hosts: all
gather_facts: true
vars:
my_environment_vars:
- F2B_DB_PURGE_AGE
- F2B_LOG_LEVEL
- SSMTP_HOST
- SSMTP_PORT
- SSMTP_TLS
my_environment: "{{ ansible_env|
ansible.utils.keep_keys(target=my_environment_vars) }}"
tasks:
- block:
- debug:
msg: |
my_environment:
{{ my_environment|to_nice_yaml|indent(2) }}
- meta: end_play
when: dry_run|d(false)|bool
- debug:
msg: Continue ...

How to delegate facts to localhost from a play targeting remote hosts

ansible version: 2.9.16 running on RHEL 7.9 python ver = 2.7.5 targeting windows 2016 servers. ( should behave the same for linux target servers too)
EDIT: Switched to using host specific variables in inventory to avoid confusion that Iam just trying to print hostnames of a group. Even here its a gross simplification. Pretend that var1 is obtained dynamically for each server instead of being declared in the inventory file.
My playbook has two plays. One targets 3 remote servers ( Note: serial: 0 i.e Concurrently ) and another just the localhost. In play1 I am trying to delegate facts obtained from each of these hosts to the localhost using delegate_facts and delegate_to. The intent is to have these facts delegated to a single host ( localhost ) so I can use it later in a play2 (using hostvars) that targets the localhost. But strangely thats not working. It only has information from the last host from Play1.
Any help will be greatly appreciated.
my inventory file inventory/test.ini looks like this:
[my_servers]
svr1 var1='abc'
svr2 var1='xyz'
svr3 var1='pqr'
My Code:
## Play1
- name: Main play that runs against multiple remote servers and builds a list.
hosts: 'my_servers' # my inventory group that contains 3 servers svr1,svr2,svr3
any_errors_fatal: false
ignore_unreachable: true
gather_facts: true
serial: 0
tasks:
- name: initialize my_server_list as a list and delegate to localhost
set_fact:
my_server_list: []
delegate_facts: yes
delegate_to: localhost
- command: /root/complex_script.sh
register: result
- set_fact:
my_server_list: "{{ my_server_list + hostvars[inventory_hostname]['result.stdout'] }}"
# run_once: true ## Commented as I need to query the hostvars for each host where this executes.
delegate_to: localhost
delegate_facts: true
- name: "Print list - 1"
debug:
msg:
- "{{ hostvars['localhost']['my_server_list'] | default(['NotFound']) | to_nice_yaml }}"
# run_once: true
- name: "Print list - 2"
debug:
msg:
- "{{ my_server_list | default(['NA']) }}"
## Play2
- name: Print my_server_list which was built in Play1
hosts: localhost
gather_facts: true
serial: 0
tasks:
- name: "Print my_server_list without hostvars "
debug:
msg:
- "{{ my_server_list | to_nice_json }}"
# delegate_to: localhost
- name: "Print my_server_list using hostvars"
debug:
msg:
- "{{ hostvars['localhost']['my_server_list'] | to_nice_yaml }}"
# delegate_to: localhost
###Output###
$ ansible-playbook -i inventory/test.ini delegate_facts.yml
PLAY [Main playbook that runs against multiple remote servers and builds a list.] ***********************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************
ok: [svr3]
ok: [svr1]
ok: [svr2]
TASK [initialize] ***************************************************************************************************************************************************************************
ok: [svr1]
ok: [svr2]
ok: [svr3]
TASK [Build a list of servers] **************************************************************************************************************************************************************
ok: [svr1]
ok: [svr2]
ok: [svr3]
TASK [Print list - 1] ***********************************************************************************************************************************************************************
ok: [svr1] =>
msg:
- |-
- pqr
ok: [svr2] =>
msg:
- |-
- pqr
ok: [svr3] =>
msg:
- |-
- pqr
TASK [Print list - 2] ***********************************************************************************************************************************************************************
ok: [svr1] =>
msg:
- - NA
ok: [svr2] =>
msg:
- - NA
ok: [svr3] =>
msg:
- - NA
PLAY [Print my_server_list] *****************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************
ok: [localhost]
TASK [Print my_server_list without hostvars] ************************************************************************************************************************************************
ok: [localhost] =>
msg:
- |-
[
"pqr"
]
TASK [Print my_server_list using hostvars] **************************************************************************************************************************************************
ok: [localhost] =>
msg:
- |-
- pqr
PLAY RECAP **********************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
svr1 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
svr2 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
svr3 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Playbook run took 0 days, 0 hours, 0 minutes, 13 seconds
###Expected Output###
I was expecting the last two debug statements in Play2 to contain the values of var1 for all the servers something like this:
TASK [Print my_server_list using hostvars] **************************************************************************************************************************************************
ok: [localhost] =>
msg:
- |-
- abc
- xyz
- pqr
Use Special Variables, e.g.
- hosts: all
gather_facts: false
tasks:
- set_fact:
my_server_list: "{{ ansible_play_hosts_all }}"
run_once: true
delegate_to: localhost
delegate_facts: true
- hosts: localhost
gather_facts: false
tasks:
- debug:
var: my_server_list
gives
ok: [localhost] =>
my_server_list:
- svr1
- svr2
- svr3
There are many other ways how to create the list, e.g.
- hosts: all
gather_facts: false
tasks:
- debug:
msg: "{{ groups.my_servers }}"
run_once: true
- hosts: all
gather_facts: false
tasks:
- debug:
msg: "{{ hostvars|json_query('*.inventory_hostname') }}"
run_once: true
Q: "Fill the list with outputs gathered by running complex commands."
A: Last example above shows how to create a list from hostvars. Register the result from the complex command, e.g.
shell> ssh admin#srv1 cat /root/complex_script.sh
#!/bin/sh
ifconfig wlan0 | grep inet | cut -w -f3
The playbook
- hosts: all
gather_facts: false
tasks:
- command: /root/complex_script.sh
register: result
- set_fact:
my_server_list: "{{ hostvars|json_query('*.result.stdout') }}"
run_once: true
delegate_to: localhost
delegate_facts: true
- hosts: localhost
gather_facts: false
tasks:
- debug:
var: my_server_list
gives
my_server_list:
- 10.1.0.61
- 10.1.0.62
- 10.1.0.63
Q: "Why the logic of delegating facts to localhost and keep appending them to that list does not work?"
A: The code below (simplified) can't work because the right-hand-side msl value still comes from the hostvars of the inventory_host despite the fact delegate_facts: true. This merely puts the created variable msl into the localhost's hostvars
- hosts: my_servers
tasks:
- set_fact:
msl: "{{ msl|default([]) + [inventory_hostname] }}"
delegate_to: localhost
delegate_facts: true
Quoting from Delegating facts
To assign gathered facts to the delegated host instead of the current host, set delegate_facts to true
As a result of such code, the variable msl will keep the last assigned value only.

ansible: create a list from comma separated string

I want to create a list from comma separated string to pass to loop in ansible, sometime variable can have only one value also
var1=test1,test2 and it can be var1=test1 also
here is my code
- name: Separate facts
set_fact: groups="{{ var1.split(',') }}"
- name: delete
gcp_compute_instance_group:
name: "{{ item }}"
zone: xxx
project: xxx
auth_kind: serviceaccount
service_account_file: xxx
state: absent
loop: "{{ groups }}"
this doesn't work, how can i achieve my requirement
your filter is correct, you do get a list variable. please see below PB and output:
---
- hosts: localhost
gather_facts: false
vars:
var1: test1,test2
var2: test3
tasks:
- name: Create the list
set_fact:
list_var1: "{{ var1.split(',') }}"
list_var2: "{{ var2.split(',') }}"
- debug:
var: list_var1
- debug:
var: list_var2
result:
[is#orangehat-29 temp]$ ansible-playbook test.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [Create the list] *****************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ***************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"list_var1": [
"test1",
"test2"
]
}
TASK [debug] ***************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"list_var2": [
"test3"
]
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[is#orangehat-29 temp]$

Resources