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

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

Related

Ansible playbook for restic

i was trying to setup Restic on a remote server using ansible but I keep getting this error
failed: [167.172.31.145] (item={'key': 'remote', 'value': {'location':
'ams3.digitaloceanspaces.com 167.172.31.145', 'password': '',
'aws_access_key': '', 'aws_secret_access_key': '',
'aws_default_region': '', 'init': True}}) => {"ansible_loop_var":
"item", "changed": false, "cmd": ["/usr/local/bin/restic", "init"],
"delta": "0:00:02.769841", "end": "2022-10-15 07:43:42.240541",
"failed_when_result": true, "item": {"key": "remote", "value":
{"aws_access_key": "", "aws_default_region": "",
"aws_secret_access_key": "", "init": true, "location":
"ams3.digitaloceanspaces.com 167.172.31.145", "password": ""}}, "msg":
"non-zero return code", "rc": 1, "start": "2022-10-15
07:43:39.470700", "stderr": "Fatal: create key in repository at
s3:ams3.digitaloceanspaces.com/resticspace/ failed: client.PutObject:
Access Denied.", "stderr_lines": ["Fatal: create key in repository at
s3:ams3.digitaloceanspaces.com/resticspace/ failed: client.PutObject:
Access Denied."], "stdout": "", "stdout_lines": []}
here's my play book
- hosts: testdrops
roles:
- role: do1jlr.restic
vars:
restic_create_schedule: true
restic_create_cron: true
restic_schedule_type: cronjob
restic_repos:
remote:
location: ams3.digitaloceanspaces.com {{ inventory_hostname }}
password: "{{ lookup('env', 'RESTIC_PASSWORD') }}"
aws_access_key: "{{ lookup('env', 'AWS_ACCESS_KEY_ID') }}"
aws_secret_access_key: "{{ lookup('env', 'AWS_SECRET_ACCESS_KEY') }}"
aws_default_region: "{{ lookup('env', 'AWS_DEFAULT_REGION') }}"
init: true
restic_backups:
etc:
name: etc
repo: remote
src: /etc/
scheduled: true
schedule_minute: 0
schedule_hour: 9
schedule_weekday: 1
schedule_month: '*'

Filter result with selectattr in an Ansible playbook

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.

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 extract sub-string from a matched element of a List variable

I have a variable, which is a list as below:
"test_list": [
"some text here",
"server1",
"command1 parameter1",
"path/to/command2 parameter2 server1_202012",
"some more text",
"New backup file is server1_202019"
]
I am trying to extract the substring server1_202019 which is at the end of the line that starts with New backup file.... There is only one line like this and I've tried below option to get the substring.
- set_fact:
test_name: "{{ test_list | select('^New backup file is (.+)$','\\1') }}"
and the output I've received is:
"test_name": "<generator object _select_or_reject at 0x3fe5b40>"
I've tried below code also, but failed.
- set_fact:
test_name: "{{ test_list | regex_search('^New backup file is (.+)$','\\1') }}"
The error is:
"msg": "Unexpected templating type error occurred on ({{ test_list | regex_search('^New backup file is (.+)$','\\\\1') }}): ..... expected string or buffer"
Could somebody please suggest how I can get the output as below:
"test_name": "server1_202019"
The ansible version I'm using is 2.9
Thanks
Select the item from the list and then use regex_replace. For example
- set_fact:
test_name: "{{ test_list|
select('regex', my_regex)|
first|
regex_replace(my_regex, my_replace) }}"
vars:
my_regex: '^New backup file is (.*)$'
my_replace: '\1'
- debug:
var: test_name
give
"test_name": "server1_202019"
Since test_list is a list, you need to loop through the items to be matched using regex. Something like below can be done.
- set_fact:
matched: "{{ item | regex_search('^New backup file is (.+)$','\\1') }}"
loop: "{{ test_list }}"
register: matching_items
- debug:
var: item.ansible_facts.matched
with_items: "{{ matching_items.results }}"
when: item.ansible_facts.matched != ""
Should output like below for debug:
TASK [debug] *******************************************************************************************************************************
skipping: [localhost] => (item={'changed': False, 'ansible_facts': {'matched': ''}, 'failed': False, 'item': 'some text here', 'ansible_loop_var': 'item'})
skipping: [localhost] => (item={'changed': False, 'ansible_facts': {'matched': ''}, 'failed': False, 'item': 'server1', 'ansible_loop_var': 'item'})
skipping: [localhost] => (item={'changed': False, 'ansible_facts': {'matched': ''}, 'failed': False, 'item': 'command1 parameter1', 'ansible_loop_var': 'item'})
skipping: [localhost] => (item={'changed': False, 'ansible_facts': {'matched': ''}, 'failed': False, 'item': 'path/to/command2 parameter2 server1_202012', 'ansible_loop_var': 'item'})
ok: [localhost] => (item={'changed': False, 'ansible_facts': {'matched': ['server1_202019']}, 'failed': False, 'item': 'New backup file is server1_202019', 'ansible_loop_var': 'item'}) => {
"ansible_loop_var": "item",
"item": {
"ansible_facts": {
"matched": [
"server1_202019"
]
},
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"item": "New backup file is server1_202019"
},
"item.ansible_facts.matched": [
"server1_202019"
]
}
skipping: [localhost] => (item={'changed': False, 'ansible_facts': {'matched': ''}, 'failed': False, 'item': 'some more text', 'ansible_loop_var': 'item'})

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