Ansible getent module's loop output results parsing - ansible

I am trying to check if a number of users are present or not in the managed node using getent module and create a list of users who are not present.
The piece of code is:
- getent:
database: passwd
key: "{{ item }}"
fail_key: no
register: x
loop:
- "user1"
- "user2"
- debug: var=x.results
- set_fact:
fail_list: "{{ x.results | }}"
I am stuck at this point.
Is there any way I can save the users who are not present to the variable fail_list as a list?
In the above example, user1 is not present and user2 is present in the managed node.
The ansible version I am using is 2.9 and the debug output is a list of dicts as below:
"x.results": [
{
"ansible_facts": {
"getent_passwd": {
"user1": null
}
},
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"database": "passwd",
"fail_key": false,
"key": "user1",
"service": null,
"split": null
}
},
"item": "user1",
"msg": "One or more supplied key could not be found in the database."
},
{
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python",
"getent_passwd": {
"user2": [
"x",
"0",
"0",
"user2",
"/home/user2",
"/bin/bash"
]
}
},
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"database": "passwd",
"fail_key": false,
"key": "user2",
"service": null,
"split": null
}
},
"item": "user2"
},

Run getent once and search the list of users. For example
- hosts: localhost
tasks:
- getent:
database: passwd
- debug:
msg: User {{ item }} exists.
loop:
- root
- user1
- user2
when: item in my_users
vars:
my_users: "{{ getent_passwd.keys()|list }}"
gives
ok: [localhost] => (item=root) => {
"msg": "User root exists."
}
skipping: [localhost] => (item=user1)
skipping: [localhost] => (item=user2)

Related

Ansible include task and loop register different result

I'm trying to use a module as an included task so I can loop several items and register the credentials in a variable.
Files:
main.yml:
---
- hosts: localhost
gather_facts: False
tasks:
- include: tasks/myvault/get-vault.yml
with_items:
- demo
register: output
- debug:
msg: "{{output}}"
get-vault.yml:
- name: Retrieve secret from Vault
community.hashi_vault.vault_kv2_get:
url: https://myvaul
path: "{{ item }}"
auth_method: token
token: '{{ mytoken }}'
namespace: MyNamespace
validate_certs: no
output:
ok: [localhost] => {
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"include": "tasks/myvault/get-vault.yml",
"include_args": {},
"item": "demo"
}
],
"skipped": false
}
}
The output doesn't display the "real" result of the task.
If I remove:
register: output and put it in get-vault.yml, here is the result:
ok: [localhost] => {
"msg": {
"changed": false,
"data": {
"data": {
"password": "Password",
"username": "Username"
}, ...
How can I get this result returned when I register the output from my included task?

Output of Ansibble task

I am using command hcloud to create cloud server in Hetzner. I get an output like this:
changed: [localhost] => (item={'name': 'TEST-VARIABLES', 'server_type': 'cx11', 'os_image': 'ubuntu-20.04', 'server_labels': 'Name=test-server', 'server_location': 'hel1'}) => {
"ansible_loop_var": "item",
"changed": true,
"hcloud_server": {
"backup_window": "None",
"datacenter": "hel1-dc2",
"delete_protection": false,
"id": "19461514",
"image": "ubuntu-20.04",
"ipv4_address": "11.111.111.111",
"ipv6": "1a71:7f9:c011:0b09::/64",
"labels": {
"Name": "test-server"
},
"location": "hel1",
"name": "TEST-VARIABLES",
"placement_group": null,
"rebuild_protection": false,
"rescue_enabled": false,
"server_type": "cx11",
"status": "running"
},
"invocation": {
"module_args": {
"allow_deprecated_image": false,
"api_token": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"backups": null,
"datacenter": null,
"delete_protection": null,
"endpoint": "https://api.SERVER.cloud/v1",
"firewalls": null,
"force": false,
"force_upgrade": false,
"id": null,
"image": "ubuntu-20.04",
"labels": {
"Name": "test-server"
},
"location": "hel1",
"name": "TEST-VARIABLES",
"placement_group": null,
"rebuild_protection": null,
"rescue_mode": null,
"server_type": "cx11",
"ssh_keys": null,
"state": "present",
"upgrade_disk": false,
"user_data": null,
"volumes": null
}
},
"item": {
"name": "TEST-VARIABLES",
"os_image": "ubuntu-20.04",
"server_labels": "Name=test-server",
"server_location": "hel1",
"server_type": "cx11"
},
"root_password": "DFLDJFLDFDLFKJDLFKJ"
}
I try to get line with "ipv4_address": "11.111.111.111", and "root_password": "DFLDJFLDFDLFKJDLFKJ", but when I use task:
---
- name: Create a basic server
hcloud_server:
api_token: "{{ token }}"
name: "{{ item.name }}"
server_type: "{{ item.server_type }}"
image: "{{ item.os_image }}"
labels: "{{ item.server_labels }}"
location: "{{ item.server_location }}"
state: present
register: server_info
with_items: "{{ server }}"
- name: Here IP
debug:
var: server_info.root_password
I got error:
TASK [/etc/ansible/roles/CREATE-server : Here IP.] *********************************************************************************************************
ok: [localhost] => {
"server_info.root_password": "VARIABLE IS NOT DEFINED!"
}
Could you please help, how I can get IP line and password line, to use them in the next task (for example to send via email). Thank you!
you register the content of loop, so your result is a list (results):
- name: display
debug:
msg: "ip: {{ item.hcloud_server.ipv4_address }} has password: {{ item.root_password }}"
loop: "{{ server_info.results }}"
result: here you have just one server declared, so just one item in the list results
"msg": "ip: 11.111.111.111 has password: DFDFDFDFDFDFDFDF"
if you want to limit the output of record, you could add loop_control parameter to loop with argument label:
loop: "{{ server_info.results }}"
loop_control:
label: "{{ item.hcloud_server.ipv4_address }}"
you could put another comment if you want with label or even empty string:
loop: "{{ server_info.results }}"
loop_control:
label: "result"

How to check the size of a file list

With Ansible, I need to check if files in directory are bigger than 50M.
I do a set_fact with result of ls and then I loop to stat all files. This works, but I am not able to parse the result to create a fail condition when files are bigger than 50M.
I do this :
- shell: ls -1 "{{ logs_directory }}"*.log
register: list_logs
- set_fact:
list_logs: "{{ list_logs.stdout_lines }}"
- debug:
msg: "{{ list_logs}}"
- name: Get size
stat:
path: "{{ item }}"
with_items: "{{ list_logs }}"
register: size_log
- name: debug
fail:
msg: "Problem with log size > 50M"
when: 'size_log.stat.size / 1024 / 1024 | int > 50'
It works if I have only 1 file, but with multiple servers and multiple files, it doesn't work... How to parse the size_log_result?
Here an example of output size_log :
ok: [192.168.1.2] => {
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"path": "/var/logs/admin.log"
}
},
"item": "/var/logs/admin.log",
"stat": {
"size": 21711,
}
},
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"path": "/var/logs/database.log"
}
},
"item": "/var/logs/database.log",
"stat": {
"size": 11162,
}
}
]
}
}
ok: [192.168.1.5] => {
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"invocation": {
"module_args": {
"path": "/var/logs/database.log"
}
},
"item": "/var/logs/database.log",
"stat": {
"size": 128453958,
}
}
]
}
}
It looks to me like you are reinventing the wheel here, when the find module seems to answer you use case perfectly already.
Given:
- hosts: localhost
gather_facts: no
tasks:
####
## You do not need this task, it is just to create a file
## big enough for demonstration purpose
####
- community.general.filesize:
path: /var/log/heavy.log
size: 51m
- find:
paths: /var/log
patterns: '*.log'
size: 51m
register: _logs
- fail:
msg: >-
Due to file(s): {{
_logs.files | map(attribute='path') | join(', ')
}}
when: _logs.files | length > 0
This will yield:
TASK [community.general.filesize] *********************************
ok: [localhost]
TASK [find] *******************************************************
ok: [localhost]
TASK [fail] *******************************************************
fatal: [localhost]: FAILED! => changed=false
msg: 'Due to file(s): /var/log/heavy.log'

Fetch the values of the variables in ansible

I am trying to list the names whose matches have been found.
- name: search for files containing string
find:
paths: /root/ansible-dir
patterns: "file3.yml"
contains: "{{ item }}"
with_items: "{{ names_list }}"
register: file_match
- name: print file
debug:
msg: "{{ file_match }}"
After the above code is run, the below code gets generated :
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"examined": 14,
"failed": false,
"files": [],
"invocation": {
"module_args": {
"age": null,
"age_stamp": "mtime",
"contains": "node_mem",
"depth": null,
"excludes": null,
"file_type": "file",
"follow": false,
"get_checksum": false,
"hidden": false,
"paths": [
"/root/ansible-dir"
],
"patterns": [
"file3.yml"
],
"recurse": false,
"size": null,
"use_regex": false
}
},
"item": "node_mem",
"matched": 0,
"msg": ""
},
How to fetch the name of the item where the matched attribute is >=1?
Use subelements. The debug below gives you a hint how to iterate the results and fetch files that contain the items
- debug:
msg: "Fetch {{ item.1 }}"
with_subelements:
- "{{ file_match.results }}"
- files
It is not necessary to test the number of files is >=1. If the list of files is empty the iteration will be skipped anyway.

Ansible Dynamic Variables in Playbook

I'm provisioning a bunch of systems (VMs) on a physical host. I'm up to the step where the VM's are running. Now I need to ssh into the VM's via their DHCP addresses. I can pull the IP addresses from the server but I need a way to set these to host_vars. Here are my groups:
ok: [kvm01] => {
"msg": {
"all": [
"kvm01",
"dcos-master-1",
"dcos-agent-1",
"dcos-agent-2",
"dcos_bootstrap"
],
"dcos": [
"dcos-master-1",
"dcos-agent-1",
"dcos-agent-2",
"dcos_bootstrap"
],
"dcos-agents": [
"dcos-agent-1",
"dcos-agent-2"
],
"dcos-bootstraps": [
"dcos_bootstrap"
],
"dcos-masters": [
"dcos-master-1"
],
"kvm": [
"kvm01"
],
"ungrouped": []
}
}
Here's my command:
- name: Get the IP of the VM (DHCP)
command: "/getip.sh {{ item }}"
register: "result"
with_items: "{{ groups['dcos'] }}"
- name: List the output of the variables
debug: msg="{{item.stdout}}"
with_items: "{{result.results}}"
When I run the playbook I get results but they are the FULL JSON result of the command rather than the stdout. There's probably a way to pull out the stdout and assign it to a fact but it's a complex hash. Here's the last result:
TASK [vm_install : Get the IP of the VM (DHCP)] ***************************************************************************
changed: [kvm01] => (item=dcos-master-1)
changed: [kvm01] => (item=dcos-agent-1)
changed: [kvm01] => (item=dcos-agent-2)
changed: [kvm01] => (item=dcos_bootstrap)
TASK [vm_install : List the output of the variables] **********************************************************************
......
ok: [kvm01] => (item={'_ansible_parsed': True, 'stderr_lines': [u'] => {
"item": {
"changed": false,
"cmd": [
"/getip.sh",
"dcos_bootstrap"
],
"delta": "0:00:00.056193",
"end": "2017-09-18 15:45:45.409693",
"invocation": {
"module_args": {
"_raw_params": "/getip.sh dcos_bootstrap",
"_uses_shell": false,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"warn": true
}
},
"item": "dcos_bootstrap",
"rc": 0,
"start": "2017-09-18 15:45:45.353500",
"stderr": " ",
"stdout": "192.168.1.130",
"stdout_lines": [
"192.168.1.130"
]
},
"msg": "192.168.1.130"
}
How can I put the output of the command into an array so that I can use it later in my playbook?
So, like I said in my comment, you've already managed to extract the information you want into an array. You can iterate over those items using with_items as in the follow task that will create an ip_address for each host:
- set_fact:
ip_address: "{{ item.stdout }}"
with_items: "{{ results.results }}"
delegate_to: "{{ item.item }}"
delegate_facts: true
Or you can create a single array containing all of the addresses using Jinja filters:
- set_fact:
all_ip_addresses: "{{ results.results|map(attribute='stdout')|list }}"
Or you could create a dictionary with the same information:
- set_fact:
all_ip_addresses: >
{{ all_ip_addresses
|default({})
|combine({ item.item: item.stdout })}}
with_items: "{{ results.results }}"

Resources