Filter result with selectattr in an Ansible playbook - ansible

I'm trying to get the playbook show only the filtered result.
But instead it showing filtered and non-filtered result.
e.g: I just want it to show only the files that not owned by root in the output.
- set_fact:
target_file: /var/log/apache2/
- name: verify that the logs ownership/perms are belong to system administrators and service accounts.
block:
- find:
paths: "{{ target_file }}"
patterns: "*.log"
register: ownership
failed_when: ownership.files | selectattr('pw_name', '!=', 'root')
- debug:
var: ownership
- find:
paths: "{{ target_file }}"
patterns: "*.log"
register: group
failed_when: group.files | selectattr('gr_name', '!=', 'adm')
- find:
paths: "{{ target_file }}"
patterns: "*.log"
register: permissions
failed_when: permissions.files | selectattr('mode', '!=', '0640')
- set_fact:
stig_text: "PASSED"
rescue:
- name: change the permission and ownership of the files
become: true
file:
path: "{{ item.path }}"
owner: root
group: adm
mode: 0640
with_items: "{{ ownership.files }}"
register: change_perms
- set_fact:
stig_text: "PASSED"
when: change_perms.changed == true
Debug output:
TASK [debug] ************************************************************
ok: [localhost] => {
"ownership": {
"changed": false,
"examined": 18,
"failed": false,
"failed_when_result": false,
"files": [
{
"atime": 1652373886.9567592,
"ctime": 1652373886.9567592,
"dev": 2,
"gid": 0,
"gr_name": "root",
"inode": 3096224743982700,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1652373886.9567592,
"nlink": 1,
"path": "/var/log/alternatives.log",
"pw_name": "root",
"rgrp": true,
"roth": true,
"rusr": true,
"size": 49270,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
},
{
"atime": 1623178394.0,
"ctime": 1652791696.643692,
"dev": 2,
"gid": 0,
"gr_name": "root",
"inode": 3377699720693451,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0600",
"mtime": 1623117753.0,
"nlink": 1,
"path": "/var/log/ubuntu-advantage.log",
"pw_name": "root",
"rgrp": false,
"roth": false,
"rusr": true,
"size": 0,
"uid": 0,
"wgrp": false,
"woth": false,
"wusr": true,
"xgrp": false,
"xoth": false,
"xusr": false
}
],
"matched": 6,
"msg": ""
}
}

Your code structure is a very strange and inefficient approach to this problem.
This is my best guess at what you were trying to do:
- name: Verify the ownership and permissions on logs
vars:
target_file: /var/log/apache2/
block:
- name: Find all log files under {{ target_file }}
find:
paths: "{{ target_file }}"
patterns: "*.log"
register: log_files
- name: Fix permissions and ownership
file:
path: "{{ item.path }}"
owner: root
group: adm
mode: "0640"
become: true
loop: "{{ logs_bad_owner | union(logs_bad_group) | union(logs_bad_perms) }}"
loop_control:
label: "{{ item.path }}"
vars:
logs_bad_owner: "{{ log_files.files | selectattr('pw_name', '!=', 'root') }}"
logs_bad_group: "{{ log_files.files | selectattr('gr_name', '!=', 'adm') }}"
logs_bad_perms: "{{ log_files.files | selectattr('mode', '!=', '0640') }}"
- set_fact:
stig_text: PASSED
rescue:
- set_fact:
stig_text: FAILED
This avoids unnecessary work like running the same find multiple times or looping over the entire set of files when you're only interested in the ones that need fixing.

Because your debug is just after a command that would fail if pw_name is not root, due to your failed_when condition, the debug just won't happen if there is any problematic file, and enters the rescue tasks instead, so when you do see the any debug task result, it means there was no problematic file, in terms on ownership, really.
Now the reason being pointed, you have to understand that Ansible is all about idempotency:
An operation is idempotent if the result of performing it once is exactly the same as the result of performing it repeatedly without any intervening actions.
Source: https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html
So, you don't even have to do anything complex here you just have to set the rights and ownership to what it should:
- name: change the permission and ownership of the files
file:
path: "{{ item }}"
owner: root
group: adm
mode: 0640
with_fileglob:
- /var/log/apache2/*.log
register: permission_audit
And then, if you want to make an audit report, you can do something like:
- name: List of security remediation run during this audit
debug:
var: >-
permission_audit.results
| selectattr('changed')
| map(attribute='diff')
Which would yield something like:
TASK [List of security remediation run during this audit] ********************
ok: [localhost] =>
permission_audit.results | selectattr('changed') | map(attribute='diff'):
- after:
mode: '0640'
path: /var/log/apache2/access.1.log
before:
mode: '0777'
path: /var/log/apache2/access.1.log
- after:
mode: '0640'
path: /var/log/apache2/access.2.log
before:
mode: '0777'
path: /var/log/apache2/access.2.log
If you want to show the success or the failure of the audit in the result of the playbook you can even go one step further and assert the length of this list:
- name: Security audit result
assert:
that: permission_changes | length == 0
fail_msg: Audit on Apache log file rights failed
success_msg: Audit on Apache log file rights passed
vars:
permission_changes: >-
{{
permission_audit.results
| selectattr('changed')
| map(attribute='diff')
}}
And now our playbook will either end in
TASK [Security audit result] **********************************************
ok: [localhost] => changed=false
msg: Audit on Apache log file rights passed
PLAY RECAP ****************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0
Or in
TASK [Security audit result] **********************************************
fatal: [localhost]: FAILED! => changed=false
assertion: permission_changes | length == 0
evaluated_to: false
msg: Audit on Apache log file rights failed
PLAY RECAP ****************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=1
Given ten log files; two having rights issues and the two tasks:
- file:
path: "{{ item }}"
owner: root
group: adm
mode: 0640
with_fileglob:
- /var/log/apache2/*.log
register: permission_audit
- name: List of security remediation run during this audit
debug:
var: >-
permission_audit.results
| selectattr('changed')
| map(attribute='diff')
This would yield:
TASK [file] *****************************************************************
changed: [localhost] => (item=/var/log/apache2/access.1.log)
ok: [localhost] => (item=/var/log/apache2/access.10.log)
ok: [localhost] => (item=/var/log/apache2/access.4.log)
ok: [localhost] => (item=/var/log/apache2/access.9.log)
ok: [localhost] => (item=/var/log/apache2/access.7.log)
ok: [localhost] => (item=/var/log/apache2/access.3.log)
ok: [localhost] => (item=/var/log/apache2/access.5.log)
ok: [localhost] => (item=/var/log/apache2/access.8.log)
ok: [localhost] => (item=/var/log/apache2/access.6.log)
changed: [localhost] => (item=/var/log/apache2/access.2.log)
TASK [List of security remediation run during this audit] *******************
ok: [localhost] =>
permission_audit.results | selectattr('changed') | map(attribute='diff'):
- after:
mode: '0640'
path: /var/log/apache2/access.1.log
before:
mode: '0777'
path: /var/log/apache2/access.1.log
- after:
mode: '0640'
path: /var/log/apache2/access.2.log
before:
mode: '0777'
path: /var/log/apache2/access.2.log
And if you rerun those same two tasks again, then it ends up in:
TASK [file] *****************************************************************
ok: [localhost] => (item=/var/log/apache2/access.1.log)
ok: [localhost] => (item=/var/log/apache2/access.10.log)
ok: [localhost] => (item=/var/log/apache2/access.4.log)
ok: [localhost] => (item=/var/log/apache2/access.9.log)
ok: [localhost] => (item=/var/log/apache2/access.7.log)
ok: [localhost] => (item=/var/log/apache2/access.3.log)
ok: [localhost] => (item=/var/log/apache2/access.5.log)
ok: [localhost] => (item=/var/log/apache2/access.8.log)
ok: [localhost] => (item=/var/log/apache2/access.6.log)
ok: [localhost] => (item=/var/log/apache2/access.2.log)
TASK [List of security remediation run during this audit] *******************
ok: [localhost] =>
permission_audit.results | selectattr('changed') | map(attribute='diff'): []
Now, with an empty list on the last debug, we know our security audit passed everything with a green check.

Related

ansible: "variable is not defined" even with ignore_erros: True & failed_when: 0 > 1 | How to define variable when task is failed/ignored/skipped?

Trying to get a variable defined when the task is partially failed/ignored. When a VM is not found in Vsphere it throws an error: "msg": "Unable to gather information for non-existing VM vm2"
I tried with and without ignore_errors: True and failed_when: 0 > 1 but no change.
I need the variable vm_fact to be defined even when a non-existing VM is in the decom_list. The plan is to change the list after i have identified which VM's are non-existing.
Please note: this task does work when all vm's are present in the system. When one is missing the task fails. I need the task not to fail so that i can clean the decom_list and then run it again.
- name: check/power(on/off) hosts on vsphere
hosts: localhost
ignore_errors: True
vars:
decom_list:
- vm1
- vm2
- vm3
tasks:
- name: check hosts on vsphere
vmware_guest_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
name: "{{ item }}"
schema: vsphere
datacenter: DC1
loop: "{{ decom_list }}"
register: vm_fact
ignore_errors: True
failed_when: 0 > 1
Whatever i try does not work because my vm_fact is not defined.
- debug:
var: "{{ vm_fact }}"
error:
TASK [debug] **************************************************************************************************************************
ok: [localhost] => {
"<class 'dict'>": "VARIABLE IS NOT DEFINED!"
}
Just to confirm i did try all i know.
When i try it with a loop:
- debug:
var: "{{ item['item'] }}"
loop: "{{ vm_fact['results'] }}"
when: '"Unable to gather information for non-existing VM" in item.msg'
I get error:
ok: [localhost] => (item={'failed': False, 'msg': 'Unable to gather information for non-existing VM vm2', 'invocation': {'module_args': {'hostname': 'hostname.domain', 'username': 'username', 'password': 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER', 'name': 'vm2', 'schema': 'vsphere', 'datacenter': 'DC1', 'port': 443, 'validate_certs': True, 'name_match': 'first', 'use_instance_uuid': False, 'tags': False, 'tag_details': False, 'proxy_host': None, 'proxy_port': None, 'uuid': None, 'moid': None, 'folder': None, 'properties': None}}, 'changed': False, 'failed_when_result': False, 'item': 'vm2', 'ansible_loop_var': 'item'}) => {
"ansible_loop_var": "item",
"item": {
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"failed_when_result": false,
"invocation": {
"module_args": {
"datacenter": "DC1",
"folder": null,
"hostname": "hostname.domain",
"moid": null,
"name": "vm2",
"name_match": "first",
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"port": 443,
"properties": null,
"proxy_host": null,
"proxy_port": null,
"schema": "vsphere",
"tag_details": false,
"tags": false,
"use_instance_uuid": false,
"username": "username",
"uuid": null,
"validate_certs": true
}
},
"item": "vm2",
"msg": "Unable to gather information for non-existing VM vm2"
},
"vm2": "VARIABLE IS NOT DEFINED!"
}
ok: [localhost] => (item={'failed': False, 'msg': 'Unable to gather information for non-existing VM vm3', 'invocation': {'module_args': {'hostname': 'hostname.domain', 'username': 'username', 'password': 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER', 'name': 'vm3', 'schema': 'vsphere', 'datacenter': 'DC1', 'port': 443, 'validate_certs': True, 'name_match': 'first', 'use_instance_uuid': False, 'tags': False, 'tag_details': False, 'proxy_host': None, 'proxy_port': None, 'uuid': None, 'moid': None, 'folder': None, 'properties': None}}, 'changed': False, 'failed_when_result': False, 'item': 'vm3', 'ansible_loop_var': 'item'}) => {
"ansible_loop_var": "item",
"item": {
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"failed_when_result": false,
"invocation": {
"module_args": {
"datacenter": "DC1",
"folder": null,
"hostname": "hostname.domain",
"moid": null,
"name": "vm3",
"name_match": "first",
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"port": 443,
"properties": null,
"proxy_host": null,
"proxy_port": null,
"schema": "vsphere",
"tag_details": false,
"tags": false,
"use_instance_uuid": false,
"username": "username",
"uuid": null,
"validate_certs": true
}
},
"item": "vm3",
"msg": "Unable to gather information for non-existing VM vm3"
},
"vm3": "VARIABLE IS NOT DEFINED!"
}
fatal: [localhost]: FAILED! => {"msg": "The conditional check '\"Unable to gather information for non-existing VM\" in item.msg' failed. The error was: error while evaluating conditional (\"Unable to gather information for non-existing VM\" in item.msg): 'dict object' has no attribute 'msg'\n\nThe error appears to be in 'location': line 33, column 24, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n # - debug: var=vm_fact\n ^ here\n\nThere appears to be both 'k=v' shorthand syntax and YAML in this task. Only one syntax may be used.\n"}
Tried:
- debug:
msg: "{{ vm_fact['results']|json_query('msg') }}"
- debug:
msg: "{{ vm_fact['results']|to_json|from_json|map(attribute='msg') }}"
- debug:
msg: "{{ vm_fact['results']|map(attribute='msg') }}"
- debug:
msg: "{{ vm_fact['results']|dict2items|map(attribute='msg') }}"
- debug:
msg: "{{ vm_fact['results']|flatten|map(attribute='msg') }}"
error:
TASK [debug] **************************************************************************************************************************
ok: [localhost] => {
"msg": ""
}
TASK [debug] **************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'msg'\n\nThe error appears to be in 'location': line 42, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - debug:\n ^ here\n"}
...ignoring
TASK [debug] **************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'msg'\n\nThe error appears to be in 'location': line 45, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - debug:\n ^ here\n"}
...ignoring
TASK [debug] **************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ vm_fact['results']|dict2items|map(attribute='msg') }}): dict2items requires a dictionary, got <class 'list'> instead."}
...ignoring
TASK [debug] **************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'msg'\n\nThe error appears to be in 'lcoation': line 51, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - debug:\n ^ here\n"}
...ignoring
You are, frankly, making things very difficult for yourself. Ansible loops through hosts, use it.
decom.inv:
[decom]
vm1
vm2
vm3
decom.yml:
- name: check/power(on/off) hosts on vsphere
hosts: decom
gather_facts: no
tasks:
- name: check hosts on vsphere
vmware_guest_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
name: "{{ ansible_hostname }}"
schema: vsphere
datacenter: DC1
register: vm_fact
ignore_errors: True
failed_when: false
delegate_to: localhost
- name: Show results
debug:
var: vm_fact

Need help looping through Ansible msg output

I need help going through the output of this task in ansible and ONLY pulling out the path for the "{{ item }}" being found.
Tried multiple ways and having very little success. Still learning ansible, and in this case, I'm trying to create a new directory based on the output of my inventory file. (the tail command for now because I only need to test it on 10 entries)
Playbook:
'''
- name: Get the inventory file printout.
command: tail "{{ build_dir }}{{ inventory_file }}"
register: command_output
- debug:
msg: "{{ command_output.stdout_lines }}"
- name : Find the RPMs in "{{ build_dir }}"
find:
paths: "{{ build_dir }}"
patterns: "{{ item }}.rpm"
recurse: yes
with_items:
- "{{ command_output.stdout_lines }}"
register: found_pkgs
- name: Just the Path for each Found Pkgs.
debug:
msg: "{{ item }}"
loop:
- "{{ found_pkgs }}"
Output:
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"examined": 15029,
"failed": false,
"files": [
{
"atime": 1629933434.2974539,
"ctime": 1629814445.3359122,
"dev": 64773,
"gid": 70000,
"gr_name": "engineering",
"inode": 469762133,
"isblk": false,
"ischr": false,
"isdir": false,
"isfifo": false,
"isgid": false,
"islnk": false,
"isreg": true,
"issock": false,
"isuid": false,
"mode": "0644",
"mtime": 1629814445.3359122,
"nlink": 1,
"path": "<REDACTED>/newISO/repos/zeek/packages/zeek-btest-4.0.2-1.1.x86_64.rpm",
You could use json_query filter to extract the path of each file from results[]. A set_fact task such as below should do:
- name: save the files path in file_names var
set_fact:
file_paths: "{{ found_pkgs | json_query('results[].files[].path') }}"
- name: show file names
debug:
var: file_paths

Get list of ip addesses from multiple citrix hypervisors vm's using ansible

I am using ansible and the xenserver_guest_info module to create a list of ip addresses from multiple virtual machines on my citrix hypervisor server. Currently I just want to print a list of ip's but storing these ip's in an ansible list would be even better. I plan to loop through this list of ip's and run commands on all these vm's using ansible.
Here is my ansible playbook currently. It loops through the dictionary output that the xenserver module returns, trying to extract the ip address.:
- name: Manage VMs
connection: local
hosts: localhost
# Hypervisor server info from vars file
vars_files:
- xen_vars.yml
tasks:
- name: Gather facts
xenserver_guest_info:
hostname: "{{ xen_address}}"
username: "{{ admin_username }}"
password: "{{ admin_password }}"
name: "{{ item }}"
loop: "{{ xen_machines }}"
register: facts
# - name: Get IP's of VM's
- debug:
msg: "{{item.instance.networks}}"
loop: "{{facts.results}}"
It produces the following output given a list of two vm's on my server:
PLAY [Manage VMs] **************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [Gather facts] ************************************************************
ok: [localhost] => (item=Ubuntu 20)
ok: [localhost] => (item=Ubuntu 20 2)
TASK [debug] *******************************************************************
ok: [localhost] => (item={'failed': False, 'changed': False, 'instance': {'state': 'poweredoff', 'name': 'Ubuntu 20', 'name_desc': '', 'uuid': 'cf5db672-67cf-7e8c-6951-f5959ab62e26', 'is_template': False, 'folder': '', 'hardware': {'num_cpus': 1, 'num_cpu_cores_per_socket': 1, 'memory_mb': 1024}, 'disks': [{'size': 21474836480, 'name': 'Ubuntu 20 0', 'name_desc': 'Created by template provisioner', 'sr': 'Local storage', 'sr_uuid': 'd7bb817b-281e-fd9c-33a3-54db8935d596', 'os_device': 'xvda', 'vbd_userdevice': '0'}], 'cdrom': {'type': 'iso', 'iso_name': 'ubuntu-20.04.1-desktop-amd64.iso'}, 'networks': [{'name': 'Pool-wide network associated with eth0', 'mac': 'a2:07:be:29:5f:ad', 'vif_device': '0', 'mtu': '1500', 'ip': '', 'prefix': '', 'netmask': '', 'gateway': '', 'ip6': [], 'prefix6': '', 'gateway6': ''}], 'home_server': 'citrix-mwyqyqaa', 'domid': '-1', 'platform': {'timeoffset': '0', 'videoram': '8', 'hpet': 'true', 'secureboot': 'false', 'device-model': 'qemu-upstream-compat', 'apic': 'true', 'device_id': '0001', 'vga': 'std', 'nx': 'true', 'pae': 'true', 'viridian': 'false', 'acpi': '1'}, 'other_config': {'base_template_name': 'Ubuntu Focal Fossa 20.04', 'import_task': 'OpaqueRef:3b6061a0-a204-4ed4-be20-842a359a70fa', 'mac_seed': 'f6ae87b5-2f00-0717-559e-8b624fe92f35', 'install-methods': 'cdrom,nfs,http,ftp', 'linux_template': 'true'}, 'xenstore_data': {'vm-data': '', 'vm-data/mmio-hole-size': '268435456'}, 'customization_agent': 'custom'}, 'invocation': {'module_args': {'hostname': '192.168.0.187', 'username': 'root', 'password': 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER', 'name': 'Ubuntu 20', 'validate_certs': True, 'uuid': None}}, 'item': 'Ubuntu 20', 'ansible_loop_var': 'item'}) => {
"msg": [
{
"gateway": "",
"gateway6": "",
"ip": "192.168.0.2",
"ip6": [],
"mac": "a2:07:be:29:5f:ad",
"mtu": "1500",
"name": "Pool-wide network associated with eth0",
"netmask": "",
"prefix": "",
"prefix6": "",
"vif_device": "0"
}
]
}
ok: [localhost] => (item={'failed': False, 'changed': False, 'instance': {'state': 'poweredoff', 'name': 'Ubuntu 20 2', 'name_desc': '', 'uuid': 'b087832e-81f1-c091-1363-8b8ba8442c8e', 'is_template': False, 'folder': '', 'hardware': {'num_cpus': 1, 'num_cpu_cores_per_socket': 1, 'memory_mb': 1024}, 'disks': [{'size': 21474836480, 'name': 'Ubuntu 20 0', 'name_desc': 'Created by template provisioner', 'sr': 'Local storage', 'sr_uuid': 'd7bb817b-281e-fd9c-33a3-54db8935d596', 'os_device': 'xvda', 'vbd_userdevice': '0'}], 'cdrom': {'type': 'iso', 'iso_name': 'ubuntu-20.04.1-desktop-amd64.iso'}, 'networks': [{'name': 'Pool-wide network associated with eth0', 'mac': 'e6:cd:ca:ff:c3:e0', 'vif_device': '0', 'mtu': '1500', 'ip': '', 'prefix': '', 'netmask': '', 'gateway': '', 'ip6': [], 'prefix6': '', 'gateway6': ''}], 'home_server': 'citrix-mwyqyqaa', 'domid': '-1', 'platform': {'timeoffset': '0', 'videoram': '8', 'hpet': 'true', 'secureboot': 'false', 'device-model': 'qemu-upstream-compat', 'apic': 'true', 'device_id': '0001', 'vga': 'std', 'nx': 'true', 'pae': 'true', 'viridian': 'false', 'acpi': '1'}, 'other_config': {'base_template_name': 'Ubuntu Focal Fossa 20.04', 'import_task': 'OpaqueRef:3b6061a0-a204-4ed4-be20-842a359a70fa', 'mac_seed': '3c56a628-0f68-34f9-fe98-4bf2214a5891', 'install-methods': 'cdrom,nfs,http,ftp', 'linux_template': 'true'}, 'xenstore_data': {'vm-data': '', 'vm-data/mmio-hole-size': '268435456'}, 'customization_agent': 'custom'}, 'invocation': {'module_args': {'hostname': '192.168.0.187', 'username': 'root', 'password': 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER', 'name': 'Ubuntu 20 2', 'validate_certs': True, 'uuid': None}}, 'item': 'Ubuntu 20 2', 'ansible_loop_var': 'item'}) => {
"msg": [
{
"gateway": "",
"gateway6": "",
"ip": "192.168.0.3",
"ip6": [],
"mac": "e6:cd:ca:ff:c3:e0",
"mtu": "1500",
"name": "Pool-wide network associated with eth0",
"netmask": "",
"prefix": "",
"prefix6": "",
"vif_device": "0"
}
]
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I tried accessing just the ip but it hasn't been working well since it is stored in a list.
Again my end goal is to just have ansible spit out a list of ip's like so:
192.168.0.2
192.168.0.3
Or even better to store these in an ansible list for later usage. Any help would be much appreciated.
If the only real problem you have is that the IPs are in a list, and you know (because of the way you have configured your VMs) that the list always has only one entry, then just get the first item in the list:
item.instance.networks[0].ip
If you want to then use these IP addresses to do some Ansible work on the VMs, I suggest that you use add_host to build a new inventory for a second play in your playbook:
- name: Manage VMs
connection: local
hosts: localhost
# Hypervisor server info from vars file
vars_files:
- xen_vars.yml
tasks:
- name: Gather facts
xenserver_guest_info:
hostname: "{{ xen_address}}"
username: "{{ admin_username }}"
password: "{{ admin_password }}"
name: "{{ item }}"
loop: "{{ xen_machines }}"
register: facts
# - name: Get IP's of VM's
- debug:
msg: "{{item.instance.networks[0].ip}}"
loop: "{{facts.results}}"
- name: Build inventory of VMs
add_host:
name: "{{ item.instance.networks[0].ip }}"
groups: vms
# You can add other variables per-host here if you want
loop: "{{ xen_machines }}"
loop_control:
label: "{{ item.instance.name }}"
- name: Do stuff directly to the VMs
hosts: vms # This is the group you just created
connection: ssh
tasks:
- debug:
msg: "{{ Hello from a VM }}"
The tasks below
- set_fact:
ips: "{{ facts.results|
json_query('[].instance.networks[].ip') }}"
- debug:
var: ips
should give
ips:
- 192.168.0.2
- 192.168.0.3

Ansible register variable iteration issue with var.results

Below is the code which hold values in a register variable:
- name: "Install Msi on the Server - {{item.app_name}}"
win_package:
path: d:\temp_deploy\{{servergroup_variable}}_{{release_variable}}.{{build_variable}}\{{ item.app_name }}_{{ item.app_expectedversion }}.msi
state: present
arguments: "INSTALLDIR={{ item.app_installPath }} SELECTEDREGION={{ environment_variable }} ALLUSERS=1 /L*V c:\\log\\Deployment-{{ item.app_name }}.log"
retries: 3
register: task_result
until: task_result.failed == true
when: (item.app_type == "webService") or
(item.app_type == "Service" )
ignore_errors: yes
register: msi_deploy_var
After I run the msi_deploy_var, it holds the following results:
ok: [server1] => {
"msg": {
"changed": false,
"failed": false,
"reboot_required": false
}
}
ok: [server2] => {
"msg": {
"changed": true,
"failed": false,
"rc": 0,
"reboot_required": false
}
}
Now the real problem starts - when I tried to iterate on msi_deploy_var then its working as expected.
- name: "Validate Success deploy - {{item.app_name}}"
set_fact:
msi_deploy_result: "{{msi_deploy_result + [item.app_name]}}"
when: (inner_item.rc is defined) and (inner_item.rc != 0)
with_items: "{{ msi_deploy_var.results }}"
loop_control:
loop_var: inner_item
But its always skipping conditional evaluation.
Any help would be appreciated.

Ansible playbook to handle both Linux and Windows hosts

Trying to write one playbook that checks whether a set of files exist on a set of servers (both Linux and Windows hosts), and if they do, then to replace the files.
Here is what i have so far:
---
- hosts: "{{hosts}}"
vars:
scripts:
- check_blackout.pl
- check_empty.pl
tasks:
- name: windows stat
with_items: "{{scripts}}"
win_stat: path=D:\scripts\{{item}}
register: windows_stat
when: "'windows' in group_names"
- name: other stat
with_items: "{{scripts}}"
stat: path=/usr/local/bin/{{item}}
register: other_stat
remote_user: "{{script_owner | default(ansible_user)}}"
when: "'windows' not in group_names"
- name: windows debug
with_items: "{{windows_stat.results}}"
debug: var={{item.item}}
when: "{{item.stat.exists}}"
- name: other debug
with_items: "{{other_stat.results}}"
debug: var={{item.item}}
when: "{{item.stat.exists}}"
...
When I run this against a Windows and Linux host for testing, I get the following:
[ansible#vmhklftpscdv1 ~]$ ansible-playbook test.yml -e "hosts=vmhkge1jasdev01,jdeesbkup" --ask-vault-pass
Vault password:
PLAY [vmhkge1jasdev01,jdeesbkup] ************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************
ok: [vmhkge1jasdev01]
ok: [jdeesbkup]
TASK [windows stat] *************************************************************************************************************************************************************
skipping: [jdeesbkup] => (item=check_blackout.pl)
skipping: [jdeesbkup] => (item=check_empty.pl)
ok: [vmhkge1jasdev01] => (item=check_blackout.pl)
ok: [vmhkge1jasdev01] => (item=check_empty.pl)
TASK [other stat] ***************************************************************************************************************************************************************
skipping: [vmhkge1jasdev01] => (item=check_empty.pl)
skipping: [vmhkge1jasdev01] => (item=check_blackout.pl)
ok: [jdeesbkup] => (item=check_blackout.pl)
ok: [jdeesbkup] => (item=check_empty.pl)
TASK [windows debug] ************************************************************************************************************************************************************
skipping: [vmhkge1jasdev01] => (item={'_ansible_parsed': True, u'stat': {u'exists': False}, '_ansible_item_result': True, '_ansible_no_log': False, u'changed': False, 'item': u'check_empty.pl', 'invocation': {'module_name': u'win_stat'}})
skipping: [vmhkge1jasdev01] => (item={'_ansible_parsed': True, u'stat': {u'exists': False}, '_ansible_item_result': True, '_ansible_no_log': False, u'changed': False, 'item': u'check_blackout.pl', 'invocation': {'module_name': u'win_stat'}})
fatal: [jdeesbkup]: FAILED! => {"failed": true, "msg": "The conditional check '{{item.stat.exists}}' failed. The error was: error while evaluating conditional ({{item.stat.exists}}): 'dict object' has no attribute 'stat'\n\nThe error appears to have been in '/home/ansible/test.yml': line 19, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n when: \"'windows' not in group_names\"\n - name: windows debug\n ^ here\n"}
TASK [other debug] **************************************************************************************************************************************************************
fatal: [vmhkge1jasdev01]: FAILED! => {"failed": true, "msg": "The conditional check '{{item.stat.exists}}' failed. The error was: error while evaluating conditional ({{item.stat.exists}}): 'dict object' has no attribute 'stat'\n\nThe error appears to have been in '/home/ansible/test.yml': line 23, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n when: \"{{item.stat.exists}}\"\n - name: other debug\n ^ here\n"}
to retry, use: --limit #/home/ansible/test.retry
PLAY RECAP **********************************************************************************************************************************************************************
jdeesbkup : ok=2 changed=0 unreachable=0 failed=1
vmhkge1jasdev01 : ok=2 changed=0 unreachable=0 failed=1
[ansible#vmhklftpscdv1 ~]$
Any ideas how I can get this working?
I've tried various combinations including checking the group membership before checking the results:
when: "'windows' in group_names and {{item.stat.exists}}"
However Ansible still seems to check the item.stat field even when the first part of the condition is false.
Or is my fundamental approach wrong, should I be splitting these tasks up into different playbooks?
Yes, you are right. with_items is evaluated before when. You can add a default empty list to avoid the error:
- name: other debug
with_items: "{{other_stat.results | default([]) }}"
debug: var={{item}}
when: "{{item.stat.exists | default(false)}}"
Got it working, here is successful playbook and output:
---
- hosts: "{{hosts}}"
vars:
scripts:
- run.pl
tasks:
- name: win_stat
with_items: scripts
win_stat: path="D:\scripts\{{item}}"
register: "win_result"
when: "'windows' in group_names"
- name: other_stat
with_items: scripts
stat: path="/usr/local/bin/{{item}}"
register: "other_result"
when: "'windows' not in group_names"
- debug: var=win_result
- debug: var=other_result
- with_items: "{{ win_result.results }}"
debug: msg="{{item.item}}"
when: "not (item.skipped | default(false))"
- with_items: "{{ other_result.results}}"
debug: msg="{{item.item}}"
when: "not (item.skipped | default(false))"
...
[ansible#vmhklftpscdv1 ~]$ ansible-playbook debug.yml -e "hosts=jdeesbkup,vmhkge1jasdev01" --ask-vault-pass
Vault password:
PLAY [jdeesbkup,vmhkge1jasdev01] **************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************
ok: [jdeesbkup]
ok: [vmhkge1jasdev01]
TASK [win_stat] *******************************************************************************************************************************************
skipping: [jdeesbkup] => (item=scripts)
ok: [vmhkge1jasdev01] => (item=scripts)
TASK [other_stat] *****************************************************************************************************************************************
skipping: [vmhkge1jasdev01] => (item=scripts)
ok: [jdeesbkup] => (item=scripts)
TASK [debug] **********************************************************************************************************************************************
ok: [jdeesbkup] => {
"win_result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"changed": false,
"item": "scripts",
"skip_reason": "Conditional check failed",
"skipped": true
}
]
}
}
ok: [vmhkge1jasdev01] => {
"win_result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"invocation": {
"module_name": "win_stat"
},
"item": "scripts",
"stat": {
"exists": false
}
}
]
}
}
TASK [debug] **********************************************************************************************************************************************
ok: [jdeesbkup] => {
"other_result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_attributes": true,
"get_checksum": true,
"get_md5": true,
"get_mime": true,
"path": "/usr/local/bin/scripts"
},
"module_name": "stat"
},
"item": "scripts",
"stat": {
"exists": false
}
}
]
}
}
ok: [vmhkge1jasdev01] => {
"other_result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"changed": false,
"item": "scripts",
"skip_reason": "Conditional check failed",
"skipped": true
}
]
}
}
TASK [debug] **********************************************************************************************************************************************
skipping: [jdeesbkup] => (item={'skipped': True, '_ansible_no_log': False, 'skip_reason': u'Conditional check failed', '_ansible_item_result': True, 'item': u'scripts', 'changed': False})
ok: [vmhkge1jasdev01] => (item={'_ansible_parsed': True, u'stat': {u'exists': False}, '_ansible_item_result': True, '_ansible_no_log': False, u'changed': False, 'item': u'scripts', 'invocation': {'module_name': u'win_stat'}}) => {
"item": {
"changed": false,
"invocation": {
"module_name": "win_stat"
},
"item": "scripts",
"stat": {
"exists": false
}
},
"msg": "scripts"
}
TASK [debug] **********************************************************************************************************************************************
---
- hosts: "{{hosts}}"
vars:
scripts:
- run.pl
tasks:
- name: win_stat
with_items: scripts
win_stat: path="D:\scripts\{{item}}"
register: "win_result"
when: "'windows' in group_names"
- name: other_stat
with_items: scripts
stat: path="/usr/local/bin/{{item}}"
register: "other_result"
when: "'windows' not in group_names"
- debug: var=win_result
- debug: var=other_result
- with_items: "{{ win_result.results }}"
debug: msg="{{item.item}}"
when: "not (item.skipped | default(false))"
- with_items: "{{ other_result.results}}"
debug: msg="{{item.item}}"
when: "not (item.skipped | default(false))"
# when: "'windows' in group_names and {{item.stat.exists}}"
# - name: win_test
# with_items: {{win_result.results}}
# debug: var="{{item}}"
# when: "'windows' in group_names and {{item.stat.exists}}"
~
~
~
skipping: [vmhkge1jasdev01] => (item={'skipped': True, '_ansible_no_log': False, 'skip_reason': u'Conditional check failed', '_ansible_item_result': True, 'item': u'scripts', 'changed': False})
ok: [jdeesbkup] => (item={'_ansible_parsed': True, u'stat': {u'exists': False}, '_ansible_item_result': True, '_ansible_no_log': False, u'changed': False, 'item': u'scripts', 'invocation': {'module_name': u'stat', u'module_args': {u'checksum_algorithm': u'sha1', u'get_checksum': True, u'follow': False, u'path': u'/usr/local/bin/scripts', u'get_md5': True, u'get_mime': True, u'get_attributes': True}}}) => {
"item": {
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_attributes": true,
"get_checksum": true,
"get_md5": true,
"get_mime": true,
"path": "/usr/local/bin/scripts"
},
"module_name": "stat"
},
"item": "scripts",
"stat": {
"exists": false
}
},
"msg": "scripts"
}
PLAY RECAP ************************************************************************************************************************************************
jdeesbkup : ok=5 changed=0 unreachable=0 failed=0
vmhkge1jasdev01 : ok=5 changed=0 unreachable=0 failed=0

Resources