Ansible: loop our shell command(Linux user group search) result and display the groups - ansible

Ansible: loop our shell command(Linux user group search) result and display the groups
Task to check if group exist
- name: "Checking if group doesn't exist"
shell: "grep -i {{ item.group }} /etc/group"
register: presence
loop: "{{ UserAddList.add_users }}"
ignore_errors: true
no_log: true
Json input file:
UserAddList is a json file
{
"add_users": [
{
"name": "test1_123",
"group": "test1_123",
"additional_groups":
[
"test2",
"group1"
],
"password" : "test1_newcdsaf",
"sudo_entry": "ALL=(ALL) NOPASSWD: ALL",
"comment": "test1"
}
],
"delete_users": [
]
}
Task to display the groups doesn't exist
- name: The following groups does't' exist
debug:
msg:
"{{ item._ansible_item_label.group }}"
loop: "{{ presence.results }}"
output:
(item={
'_ansible_parsed': True,
'stderr_lines': [
],
u'changed': True,
u'stdout': u'',
'_ansible_item_result': True,
u'msg': u'non-zero return code',
u'delta': u'0:00:00.008175',
'stdout_lines': [
],
'_ansible_item_label': {
u'comment': u'test1',
u'password': u'test1_newcdsaf',
u'group': u'test1_123',
u'name': u'test1_123',
u'sudo_entry': u'ALL=(ALL) NOPASSWD: ALL',
u'additional_groups': [
u'test2',
u'group1'
]
},
u'end': u'2019-12-10 14:23:15.725676',
'_ansible_no_log': True,
'item': {
u'comment': u'test1',
u'password': u'test1_newcdsaf',
u'group': u'test1_123',
u'name': u'test1_123',
u'sudo_entry': u'ALL=(ALL) NOPASSWD: ALL',
u'additional_groups': [
u'test2',
u'group1'
]
},
u'cmd': u'grep -i test1_123 /etc/group',
u'failed': True,
u'stderr': u'',
u'rc': 1,
u'invocation': {
u'module_args': {
u'warn': True,
u'executable': None,
u'_uses_shell': True,
u'_raw_params': u'grep -i test1_123 /etc/group',
u'removes': None,
u'argv': None,
u'creates': None,
u'chdir': None,
u'stdin': None
}
},
u'start': u'2019-12-10 14:23:15.717501'
})=>{
"changed": false,
"item": {
"changed": true,
"cmd": "grep -i test1_123 /etc/group",
"delta": "0:00:00.008175",
"end": "2019-12-10 14:23:15.725676",
"failed": true,
"invocation": {
"module_args": {
"_raw_params": "grep -i test1_123 /etc/group",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"warn": true
}
},
"item": {
"additional_groups": [
"test2",
"group1"
],
"comment": "test1",
"group": "test1_123",
"name": "test1_123",
"password": "test1_newcdsaf",
"sudo_entry": "ALL=(ALL) NOPASSWD: ALL"
},
"msg": "non-zero return code",
"rc": 1,
"start": "2019-12-10 14:23:15.717501",
"stderr": "",
"stderr_lines": [
],
"stdout": "",
"stdout_lines": [
]
},
"msg": "test1_123"
}
I don't want to display the whole output, I just want to display the groups information.
The debug is printing all the input data as well.
Please any suggestions

The debug is printing all the input data as well.
It's actually not the debug: task that is printing your data, ansible is showing you what it is looping over. However, you are using loop: over the top-level presence.results list, and .results contains not only the output you care about, but also the invocation parameters, success or failure, and the actual returned data that you care about
There are two ways of fixing that problem: tell ansible that you only want it to show something smaller in the loop label, or change the loop: to actually only loop over the deleted users
In the first way, loop_control: will do that (it even cites your exact circumstance in the docs saying When looping over complex data structures, the console output of your task can be enormous. To limit the displayed output, use the label directive with loop_control):
- name: The following groups does't' exist
debug:
msg:
"{{ item._ansible_item_label.group }}"
loop: "{{ presence.results }}"
loop_control:
label: "{{ item.item.name }}"
In the second way, just select out the group you care about and msg: it:
- name: The following groups does't' exist
debug:
msg:
"{{ item }}"
loop: "{{ presence.results | map(attribute='item') | map(attribute='group') | list }}"

Related

Ansible - Find File with a Specific Path and Copy it to a Specific Pre-Set Destination from a Dictionary

I've got a problem that I've been trying to solve for a few days and I'm starting to think I'm just not going to get it. So, here I am.
I have to write an Ansible playbook that will look for a a bunch of files in a bunch of repos and, if it should happen to find that file in those repos, copy it to a specific destination directory which will be pre-set and specific to that file. A catch is that there might be multiple instances of that file but we only want that file if it's under a specific sub-directory in the path (the location of the sub-directory changes from repo to repo and the directory structure of the repos are all pretty different).
So that I don't put you all through our terrible naming conventions, I've abstracted the problem to this kinda silly example.
Let's say that this is our directory structure:
/tmp/example
└── foods
├── breakfast
│   ├── pancakes
│   └── waffles
├── dinner
│   ├── fish
│   └── pasta
└── lunch
├── burger
└── burrito
And this is our dictionary that states the user, the food they want, and the destination path of the food should we find it:
orders:
'Bob':
food: 'burrito'
dst: '/tmp/example-homes/home/bob/plate'
'Jack':
food: 'fish'
dst: '/tmp/example-homes/home/jack/plate'
'Mary':
food: 'fish'
dst: '/tmp/example-homes/mary/plate'
Now, let's say that we want to iterate through the list of people in the orders dictionary and find their food item in /tmp/example, BUT it should only match if their food choice is under the foods/lunch directory. If we happen to find their food in the foods/lunch directory, copy it to that user's specified dst directory (notice that not all of the dst directories are the same). Skip the food if it's found under foods/breakfast or foods/dinner or even something like restaurant/lunch; we only care about foods/lunch. For example, Mary wants fish, and fish does exist, but it's in the foods/dinner directory so we're going to consider it as missing and not copy it.
I've gotten to the point where I can find the food but I'm stuck trying to tie that found food file to the dst field that tells us where the food should go. It's frustrating because I feel that all of the data that I need is actually in the find_food_results dictionary. I just don't know how to act on the results of find so as to perform the logical equivalent of "if food matched, and path contains 'foods/lunch', copy it to item.value.dst".
I also can't help but feel that there's a simpler way to do this and I'm just chasing my tail at this point. Anyhow, thanks a bunch in advance. Please let me know if I can clarify anything.
Here's the code:
---
- name: "directory finder"
hosts: 127.0.0.1
connection: local
vars:
orders:
'Bob':
food: 'burrito'
dst: '/tmp/example-homes/home/bob/plate'
'Jack':
food: 'fish'
dst: '/tmp/example-homes/home/jack/plate'
'Mary':
food: 'fish'
dst: '/tmp/example-homes/mary/plate'
tasks:
- name: "describe orders"
debug:
var: orders
- name: "describe orders | dict2items"
debug:
var: orders | dict2items
- name: "find the food"
find:
paths: "/tmp/example"
recurse: yes
file_type: file
patterns: "{{ item.value.food }}"
with_items:
- "{{ orders | dict2items }}"
register: find_food_results
- name: "describe find_food_results"
debug:
var: find_food_results
- name: "narrow down our findings to paths that contain 'foods/lunch'"
set_fact:
food_lunch_directory_paths: "{{ food_lunch_directory_paths | default([]) }} + [ '{{ item.path }}' ]"
with_items: "{{ find_food_results.results | map(attribute='files') | list }}"
when: "'foods/lunch' in item.path"
- name: "describe our food/lunch paths"
debug:
var: food_lunch_directory_paths
And here's the output:
PLAY [directory finder] *******************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************
ok: [127.0.0.1]
TASK [describe orders] ********************************************************************************************************************************************
ok: [127.0.0.1] => {
"orders": {
"Bob": {
"dst": "/tmp/example-homes/home/bob/plate",
"food": "burrito"
},
"Jack": {
"dst": "/tmp/example-homes/home/jack/plate",
"food": "fish"
},
"Mary": {
"dst": "/tmp/example-homes/mary/plate",
"food": "fish"
}
}
}
TASK [describe orders | dict2items] *******************************************************************************************************************************
ok: [127.0.0.1] => {
"orders | dict2items": [
{
"key": "Bob",
"value": {
"dst": "/tmp/example-homes/home/bob/plate",
"food": "burrito"
}
},
{
"key": "Jack",
"value": {
"dst": "/tmp/example-homes/home/jack/plate",
"food": "fish"
}
},
{
"key": "Mary",
"value": {
"dst": "/tmp/example-homes/mary/plate",
"food": "fish"
}
}
]
}
TASK [find the food] **********************************************************************************************************************************************
ok: [127.0.0.1] => (item={u'key': u'Bob', u'value': {u'food': u'burrito', u'dst': u'/tmp/example-homes/home/bob/plate'}})
ok: [127.0.0.1] => (item={u'key': u'Jack', u'value': {u'food': u'fish', u'dst': u'/tmp/example-homes/home/jack/plate'}})
ok: [127.0.0.1] => (item={u'key': u'Mary', u'value': {u'food': u'fish', u'dst': u'/tmp/example-homes/mary/plate'}})
TASK [describe find_food_results] *********************************************************************************************************************************
ok: [127.0.0.1] => {
"find_food_results": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"examined": 10,
"failed": false,
"files": [
{
"atime": 1604184466.0584693,
"ctime": 1604184466.0584693,
"dev": 66305,
"gid": 0,
"gr_name": "root",
"inode": 58724223,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1604184466.0584693,
"nlink": 1,
"path": "/tmp/example/foods/lunch/burrito",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
"invocation": {
"module_args": {
"age": null,
"age_stamp": "mtime",
"contains": null,
"depth": null,
"excludes": null,
"file_type": "file",
"follow": false,
"get_checksum": false,
"hidden": false,
"paths": [
"/tmp/example"
],
"patterns": [
"burrito"
],
"recurse": true,
"size": null,
"use_regex": false
}
},
"item": {
"key": "Bob",
"value": {
"dst": "/tmp/example-homes/home/bob/plate",
"food": "burrito"
}
},
"matched": 1,
"msg": ""
},
{
"ansible_loop_var": "item",
"changed": false,
"examined": 10,
"failed": false,
"files": [
{
"atime": 1604184480.3713806,
"ctime": 1604184480.3713806,
"dev": 66305,
"gid": 0,
"gr_name": "root",
"inode": 62917805,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1604184480.3713806,
"nlink": 1,
"path": "/tmp/example/foods/dinner/fish",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
"invocation": {
"module_args": {
"age": null,
"age_stamp": "mtime",
"contains": null,
"depth": null,
"excludes": null,
"file_type": "file",
"follow": false,
"get_checksum": false,
"hidden": false,
"paths": [
"/tmp/example"
],
"patterns": [
"fish"
],
"recurse": true,
"size": null,
"use_regex": false
}
},
"item": {
"key": "Jack",
"value": {
"dst": "/tmp/example-homes/home/jack/plate",
"food": "fish"
}
},
"matched": 1,
"msg": ""
},
{
"ansible_loop_var": "item",
"changed": false,
"examined": 10,
"failed": false,
"files": [
{
"atime": 1604184480.3713806,
"ctime": 1604184480.3713806,
"dev": 66305,
"gid": 0,
"gr_name": "root",
"inode": 62917805,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1604184480.3713806,
"nlink": 1,
"path": "/tmp/example/foods/dinner/fish",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
"invocation": {
"module_args": {
"age": null,
"age_stamp": "mtime",
"contains": null,
"depth": null,
"excludes": null,
"file_type": "file",
"follow": false,
"get_checksum": false,
"hidden": false,
"paths": [
"/tmp/example"
],
"patterns": [
"fish"
],
"recurse": true,
"size": null,
"use_regex": false
}
},
"item": {
"key": "Mary",
"value": {
"dst": "/tmp/example-homes/mary/plate",
"food": "fish"
}
},
"matched": 1,
"msg": ""
}
]
}
}
TASK [narrow down our findings to paths that contain 'foods/lunch'] ***********************************************************************************************
ok: [127.0.0.1] => (item={u'rusr': True, u'uid': 0, u'rgrp': True, u'xoth': False, u'islnk': False, u'woth': False, u'nlink': 1, u'issock': False, u'mtime': 1604184466.0584693, u'gr_name': u'root', u'path': u'/tmp/example/foods/lunch/burrito', u'xusr': False, u'atime': 1604184466.0584693, u'inode': 58724223, u'isgid': False, u'size': 0, u'isdir': False, u'wgrp': False, u'ctime': 1604184466.0584693, u'isblk': False, u'xgrp': False, u'isuid': False, u'dev': 66305, u'roth': True, u'isreg': True, u'isfifo': False, u'mode': u'0644', u'pw_name': u'root', u'gid': 0, u'ischr': False, u'wusr': True})
skipping: [127.0.0.1] => (item={u'rusr': True, u'uid': 0, u'rgrp': True, u'xoth': False, u'islnk': False, u'woth': False, u'nlink': 1, u'issock': False, u'mtime': 1604184480.3713806, u'gr_name': u'root', u'path': u'/tmp/example/foods/dinner/fish', u'xusr': False, u'atime': 1604184480.3713806, u'inode': 62917805, u'isgid': False, u'size': 0, u'isdir': False, u'wgrp': False, u'ctime': 1604184480.3713806, u'isblk': False, u'xgrp': False, u'isuid': False, u'dev': 66305, u'roth': True, u'isreg': True, u'isfifo': False, u'mode': u'0644', u'pw_name': u'root', u'gid': 0, u'ischr': False, u'wusr': True})
skipping: [127.0.0.1] => (item={u'rusr': True, u'uid': 0, u'rgrp': True, u'xoth': False, u'islnk': False, u'woth': False, u'nlink': 1, u'issock': False, u'mtime': 1604184480.3713806, u'gr_name': u'root', u'path': u'/tmp/example/foods/dinner/fish', u'xusr': False, u'atime': 1604184480.3713806, u'inode': 62917805, u'isgid': False, u'size': 0, u'isdir': False, u'wgrp': False, u'ctime': 1604184480.3713806, u'isblk': False, u'xgrp': False, u'isuid': False, u'dev': 66305, u'roth': True, u'isreg': True, u'isfifo': False, u'mode': u'0644', u'pw_name': u'root', u'gid': 0, u'ischr': False, u'wusr': True})
TASK [describe our food/lunch paths] ******************************************************************************************************************************
ok: [127.0.0.1] => {
"food_lunch_directory_paths": [
"/tmp/example/foods/lunch/burrito"
]
}
PLAY RECAP ********************************************************************************************************************************************************
127.0.0.1 : ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Let's take into account missing files. For example, given the orders
orders:
'Bob':
food: 'burrito'
dst: '/tmp/example-homes/home/bob/plate'
'Jack':
food: 'fish'
dst: '/tmp/example-homes/home/jack/plate'
'Mary':
food: 'fish'
dst: '/tmp/example-homes/mary/plate'
'Joe':
food: 'steak'
dst: '/tmp/example-homes/joe/plate'
Create the list of the foods
- name: "create list of foods"
set_fact:
foods: "{{ orders|dict2items|
map(attribute='value.food')|unique|sort|list }}"
- debug:
var: foods
gives
foods:
- burrito
- fish
- steak
Find the files and create a dictionary of foods and related files. Then use the dictionary to copy the existing files to destinations
- name: "find the foods"
find:
paths: "/tmp/example"
recurse: yes
file_type: file
patterns: "{{ item }}"
loop: "{{ foods }}"
register: find_food_results
- name: "create dictionary of foods"
set_fact:
foods: "{{ dict(foods|zip(paths)) }}"
vars:
paths: "{{ find_food_results.results|
map(attribute='files')|list }}"
- name: "copy property files to destination"
debug:
msg: "Copy {{ foods[item.value.food][0]['path'] }} to {{ item.value.dst }}"
with_dict: "{{ orders }}"
loop_control:
label: "{{ item.key }}"
when: foods[item.value.food]|length > 0
give
ok: [localhost] => (item=Bob) =>
msg: Copy /tmp/example/foods/lunch/burrito to /tmp/example-homes/home/bob/plate
ok: [localhost] => (item=Jack) =>
msg: Copy /tmp/example/foods/dinner/fish to /tmp/example-homes/home/jack/plate
ok: [localhost] => (item=Mary) =>
msg: Copy /tmp/example/foods/dinner/fish to /tmp/example-homes/mary/plate
skipping: [localhost] => (item=Joe)
Q: "File 'burrito' in the breakfast, lunch, and dinner directories ... How can I iterate over ... find_food_results.files? Almost like a nested loop."
A: Display the dictionary foods with selected paths only. The task
- debug:
msg: "{{ msg.split('\n')[:-1] }}"
vars:
msg: |
{{ item.key }}
{{ item.value|map(attribute='path')|list|to_nice_yaml }}
loop: "{{ foods|dict2items }}"
loop_control:
label: "{{ item.key }}"
gives
TASK [debug] ****
ok: [localhost] => (item=burrito) =>
msg:
- burrito
- '- /tmp/example/foods/breakfast/burrito'
- '- /tmp/example/foods/dinner/burrito'
- '- /tmp/example/foods/lunch/burrito'
ok: [localhost] => (item=fish) =>
msg:
- fish
- '- /tmp/example/foods/dinner/fish'
ok: [localhost] => (item=steak) =>
msg:
- steak
- '[]'
Use subelements if you want to iterate the lists of the files. For example
- name: "List all property files"
debug:
msg: "{{ item.0.key }} {{ item.1.path }}"
with_subelements:
- "{{ foods|dict2items }}"
- value
loop_control:
label: "{{ item.0.key }}"
gives
TASK [List all property files] ****
ok: [localhost] => (item=burrito) =>
msg: burrito /tmp/example/foods/breakfast/burrito
ok: [localhost] => (item=burrito) =>
msg: burrito /tmp/example/foods/dinner/burrito
ok: [localhost] => (item=burrito) =>
msg: burrito /tmp/example/foods/lunch/burrito
ok: [localhost] => (item=fish) =>
msg: fish /tmp/example/foods/dinner/fish
And of course as soon as I ask the question, I find the answer to it :-/
Well, in case anyone else was struggling with this, here's how I fixed it. Though, I'd love to see more efficient solutions, so please do chime in if you have one.
I was able to create a new value in the dictionary called "src" that contained the path of the file found using the find module. Then, in the copy module, I was able to iterate over the dictionary and only copy when the src contained 'foods/lunch'.
Here's the code:
---
- name: "directory finder"
hosts: 127.0.0.1
connection: local
vars:
orders:
'Bob':
food: 'burrito'
dst: '/tmp/example-homes/home/bob/plate'
'Jack':
food: 'fish'
dst: '/tmp/example-homes/home/jack/plate'
'Mary':
food: 'fish'
dst: '/tmp/example-homes/mary/plate'
tasks:
- name: "describe orders"
debug:
var: orders
- name: "describe orders | dict2items"
debug:
var: orders | dict2items
- name: "find the food"
find:
paths: "/tmp/example"
recurse: yes
file_type: file
patterns: "{{ item.value.food }}"
with_items:
- "{{ orders | dict2items }}"
register: find_food_results
- name: "create a src key/value dictionary element"
set_fact:
orders: "{{ orders | combine( new_item, recursive=true ) }}"
vars:
new_item: "{ '{{ item.item.key }}': { 'src': '{{ item.files[0].path }}' } }"
with_items: "{{ find_food_results.results }}"
- name: "copy property files to destination"
copy:
src: "{{ item.value.src }}"
dest: "{{ item.value.dst }}"
owner: root
group: root
mode: 0644
with_dict: "{{ orders }}"
when: "'foods/lunch' in item.value.src"

Ansible: Register and display results through iteration

My playbook is
---
- name: Running Checks on ACTIVATION VMs of ECM
hosts: activationvms
tasks:
- name: Checking File System status
shell: df -PTh
register: filesystem_check
- name: Display File System Statistics
debug:
msg: "{{ filesystem_check.stdout_lines }}"
- name: Check the status of list of services
shell: systemctl list-units|grep "{{ item }}"|awk '{ print $1,$2,$3,$4}'
with_items:
- zookeeper.service
- cassandra.service
register: service_status
- name: Display services status
debug:
msg: "{{ item.stdout }}"
with_items: "{{ service_status.results }}"
I want to shrink the results to only the msg line.
ok: [10.142.6.79] => (item={u'_ansible_parsed': True, u'changed': True, u'stdout': u'cassandra.service loaded active running', u'_ansible_no_log': False, u'stdout_lines': [u'cassandra.service loaded active running'], u'warnings': [], u'_ansible_item_result': True, u'start': u'2019-11-15 07:50:39.047456', u'delta': u'0:00:00.011821', u'cmd': u'systemctl list-units|grep "cassandra.service"|awk \'{ print $1,$2,$3,$4}\'', u'item': u'cassandra.service', u'rc': 0, u'invocation': {u'module_name': u'command', u'module_args': {u'warn': True, u'executable': None, u'_uses_shell': True, u'_raw_params': u'systemctl list-units|grep "cassandra.service"|awk \'{ print $1,$2,$3,$4}\'', u'removes': None, u'creates': None, u'chdir': None}}, u'end': u'2019-11-15 07:50:39.059277', u'stderr': u''}) => {
"item": {
"changed": true,
"cmd": "systemctl list-units|grep \"cassandra.service\"|awk '{ print $1,$2,$3,$4}'",
"delta": "0:00:00.011821",
"end": "2019-11-15 07:50:39.059277",
"invocation": {
"module_args": {
"_raw_params": "systemctl list-units|grep \"cassandra.service\"|awk '{ print $1,$2,$3,$4}'",
"_uses_shell": true,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"warn": true
},
"module_name": "command"
},
"item": "cassandra.service",
"rc": 0,
"start": "2019-11-15 07:50:39.047456",
"stderr": "",
"stdout": "cassandra.service loaded active running",
"stdout_lines": [
"cassandra.service loaded active running"
],
"warnings": []
},
"msg": "cassandra.service loaded active running"
}
ok: [10.142.6.72] => (item={u'_ansible_parsed': True, u'changed': True, u'stdout': u'cassandra.service loaded active running', u'_ansible_no_log': False, u'stdout_lines': [u'cassandra.service loaded active running'], u'warnings': [], u'_ansible_item_result': True, u'start': u'2019-11-15 07:50:39.429296', u'delta': u'0:00:00.011853', u'cmd': u'systemctl list-units|grep "cassandra.service"|awk \'{ print $1,$2,$3,$4}\'', u'item': u'cassandra.service', u'rc': 0, u'invocation': {u'module_name': u'command', u'module_args': {u'warn': True, u'executable': None, u'_uses_shell': True, u'_raw_params': u'systemctl list-units|grep "cassandra.service"|awk \'{ print $1,$2,$3,$4}\'', u'removes': None, u'creates': None, u'chdir': None}}, u'end': u'2019-11-15 07:50:39.441149', u'stderr': u''}) => {
"item": {
"changed": true,
"cmd": "systemctl list-units|grep \"cassandra.service\"|awk '{ print $1,$2,$3,$4}'",
"delta": "0:00:00.011853",
"end": "2019-11-15 07:50:39.441149",
"invocation": {
"module_args": {
"_raw_params": "systemctl list-units|grep \"cassandra.service\"|awk '{ print $1,$2,$3,$4}'",
"_uses_shell": true,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"warn": true
},
"module_name": "command"
},
"item": "cassandra.service",
"rc": 0,
"start": "2019-11-15 07:50:39.429296",
"stderr": "",
"stdout": "cassandra.service loaded active running",
"stdout_lines": [
"cassandra.service loaded active running"
],
"warnings": []
},
"msg": "cassandra.service loaded active running"
You need to use loop control tool. Rewrite your last play like this
- name: Display services status
debug:
msg: "{{ item.stdout }}"
with_items: "{{ service_status.results }}"
loop_control:
label: "{{ item.item }}"
That will limit that output to just the thing that was iterated into your prior command so you can tell which part of the loop you are currently in.

Debug not working with_items in ansible play

Need some help in debug the specific value from register task.
- debug:
msg: "{{ item.stdout }}"
with_items: path_result.results
This is not working
{
"changed": false,
"path_result": {
"msg": "All items completed",
"changed": true,
"results": [
{
"_ansible_parsed": true,
"stderr_lines": [],
"_ansible_item_result": true,
"end": "2019-04-10 14:55:18.726270",
"_ansible_no_log": false,
"_ansible_delegated_vars": {
"ansible_delegated_host": "**.***.***.***",
"ansible_host": "**.***.***.***"
},
"cmd": "cat /tmp/abc.conf | grep apple",
"rc": 0,
"stdout": "fruit=apple",
"item": "**.***.***.***",
"delta": "0:00:00.021499",
"stderr": "",
"changed": true,
"invocation": {
"module_args": {
"creates": null,
"executable": null,
"_uses_shell": true,
"_raw_params": "cat /tmp/abc.conf | grep apple",
"removes": null,
"warn": true,
"chdir": null,
"stdin": null
}
},
"stdout_lines": [
"fruit=apple"
],
"start": "2019-04-10 14:55:18.704771",
"_ansible_ignore_errors": null,
"failed": false
}
]
},
"_ansible_verbose_always": true,
"_ansible_no_log": false
}
I would like to debug this particular output:
"stdout": "fruit=apple"
It looks like you have registered the result of a play with: register: path_result. That variable ALSO has path_result inside it. Here's what I think you have:
- command: cat /tmp/abc.conf | grep apple
register: path_result
- debug:
msg: "{{ item.stdout }}"
with_items: "{{ path_result.results }}"
Here's what I think you need:
- command: cat /tmp/abc.conf | grep apple
register: path_result
- debug:
msg: "{{ item.stdout }}"
with_items: "{{ path_result.path_result.results }}"
Notice the added .path_result part in with_items.

Locate string/integer not in list

Im pulling a list of VLANs from our IPAM via an API and I want to be able to locate an unused "vlanId" that isnt in the list. I was expecting that I could use with_items for the JSON content and then use the random function with an until loop and occasionally it will initially generate a number that doesnt exist in the list. Mostly it just gets stuck and doesnt generate a new random number when the one generated already exists.
Playbook:
- uri:
url: "#"
validate_certs: no
headers:
token: "{{ token }}"
method: GET
force_basic_auth: yes
return_content: yes
register: ipam
- set_fact:
value: "{{ 4094 | random(start=1) }}"
until: value not in item.vlanId
with_items: "{{ ipam.json.data }}"
retries: 4093
- debug: msg="{{ value }}"
Relevant Output:
ok: [localhost] => (item={u'domainId': u'3', u'description': u'#', u'editDate': None, u'Customer ID': None, u'number': u'2241', u'vlanId': u'548', u'name': u'2241', u'Customer Name': None, u'custom_fields': None, u'Engineer': None}) => {
"ansible_facts": {
"value": "2727"
},
"ansible_facts_cacheable": false,
"attempts": 1,
"changed": false,
"item": {
"Customer ID": null,
"Customer Name": null,
"Engineer": null,
"custom_fields": null,
"description": "#",
"domainId": "3",
"editDate": null,
"name": "2241",
"number": "2241",
"vlanId": "548"
}
}
ok: [localhost] => (item={u'domainId': u'3', u'description': u'#', u'editDate': None, u'Customer ID': None, u'number': u'2242', u'vlanId': u'549', u'name': u'2242', u'Customer Name': None, u'custom_fields': None, u'Engineer': None}) => {
"ansible_facts": {
"value": "1955"
},
"ansible_facts_cacheable": false,
"attempts": 1,
"changed": false,
"item": {
"Customer ID": null,
"Customer Name": null,
"Engineer": null,
"custom_fields": null,
"description": "#",
"domainId": "3",
"editDate": null,
"name": "2242",
"number": "2242",
"vlanId": "549"
}
}
FAILED - RETRYING: set_fact (4000 retries left).Result was: {
"ansible_facts": {
"value": "50"
},
"ansible_facts_cacheable": false,
"attempts": 1,
"changed": false,
"retries": 4001
}
FAILED - RETRYING: set_fact (3999 retries left).Result was: {
"ansible_facts": {
"value": "50"
},
"ansible_facts_cacheable": false,
"attempts": 2,
"changed": false,
"retries": 4001
}
FAILED - RETRYING: set_fact (3998 retries left).Result was: {
"ansible_facts": {
"value": "50"
},
"ansible_facts_cacheable": false,
"attempts": 3,
"changed": false,
"retries": 4001
Im currently using ansible 2.4.2.0
If this is something that cant/shouldnt be done in Ansible, any guidance would be appreciated.
Here's what you want:
- debug:
msg: "{{ range(1, 4095) | difference(vlanIds) | random }}"
vars:
vlanIds: "{{ ipam.json.data | map(attribute='vlanId') | list }}"
Explanation:
map(attribute=... - create a list of vlan IDs,
range - generate a sequence from 1 to 4094,
difference - select all the elements from the above. that are not on the vlanIds list,
random - select a random element from the above.
For strings (as the title suggests) replace range with a list of possible strings.

Ansible register check_path and used it in with_dict loop

I have a following hash/dict structure of sites:
sites:
example.com:
site: example.com
mail: info#example.com
site_alias: www.example.com
example.fi:
site: example.fi
mail: info#example.fi
site_alias: example.fi
...
I register a value for every site, if there is also a folder for it. Print the result.
name: "Check if path already exists so it is the first time."
stat: path={{ cert_files }}/{{ item.value.site }}
register: check_path
with_dict: "{{ sites }}"
debug: var=check_path.results
# No need to print the whole dictionary, all results are already there.
# with_dict: "{{ sites }}"
So I get something like:
TASK [letsencrypt : debug] *****************************************************
ok: [78.47.67.114] => (item={'key': u'example.com', 'value': {u'mail': u'mail#example.com', u'site_alias': u'www.example.com', u'site': u'example.com'}}) => {
"check_path.results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_checksum": true,
"get_md5": true,
"mime": false,
"path": "/etc/letsencrypt/live/example.com"
},
"module_name": "stat"
},
"item": {
"key": "example.com",
"value": {
"mail": "info#example.com",
"site": "example.com",
"site_alias": "www.example.com"
}
},
"stat": {
"atime": 147869032.3522692,
"ctime": 149636484.0226028,
"dev": 2049,
"executable": true,
"exists": true,
"gid": 0,
"gr_name": "root",
"inode": 15725,
"isblk": false,
"ischr": false,
"isdir": true,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": false,
"issock": false,
"isuid": false,
"mode": "0755",
"mtime": 14632684.026028,
"nlink": 2,
"path": "/etc/letsencrypt/live/example.com",
"pw_name": "root",
"readable": true,
"rgrp": true,
"roth": true,
"rusr": true,
"size": 4096,
"uid": 0,
"wgrp": false,
"woth": false,
"writeable": true,
"wusr": true,
"xgrp": true,
"xoth": true,
"xusr": true
}
},
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_checksum": true,
"get_md5": true,
"mime": false,
"path": "/etc/letsencrypt/live/example.com"
},
"module_name": "stat"
},
"item": {
"key": "example.fi",
"value": {
"mail": "info#example.fi",
"site": "example.fi",
"site_alias": "www.example.fi"
}
},
"stat": {
"atime": 1493734857.9738503,
"ctime": 1485960159.8090317,
"dev": 2049,
"executable": true,
"exists": true,
How can I the use or get the value "check_path.results.stats.exists" the last value in the next task if I want to iterate again through {{ sites }} ?
I have tried something like this with no success.
- name: Make a certificate the first time.
command: /bin/bash /opt/letsencrypt/letsencrypt-auto certonly -- standalone --email "{{ item.value.mail }}" --agree-tos --keep-until- expiring -d "{{ item.value.site }}" -d "{{ item.value.site_alias }}"
with_items: check_path
when: check_path.results.stat.exists == false
or
- name: Make a certificate the first time.
command: /bin/bash /opt/letsencrypt/letsencrypt-auto certonly standalone --email "{{ item.value.mail }}" --agree-tos --keep-until-expiring -d "{{ item.value.site }}" -d "{{ item.value.site_alias }}"
with_dict: "{{ sites }}"
when: check_path.results.stat.exists == false
You should iterate over result, not over original list:
- name: Make a certificate the first time.
command: /bin/bash /opt/letsencrypt/letsencrypt-auto certonly standalone --email "{{ item.item.value.mail }}" --agree-tos --keep-until-expiring -d "{{ item.item.value.site }}" -d "{{ item.item.value.site_alias }}"
with_items: "{{ check_path.results }}"
when: not item.stat.exists
here item.item is an item of the original list.

Resources