I have a playbook that looks like this:
- hosts: host1
gather_facts: false
tasks:
- name: "Loop"
command: "echo {{ item }}"
with_items: [ 0, 2, 4, 6, 8, 10 ]
register: hello
- debug: "msg={{ hello.results }}"
Everything works correctly, and the output is returned, but there is tons and tons of output. It turns out that this:
- debug: "msg={{ hello.results.1.stdout }}"
does exactly what I want -- just grab the stdout from the command -- but only for one of the six times through the loop.
What I really want/need to do is this:
- debug: "msg={{ hello.results.*.stdout }}"
where it goes into the hello structure, accesses the results entry, goes to each member of that array, and pulls out the stdout value.
Is this possible?
UPDATE
- hosts: host1
gather_facts: false
tasks:
- name: "Loop"
command: "echo {{ item }}"
with_items: [ 0, 2, 4, 6, 8, 10 ]
register: hello
- debug:
msg: "{{item.stdout}}"
with_items: "{{hello.results}}"
is no less verbose than my original example.
TASK [debug] *******************************************************************
ok: [host1] => (item={'_ansible_parsed': True, 'stderr_lines': [], u'cmd': [
u'echo', u'0'], u'end': u'2018-01-02 20:53:08.916774', '_ansible_no_log': False
, u'stdout': u'0', '_ansible_item_result': True, u'changed': True, 'item': 0,
u'delta': u'0:00:00.002137', u'stderr': u'', u'rc': 0, u'invocation': {u'module_
args': {u'warn': True, u'executable': None, u'_uses_shell': False, u'_raw_params
': u'echo 0', u'removes': None, u'creates': None, u'chdir': None, u'stdin': Non
e}}, 'stdout_lines': [u'0'], u'start': u'2018-01-02 20:53:08.914637', 'failed':
False}) => {
"item": {
"changed": true,
"cmd": [
"echo",
"0"
],
"delta": "0:00:00.002137",
"end": "2018-01-02 20:53:08.916774",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo 0",
"_uses_shell": false,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": 0,
"rc": 0,
"start": "2018-01-02 20:53:08.914637",
"stderr": "",
"stderr_lines": [],
"stdout": "0",
"stdout_lines": [
"0"
]
},
"msg": "0"
}
I get 6 copies of the above construct.
It feels like I'm close but I'm still doing something wrong. I see "msg": "0" at the bottom, which is what I want. I just don't want the rest of it.
Solution:
- debug: "msg={{ hello.results | map(attribute='stdout') | join('\n') }}"
Remark:
By default, Ansible will print visible \n two-character sequences instead of wrapping the lines, so either use a callback plugin for a human readable output (example) or verify the method with:
- copy:
content: "{{ hello.results | map(attribute='stdout') | join('\n') }}"
dest: ./result.txt
and check the contents of the result.txt.
I have used the keyword loop to get stdout from all iterations of the previous loop:
loop: "{{ hello | json_query('results[*].stdout') }}"
I find json_query easiest to use in such register-loop situations. Official documentation can be found here ==> json-query-filter
Sure. The ansible website has documentation that explains how to use register in a loop. You just need to iterate over the hello.results array, as in:
- debug:
msg: "{{item.stdout}}"
with_items: "{{hello.results}}"
What about:
- debug: "msg={{ item.stdout }}"
with_items: "{{ hello.results }}"
I think this construct works well enough for my needs.
- hosts: localhost
gather_facts: false
vars:
stuff: [ 0,2,4,6,8,10 ]
tasks:
- name: "Loop"
command: "echo {{ item }}"
with_items: "{{ stuff }}"
register: hello
- debug: "var=hello.results.{{item}}.stdout"
with_sequence: "0-{{stuff|length - 1}}"
I was looking at a similar problem and was confused by getting lots of output when I was expecting a relatively small msg or var from debug:. Turns out most of that output was the 'label' with which Ansible was prefixing each of those small outputs. It being a few years after this question was originally asked I've been using loop rather than with_items; this also has a label: option in loop_control:, so in my case for a similar problem - getting any /etc/passwd entries for users 'alice' or 'bob',
- hosts: all
gather_facts: false
serial: 1 # output easier to read when grouped by host
tasks:
- name: Look for users in /etc/passwd
command: grep {{ item }} /etc/passwd
register: res
ignore_errors: true
loop:
- alice
- bob
- debug:
msg: "{{ item.stdout_lines }}"
when: not item.failed
loop: "{{ res.results }}"
loop_control:
label: "{{ item.item }}"
Related
Looking for help to understand why I have the additional output from the debug line below:
- name: Check kernel diff
become: true
shell: "sdiff {{ item }}/pre-kernel.out {{ item }}/post-kernel.out | grep '|' | awk -F'|' '{print $2}' | xargs"
register: "kernel"
with_items: "{{work_dir}}"
- debug:
msg: "The system is now running Kernel Version {{ item.stdout }}"
when: "{{ item.changed == true }}"
with_items: "{{ kernel.results}}"
The output from msg is correct, but how can I stop/hide all the detail that appears before it:
TASK [patching : debug] ************************
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item.changed == true }}
ok: [test02] => (item={'stderr_lines': [], 'ansible_loop_var': u'item', u'end': u'2020-05-26 16:23:37.068718', u'stderr': u'', u'stdout': u'4.14.35-1902.302.2.el7uek.x86_64', u'changed': True, 'failed': False, u'delta': u'0:00:00.008894', u'cmd': u"sdiff /var/tmp/patching_2020-05-26/pre-kernel.out /var/tmp/patching_2020-05-26/post-kernel.out | grep '|' | awk -F'|' '{print $2}' | xargs", 'item': u'/var/tmp/patching_2020-05-26', u'rc': 0, u'invocation': {u'module_args': {u'warn': True, u'executable': None, u'_uses_shell': True, u'strip_empty_ends': True, u'_raw_params': u"sdiff /var/tmp/patching_2020-05-26/pre-kernel.out /var/tmp/patching_2020-05-26/post-kernel.out | grep '|' | awk -F'|' '{print $2}' | xargs", u'removes': None, u'argv': None, u'creates': None, u'chdir': None, u'stdin_add_newline': True, u'stdin': None}}, 'stdout_lines': [u'4.14.35-1902.302.2.el7uek.x86_64'], u'start': u'2020-05-26 16:23:37.059824'}) => {
"msg": "The system is now running Kernel Version 4.14.35-1902.302.2.el7uek.x86_64"
}
So it looks more like just:
TASK [patching : debug] ************************
ok: [test02] => {
"msg": "The system is now running Kernel Version 4.14.35-1902.302.2.el7uek.x86_64"
}
var_file.yml for reference:
---
work_dir:
- /var/tmp/patching_{{ansible_date_time.date}}
I've been using this as guidance.
TL;DR;
Based on your exact example, this should be, your debug task:
- debug:
msg: "The system is now running Kernel Version {{ item.stdout }}"
loop: "{{ kernel.results }}"
when: item.changed
loop_control:
label: 'kernel'
Which will output
TASK [debug] *******************************************************************
ok: [local] => (item=kernel) => {
"msg": "The system is now running Kernel Version 4.14.35-1902.302.2.el7uek.x86_64"
}
You can't reduce it totally, as you want but you can indeed make loops less annoyingly verbose by defining a label in the loop_control attribute.
In this label you can select one of the attributes of your dictionary to be displayed when Ansible loops over it rather than the full dictionary.
- debug:
msg: "The system is now running Kernel Version {{ item.stdout }}"
loop:
- some: foo
dict: foo
with: foo
an: foo
annoyingly: foo
long: foo
list: foo
of: foo
attributes: foo
name: bar
stdout: bar
loop_control:
label: "{{ item.name }}"
This will give a recap looking like this
TASK [debug] *******************************************************************
ok: [local] => (item=bar) => {
"msg": "The system is now running Kernel Version bar"
}
Versus the cluttered one without loop_control:
TASK [debug] *******************************************************************
ok: [local] => (item={'some': 'foo', 'dict': 'foo', 'with': 'foo', 'an': 'foo', 'annoyingly': 'foo', 'long': 'foo', 'list': 'foo', 'of': 'foo', 'attributes': 'foo', 'name': 'bar', 'stdout': 'bar'}) => {
"msg": "The system is now running Kernel Version bar"
}
Also, in order to fix your warning, you just need to remove the Jinja curly bracers, as when always assume you are going to pass a Jinja expression.
And testing that something == true is an uneeded extra verbosity too, when: something-that-evaluates-to-true is enough.
So you should use
when: item.changed
Instead of your actual
when: "{{ item.changed == true }}"
PS: since Ansible 2.5 loop should be preferred rather than with_items.
without "loop_control" ansible code
- name: 'Print message if CPU utilization become abnormal'
debug:
msg:
- -------------------------------------------------------
- CPU Utilization = ( 100 - idle time ) = "{{ item.stdout }}"% is idle
- -------------------------------------------------------
#loop:
with_items:
- "{{ incidentcpuworknote_cpu }}"
- "{{ incidentcpuworknote_cpu1 }}"
- "{{ incidentcpuworknote_cpu2 }}"
when: item.stdout| int <= 10
Below is the ansible execution result before using the loop_control
TASK [Print message if CPU utilization become abnormal] ******************************************************************************************
ok: [ansiblenode] => (item={u'stderr_lines': [], u'cmd': u"mpstat -u 1 3|tail -1| awk '{print $NF}'\n", u'end': u'2021-04-03 03:37:38.417169', u'stdout': u'0.00', u'changed': True, u'failed': False, u'delta': u'0:00:03.259453', u'stderr': u'', u'rc': 0, u'stdout_lines': [u'0.00'], u'start': u'2021-04-03 03:37:35.157716'}) =>
msg:
- '-------------------------------------------------------'
- CPU Utilization = ( 100 - idle time ) = "0.00"% is idle
- '-------------------------------------------------------
with "loop_control" ansible code
Note: "loop_control" helped to avoid ansible debug messages while executing the loop
- name: 'Print message if CPU utilization become abnormal'
debug:
msg:
- -------------------------------------------------------
- CPU Utilization = ( 100 - idle time ) = "{{ item.stdout }}"% is idle
- -------------------------------------------------------
#loop:
with_items:
- "{{ incidentcpuworknote_cpu }}"
- "{{ incidentcpuworknote_cpu1 }}"
- "{{ incidentcpuworknote_cpu2 }}"
when: item.stdout| int <= 10
loop_control:
label: "{{ item.stdout }}"
Below is the ansible execution result after using the loop_control
TASK [Print message if CPU utilization become abnormal] *****************************************************
ok: [ansiblenode] => (item=0.00) =>
msg:
- '-------------------------------------------------------'
- CPU Utilization = ( 100 - idle time ) = "0.00"% is idle
- '-------------------------------------------------------'
I have a task
- name: DELEGATED ADMIN ACCOUNTS - check, get and send to the file domain.list
shell: /opt/zimbra/bin/zmprov -l gaaa -v zimbraIsDelegatedAdminAccount
and after this task I got output
changed: [Shrrah] => {
"changed": true,
"cmd": [
"sh",
"/home/information_domain.sh"
],
"delta": "0:00:02.495922",
"end": "2022-03-29 10:25:16.936051",
"invocation": {
"module_args": {
"_raw_params": "sh /home/information_domain.sh",
"_uses_shell": false,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": false
}
},
"msg": "",
"rc": 0,
"start": "2022-03-29 10:25:14.440129",
"stderr": "",
"stderr_lines": [],
"stdout": "# name admin#shrrah.esquimail.com\nzimbraIsDelegatedAdminAccount: FALSE\n\n# name prueba5#prueba5.com\n\n# name prueba7#prueba7.com\nzimbraIsDelegatedAdminAccount: TRUE\n\n# name prueba9#prueba9.com",
"stdout_lines": [
"# name admin#shrrah.esquimail.com",
"zimbraIsDelegatedAdminAccount: FALSE",
"",
"# name prueba5#prueba5.com",
"",
"# name prueba7#prueba7.com",
"zimbraIsDelegatedAdminAccount: TRUE",
"",
"# name prueba9#prueba9.com"
]
}
I need to get data with n# name prueba7#prueba7.com\nzimbraIsDelegatedAdminAccount: TRUE from "stdout" or from "stdout_lines" in format:
prueba7#prueba7.com zimbraIsDelegatedAdminAccount: TRUE
or
prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: TRUE
and send it to the file.txt. Number of lines can be different (one o more users with domain).
I have no idea how I can do this, is this possible? If you know could you please help with advice? Thank you!
You may have a look into debug – Print statements during execution, Using Variables and Return Values.
---
- hosts: localhost
become: true
gather_facts: false
vars:
RESULT:
STDOUT_LINES:
- "# name admin#shrrah.esquimail.com"
- "zimbraIsDelegatedAdminAccount: FALSE"
- ""
- "# name prueba5#prueba5.com"
- ""
- "# name prueba7#prueba7.com"
- "zimbraIsDelegatedAdminAccount: TRUE"
- ""
- "# name prueba9#prueba9.com"
tasks:
- name: Show STDOUT_LINES
debug:
msg: "{{ RESULT.STDOUT_LINES }}"
resulting into an output only of
TASK [Show STDOUT_LINES] *****************
ok: [localhost] =>
msg:
- '# name admin#shrrah.esquimail.com'
- 'zimbraIsDelegatedAdminAccount: FALSE'
- ''
- '# name prueba5#prueba5.com'
- ''
- '# name prueba7#prueba7.com'
- 'zimbraIsDelegatedAdminAccount: TRUE'
- ''
- '# name prueba9#prueba9.com'
and if Ansible Callback plugin is configured to YAML instead of JSON.
To get lines containing certain strings only you may Loop over the list based on a Condition
- name: Show lines with TRUE only
debug:
msg: "{{ item }}"
when: "'TRUE' in item"
loop: "{{ RESULT.STDOUT_LINES }}"
resulting into an output of
TASK [Show lines with TRUE only] *******************************
ok: [localhost] => (item=zimbraIsDelegatedAdminAccount: TRUE) =>
msg: 'zimbraIsDelegatedAdminAccount: TRUE'
Further Documenation
Index of all Callback Plugins
If you like to have the line before included, you could use an approach like
- name: Show lines with TRUE and line before
debug:
msg: "{{ RESULT.STDOUT_LINES[ansible_loop.index0 - 1] }}\n{{ item }}"
when: "'TRUE' in item"
loop: "{{ RESULT.STDOUT_LINES }}"
loop_control:
extended: true
label: "{{ ansible_loop.index0 }}"
resulting into an output of
TASK [Show lines with TRUE and line before] *************************************************************************************************************************************
ok: [localhost] => (item=6) =>
msg: |-
# name prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: TRUE
Further Documentation
Extended loop variables
Since you are using the shell module, you could use also an approach like
- name: DELEGATED ADMIN ACCOUNTS - check, get and send to the file domain.list
shell:
cmd: /opt/zimbra/bin/zmprov -l gaaa -v zimbraIsDelegatedAdminAccount | grep -B 1 TRUE
and gather only result lines which are true an the line before.
Further Q&A
grep a file, but show several surrounding lines?
Regarding
... send it to the file.txt
you may have a look into
Ansible - Save registered variable to file
Ansible: Save registered variables to file
...
Create a dictionary
- set_fact:
info: "{{ info|d({})|combine({_key: _val}) }}"
loop: "{{ stdout.split('#')[1:] }}"
vars:
_list: "{{ item.split('\n')|map('trim') }}"
_key: "{{ _list.0.split(' ')|last }}"
_val: "{{ _list[1:]|select()|map('from_yaml')|combine }}"
gives
info:
admin#shrrah.esquimail.com:
zimbraIsDelegatedAdminAccount: false
prueba5#prueba5.com: {}
prueba7#prueba7.com:
zimbraIsDelegatedAdminAccount: true
prueba9#prueba9.com: {}
Then, the template is trivial. Either print all items
- copy:
content: |-
{% for k,v in info.items() %}
{{ k }}
{{ v|to_nice_yaml }}
{% endfor %}
dest: file.txt
gives
shell> cat file.txt
admin#shrrah.esquimail.com
zimbraIsDelegatedAdminAccount: false
prueba5#prueba5.com
{}
prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: true
prueba9#prueba9.com
{}
, or explicitly select item(s)
- copy:
content: |-
prueba7#prueba7.com
{{ info['prueba7#prueba7.com']|to_nice_yaml }}
dest: file.txt
gives
shell> cat file.txt
prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: true
Note
Additional attributes will be parsed too, e.g.
stdout_lines: [
"# name admin#shrrah.esquimail.com",
"zimbraIsDelegatedAdminAccount: FALSE",
"",
"# name prueba5#prueba5.com",
"",
"# name prueba7#prueba7.com",
"zimbraIsDelegatedAdminAccount: TRUE",
"zimbraIsDelegatedRootAccount: TRUE",
"",
"# name prueba9#prueba9.com"
]
will give
info:
admin#shrrah.esquimail.com:
zimbraIsDelegatedAdminAccount: false
prueba5#prueba5.com: {}
prueba7#prueba7.com:
zimbraIsDelegatedAdminAccount: true
zimbraIsDelegatedRootAccount: true
prueba9#prueba9.com: {}
and consequently
shell> cat file.txt
prueba7#prueba7.com
zimbraIsDelegatedAdminAccount: true
zimbraIsDelegatedRootAccount: true
Hello guys I have a Problem.
The Problem I am having at the moment, is that the role to copy the files will skip all the files no matter if the file with the filenames is empty or not.
In Role1 I want to save the output of cat for each file. In Role2 in the when conditional, I want the task to skip if the registered output is == "".
Role1:
---
- name: copy files
shell: "cat path{{ item }}files"
register: checkempty
loop:
- test1
- test2
- test3
- test4
Role2:
---
- name: Copy Files
copy:
src: "{{ var1 }}{{ var2 }}{{ var3 }}{{ var4 }}{{ item }}/"
dest: "{{ copy_dest_sys }}" #destination path
loop: "{{ lookup('file', 'pathtofile/file').split('\n')}}"
when: hostvars['localhost'].checkempty.results == ""
Playbook:
- name: check emptiness
hosts: localhost
become: yes
vars_files:
- ../variables/varsfile
roles:
- ../variables/role1
- name: Copy Files to prod/stag
hosts: "{{hosts_exec}}"
become: yes
vars_files:
- ../vars/recommendation-delta.yml
roles:
- ../roles/role2
How can I set a registered variable with with_items and compare the output of it to ""(nothing)?
Can somebody help me with this issue?
Problem of copying whole dir is occurring as {{item}} is Empty in case of your file holding Filename is empty. It is considering src as
src: "{{ git_dest }}{{ git_recoprop_files }}"
in spite of
src: "{{ git_dest }}{{ git_recoprop_files }}{{ item }}/"
because {{item}} is Empty. I am not sure if it is compulsory for you to use first role to check if file is empty or not. if is not compulsory then you can update your 2nd role to
when: item != ""
In addition to that -
checkempty.results == ""
is also wrong. no matter if file is empty or not, this will be having an array of dictionaries for result of each loop item. And dictionaries are having kay value pair of a lot of things like item, stdout etc.
Sample :-
{
"_ansible_ignore_errors": null,
"_ansible_item_label": "inventory1",
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": true,
"cmd": "cat /home/rohit/ansible/inventory1",
"delta": "0:00:00.004505",
"end": "2019-04-21 21:13:55.042776",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "cat /home/rohit/ansible/inventory1",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": "inventory1",
"rc": 0,
"start": "2019-04-21 21:13:55.038271",
"stderr": "",
"stderr_lines": [],
"stdout": "inventory\nplaybook.yml",
"stdout_lines": [
"inventory",
"playbook.yml"
]
}
]
I think the main problem is you need to use stdout instead of results on your checkempty variable like this:
when: hostvars['localhost'].checkempty.stdout != ""
It seems like you have your logic backwards too, I think you want to use != instead of ==
I'd suggest that you refactor a bit and simplify your code by using stdout_lines in your loop like this:
- name: Copy sys Files to prod/stag
copy:
src: "{{ git_dest }}{{ git_sys_files }}{{ item }}/"
dest: "{{ copy_dest_sys }}" #destination path
loop: "{{ hostvars['localhost'].checkempty.stdout_lines }}"
when: hostvars['localhost'].checkempty.stdout != ""
stdout_lines will give you an array of the lines in the file that you cat'ed and you don't have to use your lookup and split code.
I got my playbook from which I get value what I need through debug method, However I am not able to get exact value of child of my debug var
Below is my playbook
- hosts: db
tasks:
- name: Checking if For Page Life Expectancy.
win_command: sqlcmd -q "SELECT [object_name],[counter_name],[cntr_value] FROM sys.dm_os_performance_counters WHERE [object_name] LIKE '%Manager%'AND [counter_name] = 'Page life expectancy'"
register: win_command_result
- debug:
var: win_command_result.stdout_lines.object_name
And We get output like this
TASK [debug]
ok: [db1.local] => {
"win_command_result": {
"changed": true,
"cmd": "sqlcmd -q \"SELECT [object_name],[counter_name],
[cntr_value] FROM sys.dm_os_performance_counters WHERE [object_name]
LIKE '%Manager%'AND [counter_name] = 'Page life expectancy'\"",
"delta": "0:00:01.099974",
"end": "2018-09-11 05:08:36.022907",
"failed": false,
"rc": 0,
"start": "2018-09-11 05:08:34.922933",
"stderr": "",
"stderr_lines": [],
"stdout": "object_name
counter_name
cntr_value \r\n---------------------------
----------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- --------------------\r\nSQLServer:Buffer Manager
Page life expectancy
238579\r\n\r\n(1 rows affected)\r\n",
"stdout_lines": [
"object_name
counter_name
cntr_value ",
"---------------------------------------------------------
----------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- --------------------",
"SQLServer:Buffer Manager
Page life expectancy
238579",
"",
"(1 rows affected)"
]
}
}
We only need value of cntr_value which is 238579
If i set as I though that cntr_value is child of stdout_lines
- debug:
var: win_command_result.stdout_lines.cntr_value
It says
ok: [db1.local] => {
"win_command_result.stdout_lines.cntr_value": "VARIABLE IS NOT DEFINED!"
}
How do I extract exact value of cntr_value
Your output final variable is:
myvar.win_command_result.stdout
So if you want to extract the number, do a regex:
- name: Debug
debug:
msg: "{{ myvar.win_command_result.stdout | regex_search('\\d{6}')}}"
Another option:
- name: Fact
set_fact:
mylist: "{{ myvar.win_command_result.stdout_lines | list }}"
- name: Debug
debug:
msg: "{{ item | regex_search('\\d+') }}"
with_items: "{{ mylist[2] }}"
I was given a task to verify some routing entries for all Linux server and here is how I did it using an Ansible playbook
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
You can see I have to repeat same task for each routing entry and I believe I should be able to avoid this. I tried use with_items loop but got following error message
One or more undefined variables: 'dict object' has no attribute 'stdout'
is there a way to register variable for each command and loop over them one by one ?
Starting in Ansible 1.6.1, the results registered with multiple items are stored in result.results as an array. So you can use result.results[0].stdout and so on.
Testing playbook:
---
- hosts: localhost
gather_facts: no
tasks:
- command: "echo {{item}}"
register: result
with_items: [1, 2]
- debug:
var: result
Result:
$ ansible-playbook -i localhost, test.yml
PLAY [localhost] **************************************************************
TASK: [command echo {{item}}] *************************************************
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)
TASK: [debug ] ****************************************************************
ok: [localhost] => {
"var": {
"result": {
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": [
"echo",
"1"
],
"delta": "0:00:00.002502",
"end": "2015-08-07 16:44:08.901313",
"invocation": {
"module_args": "echo 1",
"module_name": "command"
},
"item": 1,
"rc": 0,
"start": "2015-08-07 16:44:08.898811",
"stderr": "",
"stdout": "1",
"stdout_lines": [
"1"
],
"warnings": []
},
{
"changed": true,
"cmd": [
"echo",
"2"
],
"delta": "0:00:00.002516",
"end": "2015-08-07 16:44:09.038458",
"invocation": {
"module_args": "echo 2",
"module_name": "command"
},
"item": 2,
"rc": 0,
"start": "2015-08-07 16:44:09.035942",
"stderr": "",
"stdout": "2",
"stdout_lines": [
"2"
],
"warnings": []
}
]
}
}
}
PLAY RECAP ********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
A slightly different situation, which took a while to figure out. If you want to use the results of multiple items, but for changed_when, then the register variable will not have a var.results! Instead, changed_when, is evaluated for each item, and you can just directly use the register var.
Simple example, which will result in changed: false:
- action: command echo {{item}}
register: out
changed_when: "'z' in out.stdout"
with_items:
- hello
- foo
- bye
Another example:
- name: Create fulltext index for faster text searches.
mysql_db: name={{SO_database}} state=import target=/tmp/fulltext-{{item.tableName}}-{{item.columnName}}.sql
with_items:
- {tableName: Posts, columnName: Title}
- {tableName: Posts, columnName: Body}
- {tableName: Posts, columnName: Tags}
- {tableName: Comments, columnName: Text}
register: createfulltextcmd
changed_when: createindexcmd.msg.find('already exists') == -1
Finally, when you do want to loop through results in other contexts, it does seem a bit tricky to programmatically access the index as that is not exposed. I did find this one example that might be promising:
- name: add hosts to known_hosts
shell: 'ssh-keyscan -H {{item.host}}>> /home/testuser/known_hosts'
with_items:
- { index: 0, host: testhost1.test.dom }
- { index: 1, host: testhost2.test.dom }
- { index: 2, host: 192.168.202.100 }
when: ssh_known_hosts.results[{{item.index}}].rc == 1
Posting because I can't comment yet
Relating to gameweld's answer, since Ansible 2.5 there's another way to accessing the iteration index.
From the docs:
Tracking progress through a loop with index_var
New in version 2.5.
To keep track of where you are in a loop, use the index_var directive
with loop_control. This directive specifies a variable name to contain
the current loop index:
- name: count our fruit
debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
This also allows you to gather results from an array and act later to the same array, taking into account the previous results
- name: Ensure directories exist
file:
path: "{{ item }}"
state: directory
loop:
- "mouse"
- "lizard"
register: reg
- name: Do something only if directory is new
debug:
msg: "New dir created with name '{{ item }}'"
loop:
- "mouse"
- "lizard"
loop_control:
index_var: index
when: reg.results[index].changed
Please note that the "mouse lizard" array should be exactly the same
If what you need is to register the output of two commands separately, use different variable names.
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result0
changed_when: false
- debug: msg="{{result0.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result1
changed_when: false
- debug: msg="{{result1.stdout}}"