#Requires -Version 3.0 <# app-client-utils — діагностика, крок 2. Мета: довести або спростувати, що задача падає на batch-логоні (SeBatchLogonRight), і з'ясувати, хто/коли забирає право. Нічого не змінює. Паролі не читає. Події читаються за ІНДЕКСАМИ полів, а не за текстом: станція локалізована, і пошук за 'Logon Type' у першій версії дав порожнечу. Результат: C:\tl-diag2-.txt #> $ErrorActionPreference = 'SilentlyContinue' $TaskName = 'TerraLab app-client-utils' $TaskFile = Join-Path $env:SystemRoot "System32\Tasks\$TaskName" $DataDir = Join-Path $env:ProgramData 'app-client-utils' $Report = Join-Path $env:SystemDrive "tl-diag2-$env:COMPUTERNAME.txt" $Lines = New-Object System.Collections.Generic.List[string] function W([string]$s = '') { $Lines.Add($s) } function Section([string]$t) { W ''; W ('=' * 72); W "== $t"; W ('=' * 72) } # Акаунт друку. Питаємо, бо задачі зараз немає і взяти його нізвідки. $PrintAcct = $env:TL_PRINT_ACCOUNT if (-not $PrintAcct) { Write-Host '' Write-Host 'Введіть назву облікового запису друку (той, що вводили в інсталяторі).' -ForegroundColor Cyan Write-Host 'Наприклад: mo_dp_khorobrogo або cl\mo_dp_khorobrogo' -ForegroundColor DarkGray $PrintAcct = Read-Host 'Акаунт' } Section 'ЗАГАЛЬНЕ' W "час звіту : $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss K')" W "комп'ютер : $env:COMPUTERNAME" W "запущено як : $(whoami)" $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) W "адмін : $isAdmin" if (-not $isAdmin) { W '!!! ЗАПУСТІТЬ ВІД ІМЕНІ АДМІНІСТРАТОРА — без цього немає ні Security-логу, ні secedit.' } # CIM замість WMI: у першому звіті Win32_ComputerSystem мовчки повернув порожнечу. $cs = Get-CimInstance Win32_ComputerSystem $os = Get-CimInstance Win32_OperatingSystem W "у домені : $($cs.PartOfDomain)" W "домен : $($cs.Domain)" W "NetBIOS-домен машини: $env:USERDOMAIN" W "ОС : $($os.Caption) build $($os.BuildNumber)" W "остання загрузка: $($os.LastBootUpTime)" W "зараз аптайм годин: $([math]::Round(((Get-Date) - $os.LastBootUpTime).TotalHours,1))" W "акаунт друку (введений): $PrintAcct" # ---------------------------------------------------------------- SID акаунта Section 'АКАУНТ ДРУКУ' $sid = $null try { $sid = (New-Object Security.Principal.NTAccount($PrintAcct) ).Translate([Security.Principal.SecurityIdentifier]).Value W "SID: $sid" } catch { W "SID: НЕ РОЗІМЕНОВАНО — '$PrintAcct' не існує або вказано не той формат." } W '' W 'net user (групи, стан пароля):' W ((net user $PrintAcct.Split('\')[-1] /domain 2>&1 | Out-String).Trim()) W '' W '(якщо /domain не спрацював — локальний акаунт:)' W ((net user $PrintAcct.Split('\')[-1] 2>&1 | Out-String).Trim()) # ---------------------------------------------------------------- право Section 'SeBatchLogonRight — ЧИ Є ПРАВО В АКАУНТА' if ($isAdmin) { $inf = Join-Path $env:TEMP 'tl-ur2.inf' secedit /export /areas USER_RIGHTS /cfg $inf 2>&1 | Out-Null $line = (Select-String -Path $inf -Pattern '^SeBatchLogonRight').Line $deny = (Select-String -Path $inf -Pattern '^SeDenyBatchLogonRight').Line Remove-Item $inf -Force W "надано : $line" W "заборона: $(if ($deny) { $deny } else { '(не задано)' })" W '' if ($sid -and $line -match [regex]::Escape($sid)) { W ">>> Акаунт ПРИСУТНІЙ у SeBatchLogonRight напряму." } elseif ($sid) { W ">>> Акаунта НЕМАЄ у SeBatchLogonRight напряму. Право може прийти лише через групу." } W '' W 'розшифровка перелічених SID:' foreach ($t in (($line -split '=')[1] -split ',')) { $t = $t.Trim(); if (-not $t) { continue } $n = $t try { if ($t.StartsWith('*')) { $n = (New-Object Security.Principal.SecurityIdentifier($t.TrimStart('*')) ).Translate([Security.Principal.NTAccount]).Value } } catch {} W " $t -> $n" } } else { W '(пропущено — немає прав адміна)' } Section 'ХТО ВИЗНАЧАЄ ЦЕ ПРАВО: локальна політика чи доменний GPO' W 'Якщо право приходить із GPO, Планувальник не може його втримати:' W 'він дописує локальну політику, а наступний refresh (~90 хв) її затирає.' W '' W ((gpresult /r /scope:computer 2>&1 | Out-String).Trim()) # ---------------------------------------------------------------- задача Section 'ЗАДАЧА' W "файл $TaskFile : $(if (Test-Path -LiteralPath $TaskFile) {'Є'} else {'НЕМАЄ'})" $t = Get-ScheduledTask -TaskName $TaskName if ($t) { W "UserId : $($t.Principal.UserId)" W "LogonType: $($t.Principal.LogonType)" W "стан : $($t.State)" $i = $t | Get-ScheduledTaskInfo W ("останній код: {0} (0x{1:X8})" -f $i.LastTaskResult, $i.LastTaskResult) W "останній запуск: $($i.LastRunTime)" } else { W 'Задачі зараз НЕМАЄ (очікувано, якщо інсталятор впав на /Create).' } Section 'ЖУРНАЛ ПЛАНУВАЛЬНИКА' $logName = 'Microsoft-Windows-TaskScheduler/Operational' W "стан журналу: $((wevtutil gl $logName 2>&1 | Select-String 'enabled') -join ' ')" $ev = Get-WinEvent -LogName $logName -MaxEvents 400 -ErrorAction SilentlyContinue | Where-Object { $_.Message -like '*app-client-utils*' -or $_.Message -like '*TerraLab*' } | Select-Object -First 30 if ($ev) { foreach ($e in $ev) { W "[$($e.TimeCreated)] id=$($e.Id) $($e.LevelDisplayName) $(($e.Message -split "`r?`n")[0])" } } else { W '(жодної події про нашу задачу)' W 'Якщо журнал вимкнено — увімкнути можна так (це ЗМІНА, робіть лише за погодженням):' W ' wevtutil sl Microsoft-Windows-TaskScheduler/Operational /e:true' } # ---------------------------------------------------------------- 4625, за індексами Section 'ВІДМОВИ ЛОГОНУ 4625 — розбір за полями, не за текстом' W 'Ключ: LogonType=4 (batch) + SubStatus=0xC000015B => право batch logon відсутнє.' W ' SubStatus=0xC000006A => невірний пароль. 0xC0000064 => немає такого юзера.' W '' if ($isAdmin) { $f = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625} -MaxEvents 40 if (-not $f) { W '(подій 4625 немає)' } foreach ($e in $f) { $p = $e.Properties if ($p.Count -lt 12) { W "[$($e.TimeCreated)] (подія з $($p.Count) полів - схема інша, пропущено)"; continue } # Схема 4625: 5=TargetUserName 6=TargetDomainName 7=Status 9=SubStatus 10=LogonType 11=LogonProcess $lt = [int]$p[10].Value $st = [uint32]$p[7].Value $sub = [uint32]$p[9].Value $usr = $p[5].Value $dom = $p[6].Value $proc = $p[11].Value $subHex = '0x{0:X8}' -f $sub $mark = '' if ($lt -eq 4) { $mark += ' <<< BATCH' } if ($subHex -eq '0xC000015B') { $mark += ' <<< LOGON_TYPE_NOT_GRANTED' } if ($subHex -eq '0xC000006A') { $mark += ' <<< BAD_PASSWORD' } W ('[{0}] user={1}\{2} LogonType={3} Status=0x{4:X8} SubStatus={5} proc={6}{7}' -f ` $e.TimeCreated, $dom, $usr, $lt, $st, $subHex, $proc, $mark) } W '' W '--- успішні batch-логони (4624, LogonType=4) для порівняння ---' $s = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4624} -MaxEvents 200 | Where-Object { $_.Properties[8].Value -eq 4 } | Select-Object -First 10 if ($s) { foreach ($e in $s) { W "[$($e.TimeCreated)] $($e.Properties[6].Value)\$($e.Properties[5].Value)" } } else { W '(жодного успішного batch-логону в межах вибірки)' } } else { W '(пропущено — немає прав адміна)' } Section 'ПОДІЇ ПРО СТВОРЕННЯ/ВИДАЛЕННЯ ЗАДАЧ (4698 / 4699)' if ($isAdmin) { $ta = Get-WinEvent -FilterHashtable @{LogName='Security'; Id=@(4698,4699,4700,4701,4702)} -MaxEvents 40 | Where-Object { $_.Message -like '*app-client-utils*' -or $_.Message -like '*TerraLab*' } if ($ta) { foreach ($e in $ta) { W "[$($e.TimeCreated)] id=$($e.Id) $(($e.Message -split "`r?`n")[0])" } } else { W '(немає; аудит "Other Object Access Events" зазвичай вимкнений — це нормально)' } } else { W '(пропущено)' } # ---------------------------------------------------------------- логи агента Section 'LAUNCHER.LOG — усі рядки про старт/вихід (не хвіст, а вибірка)' $ll = Join-Path $DataDir 'logs\launcher.log' if (Test-Path $ll) { W "розмір: $((Get-Item $ll).Length) байт, змінено: $((Get-Item $ll).LastWriteTime)" W '' W ((Get-Content $ll | Select-String -Pattern 'starting agent|agent exited' | Select-Object -Last 30 | Out-String).TrimEnd()) W '' W '--- останні 15 рядків цілком ---' W ((Get-Content $ll -Tail 15 | Out-String).TrimEnd()) } else { W 'launcher.log немає.' } Section 'APP-CLIENT-UTILS.LOG — останні 25 рядків' $al = Join-Path $DataDir 'logs\app-client-utils.log' if (Test-Path $al) { W ((Get-Content $al -Tail 25 | Out-String).TrimEnd()) } else { W '(немає)' } Section 'СТАН ЗАРАЗ' $javaw = Get-CimInstance Win32_Process -Filter "Name='javaw.exe'" | Where-Object { $_.CommandLine -like '*app-client-utils*' } if ($javaw) { foreach ($p in $javaw) { W "javaw pid=$($p.ProcessId): $($p.CommandLine)" } } else { W 'javaw з нашим jar не працює.' } W "слухачі 8089: $((netstat -ano | Select-String ':8089' | Out-String).Trim())" # ---------------------------------------------------------------- висновок Section 'ПІДСУМОК' W 'Читати так:' W ' * є 4625 з LogonType=4 і SubStatus=0xC000015B -> право batch logon відсутнє. Діагноз підтверджено.' W ' * є 4625 з SubStatus=0xC000006A -> справа в паролі, не в правах.' W ' * у gpresult видно GPO, що задає User Rights -> локальний грант не виживе, треба правити доменну політику.' $Lines | Out-File -FilePath $Report -Encoding UTF8 Write-Host '' Write-Host '========================================================' -ForegroundColor Green Write-Host " Звіт: $Report" -ForegroundColor Green Write-Host ' Надішліть цей файл. Паролів у ньому немає.' -ForegroundColor Green Write-Host '========================================================' -ForegroundColor Green