--- - name: Patching | Initialize Windows report state ansible.builtin.set_fact: _aim_patch_action_failed: false _aim_patch_pre_reboot_performed: false _aim_patch_any_reboot_performed: false _aim_patch_preexisting_reboot_required: false _aim_patch_preexisting_reboot_reasons: [] _aim_patch_reboot_deferred: false _aim_patch_reboot_required_after: false _aim_patch_blocked_reason: null _aim_patch_continuation_required: false _aim_patch_remaining_updates_known: false _aim_patch_done: false _aim_patch_cycles: 0 _aim_windows_update_runs: [] _aim_windows_searches: [] - name: Patching | Detect pending Windows reboot before patching ansible.windows.win_reboot_info: register: _aim_patch_pre_reboot_probe - name: Patching | Record pre-existing Windows reboot state ansible.builtin.set_fact: _aim_patch_preexisting_reboot_required: '{{ _aim_patch_pre_reboot_probe.reboot_required | default(false) | bool }}' _aim_patch_preexisting_reboot_reasons: '{{ _aim_patch_pre_reboot_probe.reboot_required_reasons | default([]) }}' - name: Patching | Publish blocked Windows result when reboot is deferred when: - _aim_patch_preexisting_reboot_required | bool - not (os_patching_reboot | bool) block: - name: Patching | Build blocked Windows patch report ansible.builtin.set_fact: _aim_patch_report: >- {{ 'windows' | aim_report_patch_blocked(ansible_check_mode, os_patching_reboot_delay_minutes | int, os_patching_rescan_after_reboot | bool, _aim_patch_preexisting_reboot_reasons) }} - name: AIM | Publish blocked operation result ansible.builtin.set_stats: per_host: true aggregate: false data: aim_output: protocol: aim_output_v1 schema: patch_summary_v1 data: '{{ _aim_patch_report }}' - name: Patching | Require reboot before continuing Windows patching ansible.builtin.fail: msg: >- A reboot is already pending from a previous update or installation. Reboot the host first, or rerun with "Reboot when required" enabled. No new Windows updates were started by this run. - name: Patching | Clear pre-existing Windows reboot before patching ansible.windows.win_reboot: msg: '{{ os_patching_reboot_message }}' pre_reboot_delay: '{{ [2, (os_patching_reboot_delay_minutes | int) * 60] | max }}' reboot_timeout: '{{ os_patching_reboot_timeout | int }}' register: _aim_patch_pre_reboot when: - _aim_patch_preexisting_reboot_required | bool - os_patching_reboot | bool - not ansible_check_mode - name: Patching | Record pre-patch Windows reboot ansible.builtin.set_fact: _aim_patch_pre_reboot_performed: '{{ _aim_patch_pre_reboot.rebooted | default(false) | bool }}' _aim_patch_any_reboot_performed: '{{ _aim_patch_pre_reboot.rebooted | default(false) | bool }}' _aim_patch_done: >- {{ (_aim_patch_pre_reboot.rebooted | default(false) | bool) and not (os_patching_rescan_after_reboot | bool) }} _aim_patch_continuation_required: >- {{ (_aim_patch_pre_reboot.rebooted | default(false) | bool) and not (os_patching_rescan_after_reboot | bool) }} _aim_patch_remaining_updates_known: false - name: Patching | Search Windows updates in check mode ansible.windows.win_updates: category_names: '{{ os_patching_windows_categories }}' state: searched reboot: false register: _aim_windows_check_search when: - ansible_check_mode - not (_aim_patch_done | bool) - name: Patching | Record Windows check-mode search ansible.builtin.set_fact: _aim_windows_searches: '{{ [_aim_windows_check_search] }}' _aim_patch_remaining_updates_known: true _aim_patch_continuation_required: '{{ (_aim_windows_check_search.found_update_count | default(0) | int) > 0 }}' _aim_patch_done: true when: - ansible_check_mode - _aim_windows_check_search is defined - not (_aim_windows_check_search.skipped | default(false) | bool) - name: Patching | Process Windows patch waves ansible.builtin.include_tasks: windows_wave.yml loop: >- {{ (range(1, 13) | list) if (os_patching_rescan_after_reboot | bool) else [1] }} loop_control: loop_var: _aim_patch_wave_number label: 'Windows patch wave {{ _aim_patch_wave_number }}' when: - not ansible_check_mode - not (_aim_patch_done | bool) - name: Patching | Guard automatic continuation wave limit ansible.builtin.set_fact: _aim_patch_action_failed: true _aim_patch_blocked_reason: cycle_limit_reached _aim_patch_continuation_required: true when: - not ansible_check_mode - os_patching_rescan_after_reboot | bool - not (_aim_patch_done | bool) - name: Patching | Normalize Windows update results ansible.builtin.set_fact: _aim_patch_report: >- {{ _aim_windows_update_runs | aim_report_patch_windows_runs( _aim_windows_searches, ansible_check_mode, _aim_patch_preexisting_reboot_required | bool, _aim_patch_any_reboot_performed | bool, os_patching_reboot | bool, os_patching_reboot_delay_minutes | int, os_patching_rescan_after_reboot | bool, _aim_patch_cycles | int, _aim_patch_continuation_required | bool, _aim_patch_remaining_updates_known | bool, _aim_patch_reboot_deferred | bool, _aim_patch_reboot_required_after, _aim_patch_blocked_reason, not (_aim_patch_action_failed | bool), _aim_patch_preexisting_reboot_reasons ) }} - name: Patching | Update summary ansible.builtin.debug: msg: '{{ _aim_patch_report }}' - name: AIM | Publish operation result ansible.builtin.set_stats: per_host: true aggregate: false data: aim_output: protocol: aim_output_v1 schema: patch_summary_v1 data: '{{ _aim_patch_report }}' - name: Patching | Preserve Windows update failure ansible.builtin.fail: msg: >- Windows patching stopped before the approved patch wave completed. The structured result contains successfully installed updates, bounded failed-update reasons when available, and whether another operator-approved run is required. AIM did not replay the patch job automatically. when: _aim_patch_action_failed | bool