diff --git a/patch-dc-controllers.yaml b/patch-dc-controllers.yaml index a638c61..bb08852 100644 --- a/patch-dc-controllers.yaml +++ b/patch-dc-controllers.yaml @@ -4,47 +4,21 @@ gather_facts: no vars: - task_path: '\' # or e.g. '\\Microsoft\\Windows\\WindowsUpdate\\' + task_path: '\\' # root folder task_name: 'Patching-windows-task' - poll_delay: 60 # seconds - start_retries: 30 # up to 30 minutes to confirm it started - finish_retries: 3 # up to 6 hours to finish - winrm_port: 5986 + poll_delay: 60 + finish_retries: 3 # up to 6h tasks: - - name: Verify task exists and capture pre-start info - ansible.windows.win_powershell: - script: | - $ErrorActionPreference = 'Stop' - Import-Module ScheduledTasks - $tp='{{ task_path }}'; $tn='{{ task_name }}' - $t = Get-ScheduledTask -TaskPath $tp -TaskName $tn - $i = Get-ScheduledTaskInfo -TaskPath $tp -TaskName $tn - [PSCustomObject]@{ - Exists = $true - State = $i.State - LastRunTime = $i.LastRunTime - LastTaskResult= $i.LastTaskResult - Enabled = $t.Settings.Enabled - } | ConvertTo-Json -Compress - register: pre_info - - - name: Fail if task not found - ansible.builtin.fail: - msg: "Task '{{ task_path }}{{ task_name }}' not found." - when: pre_info.stdout | default('') == '' - - - name: Ensure task is enabled + - name: Ensure the task is enabled ansible.windows.win_powershell: script: | Import-Module ScheduledTasks $tp='{{ task_path }}'; $tn='{{ task_name }}' $t = Get-ScheduledTask -TaskPath $tp -TaskName $tn if (-not $t.Settings.Enabled) { Enable-ScheduledTask -TaskPath $tp -TaskName $tn | Out-Null } - register: enable_task - failed_when: false - - name: Start the SYSTEM patch task (schtasks) + - name: Start the SYSTEM patch task ansible.windows.win_command: > schtasks /Run /TN "{{ task_path }}{{ task_name }}" register: start_task @@ -53,80 +27,24 @@ (start_task.rc | default(1)) == 0 or ('SUCCESS' in (start_task.stdout | default(''))) - # Confirm the task actually started (LastRunTime advanced) - - name: Wait until LastRunTime changes (task actually started) + - name: Poll until Ready/Disabled with success ansible.windows.win_powershell: script: | + $ErrorActionPreference = 'Stop' Import-Module ScheduledTasks $tp='{{ task_path }}'; $tn='{{ task_name }}' - (Get-ScheduledTaskInfo -TaskPath $tp -TaskName $tn).LastRunTime.ToString('o') - register: started_info - retries: "{{ start_retries }}" + $i = Get-ScheduledTaskInfo -TaskPath $tp -TaskName $tn + [PSCustomObject]@{ State=$i.State; LastTaskResult=$i.LastTaskResult } | ConvertTo-Json -Compress + register: task_info + failed_when: false + retries: "{{ finish_retries }}" delay: "{{ poll_delay }}" until: > - (started_info.stdout | default('') | length > 0) and - (started_info.stdout != (pre_info.stdout | from_json).LastRunTime) - - # Long poll until the task finishes successfully - - name: Poll until task is Ready/Disabled with LastTaskResult 0 - block: - - name: Get current task state/result - ansible.windows.win_powershell: - script: | - $ErrorActionPreference = 'Stop' - Import-Module ScheduledTasks - $tp='{{ task_path }}'; $tn='{{ task_name }}' - $i = Get-ScheduledTaskInfo -TaskPath $tp -TaskName $tn - [PSCustomObject]@{ - State = $i.State - LastTaskResult = $i.LastTaskResult - LastRunTime = $i.LastRunTime - } | ConvertTo-Json -Compress - register: task_info - failed_when: false - retries: "{{ finish_retries }}" - delay: "{{ poll_delay }}" - until: > - (task_info.stdout | default('') | length > 0) - and ((task_info.stdout | from_json).State in ['Ready','Disabled']) - and (((task_info.stdout | from_json).LastTaskResult | int) == 0) - - rescue: - # If the node rebooted and PSRP dropped, wait for WinRM then check once more - - name: Wait for WinRM after possible reboot - ansible.windows.win_wait_for: - port: "{{ winrm_port }}" - timeout: 1800 - - - name: Final check after reconnect - ansible.windows.win_powershell: - script: | - Import-Module ScheduledTasks - $tp='{{ task_path }}'; $tn='{{ task_name }}' - $i = Get-ScheduledTaskInfo -TaskPath $tp -TaskName $tn - [PSCustomObject]@{ State=$i.State; LastTaskResult=$i.LastTaskResult; LastRunTime=$i.LastRunTime } | - ConvertTo-Json -Compress - register: task_info - failed_when: false - - - name: Show final task state/result - ansible.builtin.debug: - msg: - - "Final State: {{ (task_info.stdout | default('{}') | from_json).State | default('n/a') }}" - - "LastTaskResult: {{ (task_info.stdout | default('{}') | from_json).LastTaskResult | default('n/a') }}" - - "LastRunTime: {{ (task_info.stdout | default('{}') | from_json).LastRunTime | default('n/a') }}" - - - name: Fail if task ended but did not return 0 - ansible.builtin.fail: - msg: > - Task finished in state {{ (task_info.stdout | from_json).State }} - with LastTaskResult {{ (task_info.stdout | from_json).LastTaskResult }} (non-zero). - when: > (task_info.stdout | default('') | length > 0) and ((task_info.stdout | from_json).State in ['Ready','Disabled']) - and (((task_info.stdout | from_json).LastTaskResult | int) != 0) + and (((task_info.stdout | from_json).LastTaskResult | int) == 0) - - name: Reboot if ready (belt & suspenders) + - name: Reboot if needed ansible.windows.win_reboot: reboot_timeout: 5400 when: (task_info.stdout | from_json).State == 'Ready'