Set registered variable in loop an use it in another role - ansible

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.

Related

Get exactly data from "stdout" or "stdout_lines" with exact words of output

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

Ansible How to get the windows update kb number

I'm trying to get the pending installed windows update kb using ansible.
- name: Check for missing updates.
win_updates:
state: searched
category_names: "{{ win_updates_categories }}"
register: update_count
ignore_errors: yes
- debug: msg="{{ update_count.updates.kb }}"
but runs in error, could anyone help me, thank you !
Here is the output for register updatte
- debug:
var: update_count
"update_count": {
"changed": false,
"failed": false,
"filtered_updates": {},
"found_update_count": 1,
"installed_update_count": 0,
"reboot_required": false,
"updates": {
"67eab6a6-099b-42c5-86ce-63681f58ebd2": {
"categories": [
"Security Updates",
"Windows Server 2016"
],
"id": "67eab6a6-099b-42c5-86ce-63681f58ebd2",
"installed": false,
"kb": [
"4593226"
],
}
}
}
}
Here is the error if i only want show kb info
- debug:
var: update_count.updates.kb
"update_count.updates.kb": "VARIABLE IS NOT DEFINED!"
Please see below, I used the {{ item.value.id }} to get the ID value (last task) and saving them in an array if they are more than one, same way you should be able to retrieve the KBs. hopefully this explains
install critical updates only - reboot if nece
- name: install category critical updates only
win_updates:
category_names: ['CriticalUpdates']
server_selection: windows_update
state: installed
ignore_errors: yes
register: CriticalUpdateResults
- name: Postgres Column Values assigned
set_fact: UpdateCategory="{{ CriticalUpdateResults }}"
- name: CriticalUpdates Category assigned
set_fact: PatchCategory="CriticalUpdates"
- name: UpdatesArray assigned space
set_fact:
UpdatesArray: ""
- name: UpdatesArray assigned None if no update count
set_fact:
UpdatesArray: "None"
when: UpdateCategory.found_update_count == 0
- name: Updates Array assigned
set_fact:
UpdatesArray: "{{ UpdatesArray }} {{ **item.value.id** }}"
with_items:
- "{{ UpdateCategory.updates | dict2items }}"
when: UpdateCategory.found_update_count != 0

Find filename in Ansible and Save to a file

How can I take filename from a specific directory and use it as a variable?
Then I'll use this variable to copy a new file with variable filename
For example: In the directory /home/user1/test/ there is always only one file named test1 or some other name.
I need to extract the filename (test1) from this path to some variable
- hosts: linux
become: yes
tasks:
- name: Ansible find file examples
find:
paths: /home/user1/test/
register: files_matched
- debug:
msg: "{{ files_matched.files }}"
- name: "Save find results to file"
copy:
content: "{{ files_matched.files }}"
dest: "/tmp/find_result.txt"
Then I've should get "test1" as variable and use it in the new filename in this code:
copy:
src: /home/myuser/myfile
dest: "{{ item.dest }}"
owner: root
group: root
mode: 0666
with_items:
- { dest: '/home/user2/test/{{ files_matched }}' }
As result of first script i've got:
: 0, "ischr": false, "wusr": true, "xoth": false, "islnk": false, "nlink": 1, "issock": false, "rgrp": true, "gr_name": "root", "path": "/home/user1/test/test1", "xusr":
false, "atime": 1564553299.6092095, "isdir": false, "ctime": 1564553304.7172158, "isblk": false, "xgrp": false, "dev": 2050, "wgrp": false, "isfifo": false, "mode": "0644
", "rusr": true}]
But i need only test1 part as result, not this all.
Thank you!
Q: "How can I take filename from a specific directory and use it as a variable?'
A: Given the tree
$ tree /home/user1/test/
/home/user1/test/
├── test1
├── test2
└── test3
the tasks below
- find:
paths: /home/user1/test/
register: files_matched
- debug:
msg: "{{ '/home/user2/test/' ~ item|basename }}"
loop: "{{ files_matched.files|json_query('[*].path') }}
give
"msg": "/home/user2/test/test1"
"msg": "/home/user2/test/test3"
"msg": "/home/user2/test/test2"
I hope you are expecting something like this. this will save the list of files under the specified directory into the /tmp/find_result.txt. I hope from here you can proceed.
---
- name: find file
hosts: linux
tasks:
- name: Ansible find file examples
find:
paths: /home/user1/test/
register: files_matched
- name: "Save find results to file"
lineinfile:
line: "{{ item.path }}"
path: "/tmp/find_result.txt"
create: yes
loop: "{{ files_matched.files | flatten }}"
note: make sure that you delete the /tmp/find_result.txt once your task completed.
if you want to store only the filename not the entire path, then replace line: "{{ item.path }}" with line: "{{ item.path | basename }}"

Ansible: How to run command with shell output

Self explanatory. I want to link based on $(which {{ item }}).
Already saw the register function, but as I need to do a nested loop I'm not sure how to use it.
name: Link bins to user path
command: 'ln -s \$(which {{ item.1 }}) /home/{{ item.0 }}/bin/{{ item.1 }}'
with_nested:
- "{{ jail_users }}"
- "{{ jail_user_commands }}
Output:
failed: [rousertest] (item=[u'bob', u'date']) => {"changed": true,
"cmd": ["ln", "-s", "$(which", "date)", "/home/bob/bin/date"], "delta":
"0:00:00.011825", "end": "2019-07-11 08:17:32.921705", "item": ["bob", "date"], "msg": "non-zero return code", "rc": 1, "start": "2019-07-11
08:17:32.909880", "stderr": "ln: target ‘/home/bob/bin/date’ is not a
directory", "stderr_lines": ["ln: target ‘/home/bob/bin/date’ is not a
directory"], "stdout": "", "stdout_lines": []
Of course I was expecting something like this:
sudo ansible server -i inventory -m shell -a 'echo $(which date)'
rousertest | SUCCESS | rc=0 >>
/usr/bin/date
Below is the play. Avoid using command module for link. Use file module with state=link.
- name: Link binary
hosts: all
gather_facts: true
vars:
files:
- date
- ls
users:
- user1
- user2
tasks:
- name: Find paths
command: which {{ item }}
with_items:
- "{{ files }}"
register: result
- name: Link bins to user path
file:
src: "{{ item.1.stdout }}"
dest: "/home/{{ item.0 }}/bin/{{ item.1.item }}"
owner: "{{ item.0 }}"
group: "{{ item.0 }}"
state: link
with_nested:
- "{{ users }}"
- "{{ result.results }}"

Ansible, loop, register, and stdout

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 }}"

Resources