diff --git a/www-install-win-updates.yaml b/www-install-win-updates.yaml index ec07be5..84f5771 100644 --- a/www-install-win-updates.yaml +++ b/www-install-win-updates.yaml @@ -2,6 +2,7 @@ - name: Windows Update Installation from Assessment Report hosts: windows gather_facts: no + tasks: - name: Get current timestamp set_fact: @@ -43,27 +44,38 @@ - kb_numbers is defined - kb_numbers | length > 0 - - name: Install Windows updates by KB numbers - win_updates: - category_names: '*' - state: installed - accept_list: "{{ kb_numbers }}" - log_path: 'C:\Temp\windows_update_installation.log' - register: installation_result - when: - - kb_updates_file.stat.exists - - kb_numbers is defined - - kb_numbers | length > 0 + # ---- Patch with failure capture (block/rescue) ---- + - name: Install Windows updates by KB numbers (with failure capture) + block: + - name: Install Windows updates by KB numbers + win_updates: + category_names: '*' + state: installed + accept_list: "{{ kb_numbers }}" + log_path: 'C:\Temp\windows_update_installation.log' + register: installation_result + when: + - kb_updates_file.stat.exists + - kb_numbers is defined + - kb_numbers | length > 0 - - name: Mark host failed if any KB installs failed - set_fact: - patch_failed_host: "{{ (installation_result.failed_update_count | default(0) | int) > 0 }}" - patch_failed_count: "{{ installation_result.failed_update_count | default(0) | int }}" - when: - - kb_updates_file.stat.exists - - kb_numbers is defined - - kb_numbers | length > 0 - - installation_result is defined + - name: Mark host failed if any KB installs failed (module reported partial failures) + set_fact: + patch_failed_host: "{{ (installation_result.failed_update_count | default(0) | int) > 0 }}" + patch_failed_count: "{{ installation_result.failed_update_count | default(0) | int }}" + when: installation_result is defined + + rescue: + - name: Mark host failed because win_updates task error/exception + 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 debug: @@ -74,16 +86,16 @@ - "Updates Installed: {{ installation_result.installed_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' }}" + - "Patch failed flag: {{ patch_failed_host | default(false) }}" when: - kb_updates_file.stat.exists - kb_numbers is defined - kb_numbers | length > 0 - - installation_result is defined - name: Reboot if required win_reboot: 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 set_fact: @@ -100,9 +112,9 @@ Failed Installations: {{ installation_result.failed_update_count | default(0) }} 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: ----------------- {% for update_id, update_info in installation_result.updates.items() %} @@ -112,20 +124,12 @@ {% endif %} when: - kb_updates_file.stat.exists - - kb_numbers is defined - - kb_numbers | length > 0 - - installation_result is defined - name: Save installation report to file win_copy: content: "{{ installation_summary }}" dest: 'C:\Temp\windows_update_installation_report.txt' - when: - - kb_updates_file.stat.exists - - kb_numbers is defined - - kb_numbers | length > 0 - - installation_result is defined - - installation_summary is defined + when: installation_summary is defined - name: Give a report when no KB numbers were found on updates debug: @@ -147,12 +151,10 @@ site_id: "{{ lookup('env', 'SP_SITE_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_name: "{{ tower_job_template_name | default('Patch run') }}" job_url: "{{ tower_job_url | default('') }}" - # Timestamps (works without gather_facts) run_start: "{{ 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 }}. 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) delegate_to: localhost run_once: true @@ -179,66 +205,6 @@ no_log: true 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) delegate_to: localhost run_once: true @@ -259,14 +225,7 @@ RunEnd: "{{ run_end }}" Notes: |- {{ summary_text }} - Recap: {{ hostvars['localhost'].recap_line }} - 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 -%} + Failed hosts: {{ failed_hosts_csv }} register: sp_create ignore_errors: true no_log: true @@ -283,9 +242,6 @@ {{ _json.error.message | default(_json.message | 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 run_once: true