I recently made an upgrade of our AWX instance from 19.4.0 to 21.11.0. We're using AWX among other things to install Windows Updates on Windows servers. At the end of the whole play we're using Jinja2 template to generate an HTML report file listing the installed (or not) patches (KBs), which is sent in a mail after the play has run successfully.
After the upgrade of AWX and using AWX-EE 21.11.0 we get the error "AnsibleUndefinedVariable: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'kb'"
Versions before ugprade: AWX: 19.4.0 AWX-EE: 0.6.0
Versions after upgrade: AWX version: 21.11.0 AWX-EE version: 21.11.0
Role: winupdates_deploy:
---
# Installation of Windows Updates
- name: Install Windows Updates
ignore_errors: yes
ansible.windows.win_updates:
category_names: "{{ categorylist }}"
accept_list: "{{ accepted_kb }}"
reject_list: "{{ rejected_kb }}"
reboot: no
state: installed
log_path: "{{ loglocation }}\\awxlog_winupdates_{{ report_type }}.txt"
register: list_of_updates
Sample of var "list_of_updates":
{
"list_of_updates": {
"changed": false,
"reboot_required": false,
"found_update_count": 1,
"failed_update_count": 0,
"installed_update_count": 0,
"updates": {
"88569462-c620-4437-9d8f-baeea24ca37e": {
"title": "Security Intelligence Update for Microsoft Defender Antivirus - KB2267602 (Version 1.383.1098.0)",
"kb": [
"2267602"
],
"categories": [
"Definition Updates",
"Microsoft Defender Antivirus"
],
"id": "88569462-c620-4437-9d8f-baeea24ca37e",
"downloaded": false,
"installed": false
}
},
"filtered_updates": {
"8e212273-94f0-4b31-8bb5-00676a5423f4": {
"title": "Windows Malicious Software Removal Tool x64 - v5.110 (KB890830)",
"kb": [
"890830"
],
"categories": [
"Update Rollups",
"Windows Server 2016",
"Windows Server 2019"
],
"id": "8e212273-94f0-4b31-8bb5-00676a5423f4",
"downloaded": false,
"installed": false,
"filtered_reason": "category_names",
"filtered_reasons": [
"category_names"
]
}
},
"failed": false
},
"_ansible_verbose_always": true,
"_ansible_no_log": false,
"changed": false
}
Role: send_report_mail
---
# Generieren des HTML-Reports mit Jinja2 Templates
- name: Clean Up Reports Directory if it exists
file:
path: reports
state: absent
delegate_to: localhost
run_once: yes
- name: Create Reports Directory
file:
path: reports
state: directory
mode: 0755
delegate_to: localhost
run_once: yes
- name: Template Rendering
template:
src: individual_vm_report.html.j2
dest: "reports/z{{ inventory_hostname }}_interim_report.html"
delegate_to: localhost
- name: Create Interim Report
template:
src: consolidated_vms_report.html.j2
dest: "reports/Consolidated_VMs_Report.html"
delegate_to: localhost
run_once: yes
- name: Consolidate Interim Report
assemble:
src: reports/
dest: reports/Consolidated_VMs_Report.html
delegate_to: localhost
run_once: yes
- name: Generate Remaining Portion of Report
template:
src: consolidated_vms_report_last_block.html.j2
dest: "reports/consolidated_vms_report_last_block.html"
delegate_to: localhost
run_once: yes
- name: Get the List of Interim Reports
find:
paths: reports/
patterns: '*_interim_report.html'
register: interim_reports_to_delete
delegate_to: localhost
run_once: yes
- name: Clean Up Interim Reports
file:
path: "{{ item.path }}"
state: absent
loop: "{{ interim_reports_to_delete.files }}"
delegate_to: localhost
run_once: yes
- name: Consolidate Final Report
assemble:
src: reports/
dest: reports/Consolidated_VMs_Report.html
delegate_to: localhost
run_once: yes
- name: Rename Report File
command: mv reports/Consolidated_VMs_Report.html reports/AWXReport_WinUpdates_{{ report_type }}_{{ customername }}.html
delegate_to: localhost
run_once: yes
- name: Final Clean Up
file:
path: reports/consolidated_vms_report_last_block.html
state: absent
delegate_to: localhost
run_once: yes
# Send Mail mit HTML-Report
- name: Send Email with Report
community.general.mail:
host: *****
port: 25
from: "***@***.***"
to: "{{ sendto_email }}"
subject: "AWX Report - WinUpdates {{ report_type }} ({{ customername }})"
body: |
AWX Report - WinUpdates {{ report_type }} ({{ customername }})
Lorem Ipsum...
attach:
- reports/AWXReport_WinUpdates_{{ report_type }}_{{ customername }}.html
delegate_to: localhost
run_once: yes
Jinja2 template block (individual_vm_report.html.j2):
...
<tr>
<td>{{ inventory_hostname }}</td>
<td>{{ list_of_updates.found_update_count }} </td>
<td>
<ul>
{% for win_update in list_of_updates.updates %}
<li>{{ win_update.kb }}</li>
{% endfor %}
</ul>
</td>
<td>
<ul>
{% for win_update in list_of_updates.updates %}
<li>{{ win_update.title }}</li>
{% endfor %}
</ul>
</td>
<td>
<ul>
{% for win_update in list_of_updates.updates %}
<li>{{ win_update.installed }}</li>
{% endfor %}
</ul>
</td>
<td><font color="red">{{ report_comment }} </font></td>
</tr>
...
Error (TASK [send_report_mail : Template Rendering]):
{
"msg": "AnsibleUndefinedVariable: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'kb'",
"exception": "Traceback (most recent call last):\n File \"/usr/local/lib/python3.8/site-packages/ansible/template/__init__.py\", line 1156, in do_template\n res = j2_concat(rf)\n File \"<template>\", line 22, in root\n File \"/usr/lib/python3.8/site-packages/jinja2/runtime.py\", line 747, in _fail_with_undefined_error\n raise self._undefined_exception(self._undefined_message)\njinja2.exceptions.UndefinedError: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'kb'\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"/usr/local/lib/python3.8/site-packages/ansible/plugins/action/template.py\", line 150, in run\n resultant = templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False)\n File \"/usr/local/lib/python3.8/site-packages/ansible/template/__init__.py\", line 1193, in do_template\n raise AnsibleUndefinedVariable(e)\nansible.errors.AnsibleUndefinedVariable: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'kb'\n",
"_ansible_no_log": false,
"changed": false,
"_ansible_delegated_vars": {
"ansible_host": "localhost",
"ansible_port": 5986,
"ansible_user": "root",
"ansible_connection": "local"
}
}
Before with AWX-EE 0.6.0 it worked. Any idea/hint how I can fix this?
(Btw, I also tried AWX-EE lates and also AWX-EE 21.12.0, but they fail with "credssp: requests auth method is credssp, but requests-credssp is not installed")
I don't have an awx nor windows to test this, but I'm almost positive the problem is for win_update in list_of_updates.updates
is iterating over a dict not a list. Thus, you'll want
{% for win_update in list_of_updates.updates.values() %}
{% endfor %}
to get the values from the dict, versus what's happening now is that win_update
is the key from that dict