Extract exact value from stdout_lines from Ansible debug - ansible

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

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

Remove backslash in string with ansible

Im struggling with removing backslash in a string like this :
- name: Set var
set_fact:
var: "{{ _var.stdout_lines[0].replace('\\\\', ' ').split(' ')[1] }}"
where _var is :
"_var": {
"changed": true,
"delta": "0:00:00.406263",
"end": "2020-10-20 09:03:36.332342",
"failed": false,
"rc": 0,
"start": "2020-10-20 09:03:35.926079",
"stderr": "",
"stderr_lines": [],
"stdout": "MYSERVER\\myuser\r\n",
"stdout_lines": [
"MYSERVER\\myuser"
]
}
As Im looking to catch only myuser as a result.
you can use regex_replace to extract the username from the above string of stdout_lines[0].
here is a task to do it:
---
- hosts: localhost
gather_facts: false
vars:
myvar: "SERVER\\myuser"
tasks:
- name: extract the username
debug:
msg: '{{ myvar | regex_replace("^(.+)([\\]+)(.+)$", "\3") }}'
The 2nd group in the regex was defined to match any number of backslashes.

Getting Ansible runtime error dict object' has no attribute 'stdout_lines' despite variable not null

Below is my playbook which has a variable running_processes which contains a list of pids(one or more)
Next, I read the user ids for each of the pids. All good so far.
I then try to print the list of user ids in curr_user_ids variable using -debug module is when i get the error: 'dict object' has no attribute 'stdout_lines'
I was expecting the curr_user_ids to contain one or more entries as evident from the output shared below.
- name: Get running processes list from remote host
shell: "ps -few | grep java | grep -v grep | awk '{print $2}'"
changed_when: false
register: running_processes
- name: Gather USER IDs from processes id before killing.
shell: "id -nu `cat /proc/{{ running_processes.stdout }}/loginuid`"
register: curr_user_ids
with_items: "{{ running_processes.stdout_lines }}"
- debug: msg="USER ID LIST HERE:{{ curr_user_ids.stdout }}"
with_items: "{{ curr_user_ids.stdout_lines }}"
TASK [Get running processes list from remote host] **********************************************************************************************************
task path: /app/wls/startstop.yml:22
ok: [10.9.9.111] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "cmd": "ps -few | grep java | grep -v grep | awk '{print $2}'", "delta": "0:00:00.166049", "end": "2019-11-06 11:49:42.298603", "rc": 0, "start": "2019-11-06 11:49:42.132554", "stderr": "", "stderr_lines": [], "stdout": "24032", "stdout_lines": ["24032"]}
TASK [Gather USER IDS of processes id before killing.] ******************************************************************************************************
task path: /app/wls/startstop.yml:59
changed: [10.9.9.111] => (item=24032) => {"ansible_loop_var": "item", "changed": true, "cmd": "id -nu `cat /proc/24032/loginuid`", "delta": "0:00:00.116639", "end": "2019-11-06 11:46:41.205843", "item": "24032", "rc": 0, "start": "2019-11-06 11:46:41.089204", "stderr": "", "stderr_lines": [], "stdout": "user1", "stdout_lines": ["user1"]}
TASK [debug] ************************************************************************************************************************************************
task path: /app/wls/startstop.yml:68
fatal: [10.9.9.111]: FAILED! => {"msg": "'dict object' has no attribute 'stdout_lines'"}
Can you please suggest why am I getting the error and how can I resolve it ?
Few points to note why your solution didn't work.
The task Get running processes list from remote host returns a newline splitted \n string. So you will need to process this and turn the output into a propper list object first.
The task Gather USER IDs from processes id before killing. is returning a dictionary containing the key results where the value is of type list, so you will need iterate over it and fetch for each element the stdout value.
This is how I solved it.
---
- hosts: "localhost"
gather_facts: true
become: true
tasks:
- name: Set default values
set_fact:
process_ids: []
user_names: []
- name: Get running processes list from remote host
shell: "ps -few | grep java | grep -v grep | awk '{print $2}'"
changed_when: false
register: running_processes
- name: Register a list of Process ids (Split newline from output before)
set_fact:
process_ids: "{{ running_processes.stdout.split('\n') }}"
- name: Gather USER IDs from processes id before killing.
shell: "id -nu `cat /proc/{{ item }}/loginuid`"
register: curr_user_ids
with_items: "{{ process_ids }}"
- name: Register a list of User names (Out of result from before)
set_fact:
user_names: "{{ user_names + [item.stdout] | unique }}"
when: item.rc == 0
with_items:
- "{{ curr_user_ids.results }}"
- name: Set unique entries in User names list
set_fact:
user_names: "{{ user_names | unique }}"
- name: DEBUG
debug:
msg: "{{ user_names }}"
The variable curr_user_ids registers results of each iteration
register: curr_user_ids
with_items: "{{ running_processes.stdout_lines }}"
The list of the results is stored in
curr_user_ids.results
Take a look at the variable
- debug:
var: curr_user_ids
and loop the stdout_lines
- debug:
var: item.stdout_lines
loop: "{{ curr_user_ids.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 }}"

Ansible register result of multiple commands

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

Resources