Update www-install-win-updates.yaml
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
- name: Windows Update Installation from Assessment Report
|
- name: Windows Update Installation from Assessment Report
|
||||||
hosts: windows
|
hosts: windows
|
||||||
gather_facts: no
|
gather_facts: no
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Get current timestamp
|
- name: Get current timestamp
|
||||||
set_fact:
|
set_fact:
|
||||||
@@ -43,27 +44,38 @@
|
|||||||
- kb_numbers is defined
|
- kb_numbers is defined
|
||||||
- kb_numbers | length > 0
|
- kb_numbers | length > 0
|
||||||
|
|
||||||
- name: Install Windows updates by KB numbers
|
# ---- Patch with failure capture (block/rescue) ----
|
||||||
win_updates:
|
- name: Install Windows updates by KB numbers (with failure capture)
|
||||||
category_names: '*'
|
block:
|
||||||
state: installed
|
- name: Install Windows updates by KB numbers
|
||||||
accept_list: "{{ kb_numbers }}"
|
win_updates:
|
||||||
log_path: 'C:\Temp\windows_update_installation.log'
|
category_names: '*'
|
||||||
register: installation_result
|
state: installed
|
||||||
when:
|
accept_list: "{{ kb_numbers }}"
|
||||||
- kb_updates_file.stat.exists
|
log_path: 'C:\Temp\windows_update_installation.log'
|
||||||
- kb_numbers is defined
|
register: installation_result
|
||||||
- kb_numbers | length > 0
|
when:
|
||||||
|
- kb_updates_file.stat.exists
|
||||||
|
- kb_numbers is defined
|
||||||
|
- kb_numbers | length > 0
|
||||||
|
|
||||||
- name: Mark host failed if any KB installs failed
|
- name: Mark host failed if any KB installs failed (module reported partial failures)
|
||||||
set_fact:
|
set_fact:
|
||||||
patch_failed_host: "{{ (installation_result.failed_update_count | default(0) | int) > 0 }}"
|
patch_failed_host: "{{ (installation_result.failed_update_count | default(0) | int) > 0 }}"
|
||||||
patch_failed_count: "{{ installation_result.failed_update_count | default(0) | int }}"
|
patch_failed_count: "{{ installation_result.failed_update_count | default(0) | int }}"
|
||||||
when:
|
when: installation_result is defined
|
||||||
- kb_updates_file.stat.exists
|
|
||||||
- kb_numbers is defined
|
rescue:
|
||||||
- kb_numbers | length > 0
|
- name: Mark host failed because win_updates task error/exception
|
||||||
- installation_result is defined
|
set_fact:
|
||||||
|
patch_failed_host: true
|
||||||
|
patch_failed_count: "{{ (patch_failed_count | default(0) | int) + 1 }}"
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: Ensure failure flags exist (default to false/0)
|
||||||
|
set_fact:
|
||||||
|
patch_failed_host: "{{ patch_failed_host | default(false) }}"
|
||||||
|
patch_failed_count: "{{ patch_failed_count | default(0) | int }}"
|
||||||
|
|
||||||
- name: Display installation summary
|
- name: Display installation summary
|
||||||
debug:
|
debug:
|
||||||
@@ -74,16 +86,16 @@
|
|||||||
- "Updates Installed: {{ installation_result.installed_update_count | default(0) }}"
|
- "Updates Installed: {{ installation_result.installed_update_count | default(0) }}"
|
||||||
- "Updates Failed: {{ installation_result.failed_update_count | default(0) }}"
|
- "Updates Failed: {{ installation_result.failed_update_count | default(0) }}"
|
||||||
- "Reboot Required: {{ 'Yes' if installation_result.reboot_required | default(false) else 'No' }}"
|
- "Reboot Required: {{ 'Yes' if installation_result.reboot_required | default(false) else 'No' }}"
|
||||||
|
- "Patch failed flag: {{ patch_failed_host | default(false) }}"
|
||||||
when:
|
when:
|
||||||
- kb_updates_file.stat.exists
|
- kb_updates_file.stat.exists
|
||||||
- kb_numbers is defined
|
- kb_numbers is defined
|
||||||
- kb_numbers | length > 0
|
- kb_numbers | length > 0
|
||||||
- installation_result is defined
|
|
||||||
|
|
||||||
- name: Reboot if required
|
- name: Reboot if required
|
||||||
win_reboot:
|
win_reboot:
|
||||||
reboot_timeout: 1800
|
reboot_timeout: 1800
|
||||||
when: installation_result.reboot_required | default(false)
|
when: installation_result is defined and (installation_result.reboot_required | default(false))
|
||||||
|
|
||||||
- name: Create installation report
|
- name: Create installation report
|
||||||
set_fact:
|
set_fact:
|
||||||
@@ -100,9 +112,9 @@
|
|||||||
Failed Installations: {{ installation_result.failed_update_count | default(0) }}
|
Failed Installations: {{ installation_result.failed_update_count | default(0) }}
|
||||||
Reboot Required: {{ installation_result.reboot_required | default('No') }}
|
Reboot Required: {{ installation_result.reboot_required | default('No') }}
|
||||||
|
|
||||||
Requested KB Numbers: {{ kb_numbers | join(', ') }}
|
Requested KB Numbers: {{ kb_numbers | default([]) | join(', ') }}
|
||||||
|
|
||||||
{% if installation_result.updates is defined %}
|
{% if installation_result is defined and installation_result.updates is defined %}
|
||||||
Installed Updates:
|
Installed Updates:
|
||||||
-----------------
|
-----------------
|
||||||
{% for update_id, update_info in installation_result.updates.items() %}
|
{% for update_id, update_info in installation_result.updates.items() %}
|
||||||
@@ -112,20 +124,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
when:
|
when:
|
||||||
- kb_updates_file.stat.exists
|
- kb_updates_file.stat.exists
|
||||||
- kb_numbers is defined
|
|
||||||
- kb_numbers | length > 0
|
|
||||||
- installation_result is defined
|
|
||||||
|
|
||||||
- name: Save installation report to file
|
- name: Save installation report to file
|
||||||
win_copy:
|
win_copy:
|
||||||
content: "{{ installation_summary }}"
|
content: "{{ installation_summary }}"
|
||||||
dest: 'C:\Temp\windows_update_installation_report.txt'
|
dest: 'C:\Temp\windows_update_installation_report.txt'
|
||||||
when:
|
when: installation_summary is defined
|
||||||
- kb_updates_file.stat.exists
|
|
||||||
- kb_numbers is defined
|
|
||||||
- kb_numbers | length > 0
|
|
||||||
- installation_result is defined
|
|
||||||
- installation_summary is defined
|
|
||||||
|
|
||||||
- name: Give a report when no KB numbers were found on updates
|
- name: Give a report when no KB numbers were found on updates
|
||||||
debug:
|
debug:
|
||||||
@@ -147,12 +151,10 @@
|
|||||||
site_id: "{{ lookup('env', 'SP_SITE_ID') }}"
|
site_id: "{{ lookup('env', 'SP_SITE_ID') }}"
|
||||||
list_id: "{{ lookup('env', 'SP_LIST_ID') }}"
|
list_id: "{{ lookup('env', 'SP_LIST_ID') }}"
|
||||||
|
|
||||||
# Helpful AWX vars (exist in AWX/Controller job context)
|
|
||||||
job_id: "{{ tower_job_id | default('n/a') }}"
|
job_id: "{{ tower_job_id | default('n/a') }}"
|
||||||
job_name: "{{ tower_job_template_name | default('Patch run') }}"
|
job_name: "{{ tower_job_template_name | default('Patch run') }}"
|
||||||
job_url: "{{ tower_job_url | default('') }}"
|
job_url: "{{ tower_job_url | default('') }}"
|
||||||
|
|
||||||
# Timestamps (works without gather_facts)
|
|
||||||
run_start: "{{ lookup('pipe','date -u +%Y-%m-%dT%H:%M:%SZ') }}"
|
run_start: "{{ lookup('pipe','date -u +%Y-%m-%dT%H:%M:%SZ') }}"
|
||||||
run_end: "{{ lookup('pipe','date -u +%Y-%m-%dT%H:%M:%SZ') }}"
|
run_end: "{{ lookup('pipe','date -u +%Y-%m-%dT%H:%M:%SZ') }}"
|
||||||
|
|
||||||
@@ -162,6 +164,30 @@
|
|||||||
URL={{ job_url }}.
|
URL={{ job_url }}.
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
- name: Aggregate per-host patch failures (from first play)
|
||||||
|
run_once: true
|
||||||
|
delegate_to: localhost
|
||||||
|
vars:
|
||||||
|
flags: "{{ ansible_play_hosts_all
|
||||||
|
| map('extract', hostvars, 'patch_failed_host')
|
||||||
|
| map('default', false) | list }}"
|
||||||
|
failed_hosts: >-
|
||||||
|
{{ ansible_play_hosts_all
|
||||||
|
| select('extract', hostvars, 'patch_failed_host')
|
||||||
|
| list
|
||||||
|
| select('equalto', true)
|
||||||
|
| list
|
||||||
|
| map('extract', hostvars, 'inventory_hostname')
|
||||||
|
| list }}
|
||||||
|
set_fact:
|
||||||
|
any_patch_failed: "{{ (flags | select('equalto', true) | list | length) > 0 }}"
|
||||||
|
failed_hosts_csv: "{{ failed_hosts | join(', ') if failed_hosts|length>0 else 'None' }}"
|
||||||
|
|
||||||
|
- name: Build final status (only from per-host flags)
|
||||||
|
run_once: true
|
||||||
|
set_fact:
|
||||||
|
status_final: "{{ 'failed' if (any_patch_failed | default(false)) else 'successful' }}"
|
||||||
|
|
||||||
- name: Acquire Graph token (client credentials)
|
- name: Acquire Graph token (client credentials)
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
run_once: true
|
run_once: true
|
||||||
@@ -179,66 +205,6 @@
|
|||||||
no_log: true
|
no_log: true
|
||||||
failed_when: graph_token.status not in [200]
|
failed_when: graph_token.status not in [200]
|
||||||
|
|
||||||
# --- AWX recap (controller's tallies) ---
|
|
||||||
- name: Fetch play recap (playbook_on_stats)
|
|
||||||
delegate_to: localhost
|
|
||||||
run_once: true
|
|
||||||
uri:
|
|
||||||
url: "{{ lookup('env','AWX_API_URL') }}/api/v2/jobs/{{ tower_job_id }}/job_events/?event=playbook_on_stats"
|
|
||||||
method: GET
|
|
||||||
headers:
|
|
||||||
Authorization: "Bearer {{ lookup('env','AWX_API_TOKEN') }}"
|
|
||||||
Content-Type: "application/json"
|
|
||||||
return_content: true
|
|
||||||
status_code: 200
|
|
||||||
register: _recap
|
|
||||||
no_log: true
|
|
||||||
|
|
||||||
- name: Parse recap totals
|
|
||||||
run_once: true
|
|
||||||
vars:
|
|
||||||
stats: "{{ (_recap.json.results | default([])) | first | default({}) }}"
|
|
||||||
data: "{{ stats.event_data | default({}) }}"
|
|
||||||
set_fact:
|
|
||||||
recap_ok: "{{ (data.ok | default({})) | dict2items | map(attribute='value') | map('int') | sum }}"
|
|
||||||
recap_changed: "{{ (data.changed | default({})) | dict2items | map(attribute='value') | map('int') | sum }}"
|
|
||||||
recap_failed: "{{ (data.failures | default({})) | dict2items | map(attribute='value') | map('int') | sum }}"
|
|
||||||
recap_skipped: "{{ (data.skipped | default({})) | dict2items | map(attribute='value') | map('int') | sum }}"
|
|
||||||
recap_unreach: "{{ (data.dark | default({})) | dict2items | map(attribute='value') | map('int') | sum }}"
|
|
||||||
|
|
||||||
- name: Build SharePoint recap line (store on localhost for later use)
|
|
||||||
run_once: true
|
|
||||||
delegate_to: localhost
|
|
||||||
delegate_facts: true
|
|
||||||
set_fact:
|
|
||||||
recap_line: >-
|
|
||||||
OK={{ recap_ok | default(0) }},
|
|
||||||
Changed={{ recap_changed | default(0) }},
|
|
||||||
Failed={{ recap_failed | default(0) }},
|
|
||||||
Unreachable={{ recap_unreach | default(0) }}
|
|
||||||
|
|
||||||
# --- Aggregate per-host patch failures set in the first play ---
|
|
||||||
- name: Aggregate per-host patch failures
|
|
||||||
run_once: true
|
|
||||||
delegate_to: localhost
|
|
||||||
vars:
|
|
||||||
flags: "{{ ansible_play_hosts_all
|
|
||||||
| map('extract', hostvars, 'patch_failed_host')
|
|
||||||
| map('default', false) | list }}"
|
|
||||||
set_fact:
|
|
||||||
any_patch_failed: "{{ (flags | select('equalto', true) | list | length) > 0 }}"
|
|
||||||
|
|
||||||
- name: Build final status from recap + per-host flags
|
|
||||||
run_once: true
|
|
||||||
set_fact:
|
|
||||||
status_final: >-
|
|
||||||
{{ 'failed'
|
|
||||||
if (any_patch_failed | default(false))
|
|
||||||
or (recap_failed | int > 0)
|
|
||||||
or (recap_unreach | int > 0)
|
|
||||||
else 'successful' }}
|
|
||||||
|
|
||||||
# --- Create list item (Graph) ---
|
|
||||||
- name: Create SharePoint list item (Graph)
|
- name: Create SharePoint list item (Graph)
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
run_once: true
|
run_once: true
|
||||||
@@ -259,14 +225,7 @@
|
|||||||
RunEnd: "{{ run_end }}"
|
RunEnd: "{{ run_end }}"
|
||||||
Notes: |-
|
Notes: |-
|
||||||
{{ summary_text }}
|
{{ summary_text }}
|
||||||
Recap: {{ hostvars['localhost'].recap_line }}
|
Failed hosts: {{ failed_hosts_csv }}
|
||||||
Failed hosts: {% set first=true -%}
|
|
||||||
{%- for h in ansible_play_hosts_all -%}
|
|
||||||
{%- if hostvars[h].patch_failed_host | default(false) -%}
|
|
||||||
{{ '' if first else ', ' }}{{ h }}{% set first=false -%}
|
|
||||||
{%- endif -%}
|
|
||||||
{%- endfor -%}
|
|
||||||
{%- if first -%}None{%- endif -%}
|
|
||||||
register: sp_create
|
register: sp_create
|
||||||
ignore_errors: true
|
ignore_errors: true
|
||||||
no_log: true
|
no_log: true
|
||||||
@@ -283,9 +242,6 @@
|
|||||||
{{ _json.error.message
|
{{ _json.error.message
|
||||||
| default(_json.message
|
| default(_json.message
|
||||||
| default(sp_create.msg | default('Unknown error'))) }}
|
| default(sp_create.msg | default('Unknown error'))) }}
|
||||||
hint: >
|
|
||||||
400: column internal names; 401: scope/audience; 403: permissions;
|
|
||||||
404: siteId/listId.
|
|
||||||
|
|
||||||
- name: Fail if SharePoint item was not created
|
- name: Fail if SharePoint item was not created
|
||||||
run_once: true
|
run_once: true
|
||||||
|
|||||||
Reference in New Issue
Block a user