Get specific parts of the output in Ansible - loops

I need to get only specific parts of the output from Ansible, but it is giving me the whole information. I've tried using filtering unsuccessfully.
This is the playbook I am testing with (it has a loop and is probably what is throwing me off)
- name: PLAYBOOK -> Testing
hosts: esxi
gather_facts: no
vars_files:
- vars.yml
vars:
vmnic:
- vmnic0
- vmnic1
tasks:
- name: Get NIC driver/firmware details - shell
shell: esxcli network nic get -n {{ item }} | grep -e Driver -e Firmware -e Version -e Name
loop: "{{ vmnic }}"
register: nic_details
- name: Output NIC driver/firmware details
debug: var=item.stdout_lines
loop: "{{ nic_details['results'] }}"
This is the output I get for one host, I only need the last bit i.e. Driver Info, Driver, Firmware Version, Version and `Name of each VMNIC.
ok: [srv-pocte02.test.local] => (item={'changed': True, 'end': '2022-05-19 15:50:50.326514', 'stdout': ' Driver Info: \n Driver: igbn\n Firmware Version: 1.61.0:0x8000090e\n Version: 1.4.1\n Name: vmnic0', 'cmd': 'esxcli network nic get -n vmnic0 | grep -e Driver -e Firmware -e Version -e Name', 'stderr': '', 'start': '2022-05-19 15:50:49.515808', 'invocation': {'module_args': {'stdin_add_newline': True, 'argv': None, 'stdin': None, 'removes': None, 'creates': None, 'warn': False, '_uses_shell': True, 'executable': None, 'chdir': None, 'strip_empty_ends': True, '_raw_params': 'esxcli network nic get -n vmnic0 | grep -e Driver -e Firmware -e Version -e Name'}}, 'rc': 0, 'msg': '', 'delta': '0:00:00.810706', 'stdout_lines': [' Driver Info: ', ' Driver: igbn', ' Firmware Version: 1.61.0:0x8000090e', ' Version: 1.4.1', ' Name: vmnic0'], 'stderr_lines': [], 'failed': False, 'item': 'vmnic0', 'ansible_loop_var': 'item'}) => {
"ansible_loop_var": "item",
"item": {
"ansible_loop_var": "item",
"changed": true,
"cmd": "esxcli network nic get -n vmnic0 | grep -e Driver -e Firmware -e Version -e Name",
"delta": "0:00:00.810706",
"end": "2022-05-19 15:50:50.326514",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "esxcli network nic get -n vmnic0 | grep -e Driver -e Firmware -e Version -e Name",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": false
}
},
"item": "vmnic0",
"msg": "",
"rc": 0,
"start": "2022-05-19 15:50:49.515808",
"stderr": "",
"stderr_lines": [],
"stdout": " Driver Info: \n Driver: igbn\n Firmware Version: 1.61.0:0x8000090e\n Version: 1.4.1\n Name: vmnic0",
"stdout_lines": [
" Driver Info: ",
" Driver: igbn",
" Firmware Version: 1.61.0:0x8000090e",
" Version: 1.4.1",
" Name: vmnic0"
]
},
"item.stdout_lines": [
" Driver Info: ",
" Driver: igbn",
" Firmware Version: 1.61.0:0x8000090e",
" Version: 1.4.1",
" Name: vmnic0"
]
}
ok: [srv-pocte02.test.local] => (item={'start': '2022-05-19 15:50:50.867894', 'msg': '', 'cmd': 'esxcli network nic get -n vmnic1 | grep -e Driver -e Firmware -e Version -e Name', 'rc': 0, 'invocation': {'module_args': {'stdin_add_newline': True, 'stdin': None, 'removes': None, 'strip_empty_ends': True, '_uses_shell': True, 'creates': None, 'warn': False, 'chdir': None, 'executable': None, '_raw_params': 'esxcli network nic get -n vmnic1 | grep -e Driver -e Firmware -e Version -e Name', 'argv': None}}, 'changed': True, 'stderr': '', 'end': '2022-05-19 15:50:51.706813', 'stdout': ' Driver Info: \n Driver: igbn\n Firmware Version: 1.61.0:0x8000090e\n Version: 1.4.1\n Name: vmnic1', 'delta': '0:00:00.838919', 'stdout_lines': [' Driver Info: ', ' Driver: igbn', ' Firmware Version: 1.61.0:0x8000090e', ' Version: 1.4.1', ' Name: vmnic1'], 'stderr_lines': [], 'failed': False, 'item': 'vmnic1', 'ansible_loop_var': 'item'}) => {
"ansible_loop_var": "item",
"item": {
"ansible_loop_var": "item",
"changed": true,
"cmd": "esxcli network nic get -n vmnic1 | grep -e Driver -e Firmware -e Version -e Name",
"delta": "0:00:00.838919",
"end": "2022-05-19 15:50:51.706813",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "esxcli network nic get -n vmnic1 | grep -e Driver -e Firmware -e Version -e Name",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": false
}
},
"item": "vmnic1",
"msg": "",
"rc": 0,
"start": "2022-05-19 15:50:50.867894",
"stderr": "",
"stderr_lines": [],
"stdout": " Driver Info: \n Driver: igbn\n Firmware Version: 1.61.0:0x8000090e\n Version: 1.4.1\n Name: vmnic1",
"stdout_lines": [
" Driver Info: ",
" Driver: igbn",
" Firmware Version: 1.61.0:0x8000090e",
" Version: 1.4.1",
" Name: vmnic1"
]
},
"item.stdout_lines": [
" Driver Info: ",
" Driver: igbn",
" Firmware Version: 1.61.0:0x8000090e",
" Version: 1.4.1",
" Name: vmnic1"
]
}

You can use the map filter to extract one field of a list of dictionaries. It can also be used to map one filter to each items of the list, for example, a from_yaml that could help you have dictionaries out of the string you get in stdout.
Given the task:
- debug:
var: nic_details.results | map(attribute="stdout") | map('from_yaml')
This would result in:
nic_details.results | map(attribute="stdout") | map('from_yaml'):
- Driver Info:
Driver: igbn
Firmware Version: 1.61.0:0x8000090e
Version: 1.4.1
Name: vmnic0
- Driver Info:
Driver: igbn
Firmware Version: 1.61.0:0x8000090e
Version: 1.4.1
Name: vmnic1

Related

Unable to fetch existing file. Error: "the remote file does not exist, not transferring, ignored"

I want ansible to read a csv file in test server (10.15.170.22), but keep getting this error : "the remote file does not exist, not transferring, ignored".
This is how I structured my playbooks:
- name: Find files in sub-folders
hosts: testserverhost
tasks:
- name: Search for files in folder
win_find:
paths: C:\folder\data\
register: file
- name: set_fact
set_fact:
filename: "{{ file | json_query('files[0].filename') }}"
Since there seems to be no way for ansible to directly read the file in test server 10.15.170.22, I had to include a fetch file task before the read_csv task.
- name: Fetching file and reading data
hosts: localhost
tasks:
- name: Set fact
set_fact:
filename: "{{ hostvars['10.15.170.22']['filename'] }}"
- name: Fetch file from test server to ansible controller server
fetch:
src: C:\folder\data\{{ filename }}
dest: /var/lib/awx/projects/ansibleproject/testproject/
flat: yes
- name: Read file
read_csv:
path: /var/lib/awx/projects/ansibleproject/testproject/{{ filename }}
key: FirstName
fieldnames: FirstName,LastName
delimiter: ','
register: userdata
Output of playbooks:
TASK [Search for files in folder] ********************************************************************************************
ok: [10.15.170.22] => {"changed": false, "examined": 1, "files": [{"attributes": "Archive", "checksum": "b86f10b0f2305c6c97a1464c19d9f53dce6b0367", "creationtime": 1676388747.9286883, "exists": true, "extension": ".csv", "filename": "testdata.csv", "hlnk_targets": [], "isarchive": true, "isdir": false, "ishidden": false, "isjunction": false, "islnk": false, "isreadonly": false, "isreg": true, "isshared": false, "lastaccesstime": 1676388747.9504724, "lastwritetime": 1676301040, "lnk_source": null, "lnk_target": null, "nlink": 1, "owner": "BUILTIN\\Administrators", "path": "C:\\folder\\data\\testdata.csv", "sharename": null, "size": 1225}], "matched": 1}
TASK [set_fact] ********************************************************************************************
ok: [10.15.170.22] => {
"ansible_facts": {
"filename": "testdata.csv"
},
"changed": false
}
TASK [Set fact] ********************************************************************************************
ok: [localhost] => {
"ansible_facts": {
"filename": "testdata.csv"
},
"changed": false
}
TASK [Fetch file from test server to ansible controller server] ********************************************************************************************
fatal: [localhost]: FAILED! => {
"changed": false,
"invocation": {
"module_args": {
"src": "C:\\folder\\data\\testdata.csv"
}
},
"msg": "the remote file does not exist, not transferring, ignored"
}
I tried switching testserverhost to localhost, and also tried C:\folder\data\testdata.csv and C:/folder/data/testdata.csv for src, but I still get errors. I don't understand where did it go wrong.

Assigning IP to multiple hosts

I am trying to assign a range of IP to different hosts, but first I am checking if the IP is already attributed. If it is, it gives me an error, so I would like to avoid that too(actually I can assign the IP, but can't pass that error, I am using ignore_errors: yes for now but I would like a better way). Here is part of my script:
---
- hosts: all
gather_facts: yes
become: yes
vars_files:
- client2
tasks:
- set_fact:
intip: "{{ intip | default([]) + [item] }}"
loop: "{{ ansible_interfaces | map('extract',ansible_facts, 'ipv4') | select('defined') | map(attribute='address') | list }}"
ignore_errors: yes
- name: Check IP address
shell:
"ip a a '{{ start_ip|ipmath(my_idx) }}' dev spanbr"
loop_control:
index_var: my_idx
when: (item == inventory_hostname )
loop: "{{ ansible_play_hosts }}"
ignore_errors: yes
I am using this var file, but maybe there is a better way? (I am trying to use something like the first example, but can't get my head around to use it on every hosts)
First vars file (didn't try yet):
client:
- interface:
- local_ip: 10.10.10.10
- name: eth1
- interface:
- local_ip: 10.10.10.11
- name: eth1
Second file:
interface:
- config:
- name: eth0
- config:
- name: eth1
start_ip: 10.10.10.10
I can get only one interface getting the IP and ignoring the address, but as the when conditional is not a loop it only check one interface:
Output:
TASK [Check IP address] ************************************************************************************************************************
skipping: [host2] => (item=host1)
failed: [host1] (item=host1) => {"ansible_index_var": "my_idx", "ansible_loop_var": "item", "changed": true, "cmd": "ip a a '10.10.10.10' dev spanbr", "delta": "0:00:00.005429", "end": "2022-10-20 08:49:43.800954", "item": "host1", "msg": "non-zero return code", "my_idx": 0, "rc": 2, "start": "2022-10-20 08:49:43.795525", "stderr": "RTNETLINK answers: File exists", "stderr_lines": ["RTNETLINK answers: File exists"], "stdout": "", "stdout_lines": []}
skipping: [host1] => (item=host2)
...ignoring
failed: [host2] (item=host2) => {"ansible_index_var": "my_idx", "ansible_loop_var": "item", "changed": true, "cmd": "ip a a '10.10.10.11' dev spanbr", "delta": "0:00:00.002691", "end": "2022-10-20 07:49:43.815422", "item": "host2", "msg": "non-zero return code", "my_idx": 1, "rc": 2, "start": "2022-10-20 07:49:43.812731", "stderr": "RTNETLINK answers: File exists", "stderr_lines": ["RTNETLINK answers: File exists"], "stdout": "", "stdout_lines": []}
...ignoring
I would like to use a notify but I need to use the loop on the task itself so that may be an issue...
Any ideas please??
Here is the output of intip (set_fact) if that helps:
TASK [set_fact] ********************************************************************************************************************************
ok: [host1] => (item=192.168.1.100)
ok: [host1] => (item=127.0.0.1)
ok: [host1] => (item=10.10.10.10)
ok: [host1] => (item=169.254.0.1)
ok: [host2] => (item=127.0.0.1)
ok: [host2] => (item=10.10.10.11)
ok: [host2] => (item=192.168.1.101)
Alright, I found a way to do it, by using host_vars and group_vars, and making my script even easier to write :
---
- hosts: all
gather_facts: yes
become: yes
tasks:
- set_fact:
intip: "{{ hostvars[inventory_hostname].ansible_all_ipv4_addresses }}"
- name: Check IP address
shell:
"ip a a '{{ local_ip }}' dev spanbr"
when: local_ip not in intip
Just by creating 2 files in host_vars.
host1.yaml:
ansible_host: "some_ip"
local_ip: 10.10.10.10
and the same for host2.yaml with different values. All is working great and it's making the script much easier to read too.

Ansible - Looping through list and writing into it

I am ansible beginner and I have a problem with ansible playbook that should gather info about system version from multiple servers.
First step was to gather info about server (if it uses Jboss or Tomcat and where it is) - I was able to do it and store it in list like this:
"server_list": [
{
"hostname": "tst-24.ph.koop.cz",
"path": "/srv/ais/test/knz-batch/eap",
"pid": "14660",
"type": "wildfly/jboss",
"version": "",
"version_cmd": "/srv/ais/test/knz-batch/eap/bin/standalone.sh --version"
},
{
"hostname": "tst-24.ph.koop.cz",
"path": "/srv/ais/test/knz-eap/eap",
"pid": "20153",
"type": "wildfly/jboss",
"version": "",
"version_cmd": "/srv/ais/test/knz-eap/eap/bin/standalone.sh --version"
},
{
"hostname": "tst-24.ph.koop.cz",
"path": "/srv/ais/skoleni/knz-ws-int/eap",
"pid": "24861",
"type": "wildfly/jboss",
"version": "",
"version_cmd": "/srv/ais/skoleni/knz-ws-int/eap/bin/standalone.sh --version"
},
{
"hostname": "tst-24.ph.koop.cz",
"path": "/srv/ais/skoleni/knz-ws/wildfly",
"pid": "25195",
"type": "wildfly/jboss",
"version": "",
"version_cmd": "/srv/ais/skoleni/knz-ws/wildfly/bin/standalone.sh --version"
},
{
"hostname": "tst-24.ph.koop.cz",
"path": "/srv/ais/skoleni/knz-ws/undertow",
"pid": "25667",
"type": "tomcat",
"version": "",
"version_cmd": "/srv/ais/skoleni/knz-ws/undertow/bin/version.sh --version"
},
{
"hostname": "tst-24.ph.koop.cz",
"path": "/srv/ais/skoleni/knz/wildfly",
"pid": "26446",
"type": "wildfly/jboss",
"version": "",
"version_cmd": "/srv/ais/skoleni/knz/wildfly/bin/standalone.sh --version"
}
]
Now I need to run other shell command (that will use version_cmd) to get that version (with use of GREP).
But I don't know how to write into list when looping through. And second problem is how to make more conditions (to use other regex for wildfly/jboss)
- name: Get server version
shell: "{{item.version_cmd}}| grep -Po {{ regexp_tomcat_version }}"
loop: "{{ server_list }}"
loop_control:
label: "Check version for {{ item.path }}"
when: item.type == "tomcat"
register: server_list.version
vars:
regexp_tomcat_version: 'Server version:\s*([^(\n)]*)'
ignore_errors: yes
Is this thing even possible in Ansible?
For example, given the list below for testing
server_list:
- hostname: srv1
path: /usr/bin/cc
type: prod
version_cmd: cc --version
version_parse: awk 'FNR == 1 {print $4}'
- hostname: srv1
path: /usr/local/bin/python
type: devel
version_cmd: python --version
version_parse: awk 'FNR == 1 {print $2}'
- hostname: srv2
path: /usr/bin/cc
type: prod
version_cmd: cc --version
version_parse: awk 'FNR == 1 {print $4}'
- hostname: srv2
path: /usr/local/bin/python
type: devel
version_cmd: python --version
version_parse: awk 'FNR == 1 {print $2}'
Create a dynamic group of hostnames in the first play and run it in the second play. In the second play, register the versions into the variable server_list_versions. Then iterate ansible_play_hosts and create the dictionary versions.
- name: Create group server_group
hosts: localhost
tasks:
- add_host:
name: "{{ item }}"
groups: server_group
server_list: "{{ server_list }}"
loop: "{{ server_list|map(attribute='hostname')|unique }}"
- name: Collect versions
hosts: server_group
gather_facts: false
tasks:
- shell: "{{ item.version_cmd }}|{{ item.version_parse }}"
loop: "{{ server_list|selectattr('hostname', 'eq', inventory_hostname) }}"
loop_control:
label: "Check version for {{ item.path }}"
# when: item.type == "devel"
register: server_list_versions
- set_fact:
versions: "{{ versions|d({})|
combine({item: hostvars[item].server_list_versions.results|
json_query('[].{path: item.path,
version: stdout,
stderr: stderr}')}) }}"
loop: "{{ ansible_play_hosts }}"
run_once: true
gives
versions:
srv1:
- path: /usr/bin/cc
stderr: ''
version: 11.0.1
- path: /usr/local/bin/python
stderr: ''
version: 3.8.12
srv2:
- path: /usr/bin/cc
stderr: ''
version: 11.0.1
- path: /usr/local/bin/python
stderr: '/bin/sh: python: not found'
version: ''
If you want to update the attribute version in server_list create a dictionary
- set_fact:
vers_dict: "{{ vers_dict|d({})|
combine({item: hostvars[item].server_list_versions.results|
json_query('[].{path: item.path,
version: stdout}')|
items2dict(key_name='path',
value_name='version')}) }}"
loop: "{{ ansible_play_hosts }}"
run_once: true
gives
vers_dict:
srv1:
/usr/bin/cc: 11.0.1
/usr/local/bin/python: 3.8.12
srv2:
/usr/bin/cc: 11.0.1
/usr/local/bin/python: ''
Then, use this dictionary to update the attribute version
- set_fact:
server_list_new: "{{ server_list_new|d([]) +
[item|combine({'version': version})] }}"
loop: "{{ server_list }}"
vars:
version: "{{ vers_dict[item.hostname][item.path] }}"
run_once: true
gives
server_list_new:
- hostname: srv1
path: /usr/bin/cc
type: prod
version: 11.0.1
version_cmd: cc --version
version_parse: awk 'FNR == 1 {print $4}'
- hostname: srv1
path: /usr/local/bin/python
type: devel
version: 3.8.12
version_cmd: python --version
version_parse: awk 'FNR == 1 {print $2}'
- hostname: srv2
path: /usr/bin/cc
type: prod
version: 11.0.1
version_cmd: cc --version
version_parse: awk 'FNR == 1 {print $4}'
- hostname: srv2
path: /usr/local/bin/python
type: devel
version: ''
version_cmd: python --version
version_parse: awk 'FNR == 1 {print $2}'

Loop ansible_host stuck with first item

I am using the module csv-source-of-truth (https://github.com/joelwking/csv-source-of-truth) to get the IP and OS information from a csv file. I was able to register these info into a vsheet and using debug, I can see that I can loop through the contents of the vsheet.
However, when I use ios_command and try to loop through the vsheet, it seems that it gets stuck at the first entry of the vsheet.
This are the contents of the Inventory.csv file:
192.168.68.201,ios
192.168.68.202,ios
Code:
---
- hosts: localhost
gather_facts: false
tasks:
- name: Block
block:
- name: Use CSV
csv_to_facts:
src: '{{playbook_dir}}/NEW/Inventory.csv'
vsheets:
- INFO:
- IP
- OS
- debug:
msg: '{{item.IP}}'
loop: '{{INFO}}'
- name: Show Version
vars:
ansible_host: '{{item.IP}}'
ansible_network_os: '{{item.OS}}'
ansible_user: cisco
ansible_ssh_pass: cisco
ansible_connection: network_cli
ansible_become: yes
ansible_become_method: enable
ios_command:
commands: show version
register: output
loop: '{{INFO}}'
- name: Show the output of looped Show Version
debug:
var: output
- name: Show just the stdout_lines
debug:
var: output.results.{{item}}.stdout_lines
with_sequence: "0-{{output|length - 2}}"
You will notice on the output that it only has results for R1 when you look at the uptime information. i.e. R1 has an uptime of such and such.
PLAY [localhost] **********************************************************************************************************************************************
TASK [Use CSV] ************************************************************************************************************************************************
ok: [localhost]
TASK [debug] **************************************************************************************************************************************************
ok: [localhost] => (item={u'IP': u'192.168.68.201', u'OS': u'ios'}) => {
"msg": "192.168.68.201"
}
ok: [localhost] => (item={u'IP': u'192.168.68.202', u'OS': u'ios'}) => {
"msg": "192.168.68.202"
}
TASK [Show Version] *******************************************************************************************************************************************
ok: [localhost] => (item={u'IP': u'192.168.68.201', u'OS': u'ios'})
ok: [localhost] => (item={u'IP': u'192.168.68.202', u'OS': u'ios'})
TASK [Show the output of looped Show Version] *****************************************************************************************************************
ok: [localhost] => {
"output": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"auth_pass": null,
"authorize": null,
"commands": [
"show version"
],
"host": null,
"interval": 1,
"match": "all",
"password": null,
"port": null,
"provider": null,
"retries": 10,
"ssh_keyfile": null,
"timeout": null,
"username": null,
"wait_for": null
}
},
"item": {
"IP": "192.168.68.201",
"OS": "ios"
},
"stdout": [
-- Output removed for brevity
],
"stdout_lines": [
[
"-- Output removed for brevity
"R1 uptime is 1 hour, 34 minutes",
]
]
},
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"auth_pass": null,
"authorize": null,
"commands": [
"show version"
],
"host": null,
"interval": 1,
"match": "all",
"password": null,
"port": null,
"provider": null,
"retries": 10,
"ssh_keyfile": null,
"timeout": null,
"username": null,
"wait_for": null
}
},
"item": {
"IP": "192.168.68.202",
"OS": "ios"
},
"stdout": [
-- Output removed for brevity
],
"stdout_lines": [
[
-- Output removed for brevity
"R1 uptime is 1 hour, 34 minutes",
]
]
}
]
}
}
TASK [Show just the stdout_lines] *****************************************************************************************************************************
ok: [localhost] => (item=0) => {
"ansible_loop_var": "item",
"item": "0",
"output.results.0.stdout_lines": [
[
-- Output removed for brevity
"R1 uptime is 1 hour, 34 minutes",
]
]
}
ok: [localhost] => (item=1) => {
"ansible_loop_var": "item",
"item": "1",
"output.results.1.stdout_lines": [
[
-- Output removed for brevity
"R1 uptime is 1 hour, 34 minutes",
]
]
}
PLAY RECAP ****************************************************************************************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Try to create an inventory
- name: Create inventory
add_host:
hostname: '{{ item.IP }}'
groups: temp_group_01
ansible_network_os: '{{ item.OS }}'
ansible_user: cisco
ansible_ssh_pass: cisco
ansible_connection: network_cli
ansible_become: yes
ansible_become_method: enable
loop: '{{ INFO }}'
and delegate to the hosts
- name: Show Version
ios_command:
commands: show version
register: output
delegate_to: '{{ item }}'
loop: '{{ groups['temp_group_01'] }}'
Explanation
From the play below can be seen that the connection does not obey the changed ansible_host and keeps using the first item in the loop.
- hosts: test_01
tasks:
- command: hostname
register: result
vars:
ansible_host: "{{ item }}"
loop:
- test_02
- test_03
- debug:
msg: "{{ result.results|map(attribute='stdout')|list }}"
gives
TASK [command] ******************************************************************************
changed: [test_01] => (item=test_02)
changed: [test_01] => (item=test_03)
TASK [debug] ********************************************************************************
ok: [test_01] => {
"msg": [
"test_02",
"test_02"
]
}
This behavior is very probably caused by the connection plugin because vars works as expected. The play below
- hosts: test_01
tasks:
- command: echo "{{ ansible_host }}"
register: result
vars:
ansible_host: "{{ item }}"
loop:
- test_02
- test_03
- debug:
msg: "{{ result.results|map(attribute='stdout')|list }}"
gives
TASK [command] ******************************************************************************
changed: [test_01] => (item=test_02)
changed: [test_01] => (item=test_03)
TASK [debug] ********************************************************************************
ok: [test_01] => {
"msg": [
"test_02",
"test_03"
]
}
As a result, it's not possible to loop ansible_host. Instead, delegate_to shall be used.

Ansible: Iterate and include multiple variable files

I have hundreds of files (generated through some application) which I am trying to iterate and include them as variable files.
See below files as example. There are many more variables in each of the files, I have toned down to make the example clear.
# cat /tmp/abc/dev1.yml
---
app_name: dev1
instance: dev
port: 1234
server: test1
#
# cat /tmp/abc/dev2.yml
---
app_name: dev2
instance: dev
port: 4567
server: test2
#
# cat /tmp/abc/dev3.yml
---
app_name: dev3
instance: dev
port: 2223
server: test3
#
Now, when I use these tasks in my playbook, I can see the variables (app_name, instance, port, etc) defined in the files (/tmp/abc/*.yml) in the output as ansible_facts.
- action: shell echo "{{ item }}"
with_fileglob: /tmp/abc/*
register: res
- include_vars: "{{ item.item }}"
with_items: res.results
when: item.changed == True
register: task1
This is my output, when I run the playbook.
root#vikas027:~# ansible-playbook -v configs.yml
PLAY [dev] **************************************************************
GATHERING FACTS ***************************************************************
ok: [vikas027.test.com]
TASK: [shell echo "{{ item }}"] ***********************************************
changed: [vikas027.test.com] => (item=/tmp/abc/dev3.yml) => {"changed": true, "cmd": "echo \"/tmp/abc/dev3.yml\"", "delta": "0:00:00.004915", "end": "2015-08-31 20:11:57.702623", "item": "/tmp/abc/dev3.yml", "rc": 0, "start": "2015-08-31 20:11:57.697708", "stderr": "", "stdout": "/tmp/abc/dev3.yml", "warnings": []}
changed: [vikas027.test.com] => (item=/tmp/abc/dev2.yml) => {"changed": true, "cmd": "echo \"/tmp/abc/dev2.yml\"", "delta": "0:00:00.004945", "end": "2015-08-31 20:11:58.130295", "item": "/tmp/abc/dev2.yml", "rc": 0, "start": "2015-08-31 20:11:58.125350", "stderr": "", "stdout": "/tmp/abc/dev2.yml", "warnings": []}
changed: [vikas027.test.com] => (item=/tmp/abc/dev1.yml) => {"changed": true, "cmd": "echo \"/tmp/abc/dev1.yml\"", "delta": "0:00:00.004864", "end": "2015-08-31 20:11:58.440205", "item": "/tmp/abc/dev1.yml", "rc": 0, "start": "2015-08-31 20:11:58.435341", "stderr": "", "stdout": "/tmp/abc/dev1.yml", "warnings": []}
TASK: [include_vars {{ item.item }}] ******************************************
ok: [vikas027.test.com] => (item={u'cmd': u'echo "/tmp/abc/dev3.yml"', u'end': u'2015-08-31 20:11:57.702623', u'stderr': u'', u'stdout': u'/tmp/abc/dev3.yml', u'changed': True, u'rc': 0, 'item': '/tmp/abc/dev3.yml', u'warnings': [], u'delta': u'0:00:00.004915', 'invocation': {'module_name': u'shell', 'module_args': u'echo "/tmp/abc/dev3.yml"'}, 'stdout_lines': [u'/tmp/abc/dev3.yml'], u'start': u'2015-08-31 20:11:57.697708'}) => {"ansible_facts": {"app_name": "dev3", "instance": "dev", "port": 2223, "server": "test3"}, "item": {"changed": true, "cmd": "echo \"/tmp/abc/dev3.yml\"", "delta": "0:00:00.004915", "end": "2015-08-31 20:11:57.702623", "invocation": {"module_args": "echo \"/tmp/abc/dev3.yml\"", "module_name": "shell"}, "item": "/tmp/abc/dev3.yml", "rc": 0, "start": "2015-08-31 20:11:57.697708", "stderr": "", "stdout": "/tmp/abc/dev3.yml", "stdout_lines": ["/tmp/abc/dev3.yml"], "warnings": []}}
ok: [vikas027.test.com] => (item={u'cmd': u'echo "/tmp/abc/dev2.yml"', u'end': u'2015-08-31 20:11:58.130295', u'stderr': u'', u'stdout': u'/tmp/abc/dev2.yml', u'changed': True, u'rc': 0, 'item': '/tmp/abc/dev2.yml', u'warnings': [], u'delta': u'0:00:00.004945', 'invocation': {'module_name': u'shell', 'module_args': u'echo "/tmp/abc/dev2.yml"'}, 'stdout_lines': [u'/tmp/abc/dev2.yml'], u'start': u'2015-08-31 20:11:58.125350'}) => {"ansible_facts": {"app_name": "dev2", "instance": "dev", "port": 4567, "server": "test2"}, "item": {"changed": true, "cmd": "echo \"/tmp/abc/dev2.yml\"", "delta": "0:00:00.004945", "end": "2015-08-31 20:11:58.130295", "invocation": {"module_args": "echo \"/tmp/abc/dev2.yml\"", "module_name": "shell"}, "item": "/tmp/abc/dev2.yml", "rc": 0, "start": "2015-08-31 20:11:58.125350", "stderr": "", "stdout": "/tmp/abc/dev2.yml", "stdout_lines": ["/tmp/abc/dev2.yml"], "warnings": []}}
ok: [vikas027.test.com] => (item={u'cmd': u'echo "/tmp/abc/dev1.yml"', u'end': u'2015-08-31 20:11:58.440205', u'stderr': u'', u'stdout': u'/tmp/abc/dev1.yml', u'changed': True, u'rc': 0, 'item': '/tmp/abc/dev1.yml', u'warnings': [], u'delta': u'0:00:00.004864', 'invocation': {'module_name': u'shell', 'module_args': u'echo "/tmp/abc/dev1.yml"'}, 'stdout_lines': [u'/tmp/abc/dev1.yml'], u'start': u'2015-08-31 20:11:58.435341'}) => {"ansible_facts": {"app_name": "dev1", "instance": "dev", "port": 1234, "server": "test1"}, "item": {"changed": true, "cmd": "echo \"/tmp/abc/dev1.yml\"", "delta": "0:00:00.004864", "end": "2015-08-31 20:11:58.440205", "invocation": {"module_args": "echo \"/tmp/abc/dev1.yml\"", "module_name": "shell"}, "item": "/tmp/abc/dev1.yml", "rc": 0, "start": "2015-08-31 20:11:58.435341", "stderr": "", "stdout": "/tmp/abc/dev1.yml", "stdout_lines": ["/tmp/abc/dev1.yml"], "warnings": []}}
PLAY RECAP ********************************************************************
vikas027.test.com : ok=3 changed=1 unreachable=0 failed=0
root#vikas027:~#
How can I reference variables like app_name, instance, port, etc in other tasks ? I tried using below code and few other combinations in vain.
- debug: msg="{{ task1.app_name }}"
with_items: task1.results
Your variable files, dev1.yml, dev2.yml, etc. all reference the same variable names. Is this on purpose, or just part of your example? I ask because your example as it's currently shown, would result in just the last set of variables being defined, so as far as ansible is concerned it appears that the variables would ultimately just be defined as if you did this:
vars:
app_name: dev3
instance: dev
port: 2223
server: test3
You would just reference the variables by their given names:
- debug: var=app_name
- debug: var=instance
etc.
What I'm guessing you actually want to be doing is having those variable files look something like this:
---
app:
dev1:
instance: "dev"
port: "1234"
server: "host1"
and
---
app:
dev2:
instance: "dev"
port: "4321"
server: "host2"
You would then reference your objects something like this:
# should list "dev1", "dev2", "dev3"...
- debug: msg={{ item }}
with_dict: app
# should list the server names for each device
- debug: var = app[item][server]
with_dict: app
I was working on this whole day today, tried umpteen configuration changes in vain. Finally, it is working the way I wanted it to work.
This is what one needs to do if in a similar situation. Hope this helps someone.
First, register your facts locally. I chose the default /etc/ansible/facts.d/ directory for the same. Here are more details.
Key things to remember:-
Extension should be .fact
File should be executable (I gave 0755)
Format is JSON (I've used yaml-to-json to convert my yaml files to json. You can use ruby or perl one-liners too.)
Then, to iterate the facts registered in the previous step, we need to load/reload the facts in the playbook in order to use them in tasks/playbooks.
- local_action: setup filter=ansible_local
- template: src=nginx_lb.conf.j2 dest=/etc/nginx/conf.d/{{ item.key }}.conf
with_dict: "{{ ansible_local }}"
All variables can now be used in the jinja2 template. For example, port can be referenced as item.value.port.

Resources