From 24bcd498eae772f48e4ea1c7fd297aebcfa3ee7f Mon Sep 17 00:00:00 2001 From: "mhorak@totalservice.cz" Date: Tue, 19 Aug 2025 17:19:44 +0200 Subject: [PATCH] task --- patch-dc-controllers.yaml | 113 +++++++++++++++++++++++++++++--------- 1 file changed, 88 insertions(+), 25 deletions(-) diff --git a/patch-dc-controllers.yaml b/patch-dc-controllers.yaml index fda607d..c222ba6 100644 --- a/patch-dc-controllers.yaml +++ b/patch-dc-controllers.yaml @@ -1,55 +1,118 @@ --- -- name: Run DC patch task via JEA-PatchOps +- name: Diagnose & start patch task via JEA-PatchOps hosts: domain_controllers gather_facts: no vars: - task_path: '\\' # root folder - task_name: 'Patching-windows-task' - poll_delay: 60 - finish_retries: 3 # up to 6h + task_path: '\\' # root + task_name: 'Patching-windows-task' # your task + poll_delay: 30 + finish_retries: 3 # up to 2h tasks: - - name: Ensure the task is enabled + - name: Show effective identity (who JEA runs as) + ansible.windows.win_powershell: + script: "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name" + register: whoami + + - name: Read task details (state/enabled/last run) + 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]@{ + Enabled = $t.Settings.Enabled + State = $i.State + LastRunTime = $i.LastRunTime + LastTaskResult= $i.LastTaskResult + Actions = ($t.Actions | Select-Object -ExpandProperty Execute -ErrorAction SilentlyContinue) -join ';' + RunAs = $t.Principal.UserId + } | ConvertTo-Json -Compress + register: pre + + - name: Compute on-disk task file path (C:\Windows\System32\Tasks\...) + ansible.windows.win_powershell: + script: | + $tp='{{ task_path }}'.Trim() + if (-not $tp.EndsWith('\')) { $tp += '\' } + $rel = $tp.Trim('\') -replace '\\','\' + if ([string]::IsNullOrEmpty($rel)) { $rel = '' } + $p = Join-Path 'C:\Windows\System32\Tasks' (Join-Path $rel '{{ task_name }}') + $p + register: taskfile + + - name: Show ACL on the task file + ansible.windows.win_powershell: + script: | + $p='{{ taskfile.stdout | trim }}' + if (Test-Path $p) { (Get-Acl $p).Access | Select IdentityReference,FileSystemRights,AccessControlType | Out-String } else { "MISSING: $p" } + register: aclinfo + + - name: Ensure JEA RunAs group can Start (RX,WD) the task file (adjust your group!) + ansible.windows.win_command: > + icacls "{{ taskfile.stdout | trim }}" + /grant "TS-LABS25\SEC-PatchOperators:(RX,WD)" + register: aclgrant + changed_when: "'processed' in (aclgrant.stdout | default(''))" + failed_when: false + + - name: Enable task if disabled 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: enabled + failed_when: false - - name: Start the SYSTEM patch task + - name: Start via schtasks.exe (most reliable under JEA) ansible.windows.win_command: > schtasks /Run /TN "{{ task_path }}{{ task_name }}" - register: start_task + register: start_out failed_when: false changed_when: > - (start_task.rc | default(1)) == 0 - or ('SUCCESS' in (start_task.stdout | default(''))) + (start_out.rc | default(1)) == 0 + or ('SUCCESS' in (start_out.stdout | default(''))) - - name: Poll until Ready/Disabled with LastTaskResult 0 + - name: Confirm it actually started (LastRunTime changed) + ansible.windows.win_powershell: + script: | + Import-Module ScheduledTasks + $tp='{{ task_path }}'; $tn='{{ task_name }}' + (Get-ScheduledTaskInfo -TaskPath $tp -TaskName $tn).LastRunTime.ToString('o') + register: started + retries: 20 + delay: 15 + until: > + (started.stdout | default('') | length > 0) and + (started.stdout != ((pre.stdout | default('{}') | from_json).LastRunTime)) + + - name: Poll to finish (Ready/Disabled) and success (0 or 3010) 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 + [PSCustomObject]@{ State=$i.State; LastTaskResult=$i.LastTaskResult; LastRunTime=$i.LastRunTime } | ConvertTo-Json -Compress + register: info failed_when: false retries: "{{ finish_retries }}" delay: "{{ poll_delay }}" until: > - ((task_info.stdout | default('')) | length > 0) - and (((task_info.stdout | default('{}')) | from_json).State in ['Ready','Disabled']) - and ((((task_info.stdout | default('{}')) | from_json).LastTaskResult | int) in [0,3010]) + (info.stdout | default('') | length > 0) + and ((info.stdout | from_json).State in ['Ready','Disabled']) + and (((info.stdout | from_json).LastTaskResult | int) in [0,3010]) - - name: Parse task info safely - ansible.builtin.set_fact: - task_info_parsed: "{{ (task_info.stdout | default('{}')) | from_json }}" - - - name: Reboot if needed (state Ready) - ansible.windows.win_reboot: - reboot_timeout: 5400 - when: task_info_parsed.State == 'Ready' + - name: Debug summary + ansible.builtin.debug: + msg: + - "RunAs (JEA): {{ whoami.stdout | default('n/a') }}" + - "Task file: {{ taskfile.stdout | trim }}" + - "ACL before grant (snippet): {{ aclinfo.stdout | default('n/a') | regex_replace('\\s+$','') }}" + - "Start output rc/stdout: {{ start_out.rc | default('n/a') }} / {{ start_out.stdout | default('') | trim }}" + - "Final State/Result: {{ (info.stdout | from_json).State }} / {{ (info.stdout | from_json).LastTaskResult }}"