fix: Thumbprint при одном cert после нативного сброса (PS 5.1 unwrap)
Добавлены Get-RdpRepairCertificateArray/FirstCertificate, самотесты на вложенные массивы; Fix-RdpTls больше не использует Select-Object -First 1 на cert. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -27,6 +27,8 @@ cd C:\path\to\fix-DAK-PC\scripts
|
|||||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\Invoke-RdpRepairSelfTest.ps1 -IncludeAdminTests
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\Invoke-RdpRepairSelfTest.ps1 -IncludeAdminTests
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Самотесты проверяют синтаксис, загрузку модулей, чтение cert store и ловушки PowerShell 5.1 с одним cert; **не** прогоняют полный `Fix-RdpTls.ps1` (очистку store, reboot, привязку).
|
||||||
|
|
||||||
5. **Полный интерактивный ремонт**:
|
5. **Полный интерактивный ремонт**:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ try {
|
|||||||
Write-RdpRepairLog 'No cert in Remote Desktop store - run full repair or repair install first' 'ERROR' $logPath
|
Write-RdpRepairLog 'No cert in Remote Desktop store - run full repair or repair install first' 'ERROR' $logPath
|
||||||
exit 2
|
exit 2
|
||||||
}
|
}
|
||||||
$valid = $certs | Where-Object { $_.NotAfter -gt (Get-Date) } | Select-Object -First 1
|
$valid = @(Get-RdpRepairCertificateArray $certs | Where-Object { $_.NotAfter -gt (Get-Date) })[0]
|
||||||
if (-not $valid) {
|
if (-not $valid) {
|
||||||
Write-RdpRepairLog 'Все cert просрочены' 'ERROR' $logPath
|
Write-RdpRepairLog 'Все cert просрочены' 'ERROR' $logPath
|
||||||
exit 2
|
exit 2
|
||||||
@@ -98,7 +98,7 @@ try {
|
|||||||
$snap = Get-RdpRepairDiagnosticSnapshot
|
$snap = Get-RdpRepairDiagnosticSnapshot
|
||||||
$snap | Format-List ComputerName, OsCaption, SecurityLayer, SslHash, CertCount, Events1057_24h, GpoLicenseSrv
|
$snap | Format-List ComputerName, OsCaption, SecurityLayer, SslHash, CertCount, Events1057_24h, GpoLicenseSrv
|
||||||
|
|
||||||
$expired = @($snap.Certificates | Where-Object { $_.NotAfter -lt (Get-Date) })
|
$expired = @(Get-RdpRepairCertificateArray $snap.Certificates | Where-Object { $_.NotAfter -lt (Get-Date) })
|
||||||
if ($expired.Count -gt 0) {
|
if ($expired.Count -gt 0) {
|
||||||
Write-RdpRepairLog "Найдено просроченных cert: $($expired.Count)" 'WARN' $logPath
|
Write-RdpRepairLog "Найдено просроченных cert: $($expired.Count)" 'WARN' $logPath
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,8 @@ try {
|
|||||||
$native = Invoke-RdpRepairNativeReset -LogPath $logPath
|
$native = Invoke-RdpRepairNativeReset -LogPath $logPath
|
||||||
if ($native.CertCount -gt 0) {
|
if ($native.CertCount -gt 0) {
|
||||||
Write-RdpRepairLog "После сброса без reboot: нативный cert найден ($($native.CertCount))" 'OK' $logPath
|
Write-RdpRepairLog "После сброса без reboot: нативный cert найден ($($native.CertCount))" 'OK' $logPath
|
||||||
$nativeCert = $native.Certificates | Select-Object -First 1
|
$nativeCert = Get-RdpRepairFirstCertificate -Certificates $native.Certificates
|
||||||
|
if (-not $nativeCert) { throw 'Native cert list empty after reset' }
|
||||||
$bindNative = Test-RdpRepairCertificateBindingPersisted -ExpectedThumbprint $nativeCert.Thumbprint -LogPath $logPath
|
$bindNative = Test-RdpRepairCertificateBindingPersisted -ExpectedThumbprint $nativeCert.Thumbprint -LogPath $logPath
|
||||||
if ($bindNative.Persisted) {
|
if ($bindNative.Persisted) {
|
||||||
Write-RdpRepairLog 'Native cert accepted - you can use RestoreTls' 'OK' $logPath
|
Write-RdpRepairLog 'Native cert accepted - you can use RestoreTls' 'OK' $logPath
|
||||||
|
|||||||
@@ -76,6 +76,30 @@ Assert-Test 'Get-RdpRepairRemoteDesktopCertificates exposes Count' {
|
|||||||
$null = $certs.Count
|
$null = $certs.Count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Assert-Test 'single cert in PSCustomObject keeps Thumbprint' {
|
||||||
|
$mock = [PSCustomObject]@{
|
||||||
|
Thumbprint = 'DEADBEEF'
|
||||||
|
Subject = 'CN=test'
|
||||||
|
NotAfter = (Get-Date).AddDays(30)
|
||||||
|
HasKey = $true
|
||||||
|
}
|
||||||
|
$box = [PSCustomObject]@{ Certificates = @($mock) }
|
||||||
|
$first = Get-RdpRepairFirstCertificate -Certificates $box.Certificates
|
||||||
|
if ($first.Thumbprint -ne 'DEADBEEF') { throw 'Thumbprint lost via property unwrap' }
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert-Test 'Get-RdpRepairCertificateArray flattens nested Object[]' {
|
||||||
|
$mock = [PSCustomObject]@{
|
||||||
|
Thumbprint = 'NESTED01'
|
||||||
|
Subject = 'CN=nested'
|
||||||
|
NotAfter = (Get-Date).AddDays(1)
|
||||||
|
HasKey = $true
|
||||||
|
}
|
||||||
|
$box = [PSCustomObject]@{ Certificates = ,[object[]]@(,[object[]]@($mock)) }
|
||||||
|
$first = Get-RdpRepairFirstCertificate -Certificates $box.Certificates
|
||||||
|
if ($first.Thumbprint -ne 'NESTED01') { throw "expected NESTED01 got $($first.Thumbprint)" }
|
||||||
|
}
|
||||||
|
|
||||||
Assert-Test 'Get-RdpRepairDiagnosticSnapshot object shape' {
|
Assert-Test 'Get-RdpRepairDiagnosticSnapshot object shape' {
|
||||||
if (-not (Test-RdpRepairIsAdministrator)) {
|
if (-not (Test-RdpRepairIsAdministrator)) {
|
||||||
Write-Host ' (skip snapshot fields - not admin)' -ForegroundColor DarkYellow
|
Write-Host ' (skip snapshot fields - not admin)' -ForegroundColor DarkYellow
|
||||||
@@ -85,6 +109,10 @@ Assert-Test 'Get-RdpRepairDiagnosticSnapshot object shape' {
|
|||||||
foreach ($prop in @('ComputerName', 'SecurityLayer', 'CertCount', 'TermService')) {
|
foreach ($prop in @('ComputerName', 'SecurityLayer', 'CertCount', 'TermService')) {
|
||||||
if (-not $s.PSObject.Properties[$prop]) { throw "missing property $prop" }
|
if (-not $s.PSObject.Properties[$prop]) { throw "missing property $prop" }
|
||||||
}
|
}
|
||||||
|
if ($s.CertCount -gt 0) {
|
||||||
|
$first = Get-RdpRepairFirstCertificate -Certificates $s.Certificates
|
||||||
|
if (-not $first.Thumbprint) { throw 'snapshot cert missing Thumbprint' }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert-Test 'Registry path constants well-formed' {
|
Assert-Test 'Registry path constants well-formed' {
|
||||||
|
|||||||
@@ -86,8 +86,30 @@ function Get-RdpRepairRemoteDesktopCertificates {
|
|||||||
} finally {
|
} finally {
|
||||||
$store.Close()
|
$store.Close()
|
||||||
}
|
}
|
||||||
# Запятая перед return: иначе PS 5.1 разворачивает массив из одного cert и ломает .Count
|
return [object[]]$list.ToArray()
|
||||||
return ,[object[]]$list.ToArray()
|
}
|
||||||
|
|
||||||
|
function Get-RdpRepairCertificateArray {
|
||||||
|
param([object]$Certificates)
|
||||||
|
if ($null -eq $Certificates) { return @() }
|
||||||
|
$flat = New-Object System.Collections.Generic.List[object]
|
||||||
|
foreach ($item in @($Certificates)) {
|
||||||
|
if ($item -is [object[]]) {
|
||||||
|
foreach ($inner in @($item)) {
|
||||||
|
if ($null -ne $inner) { [void]$flat.Add($inner) }
|
||||||
|
}
|
||||||
|
} elseif ($null -ne $item) {
|
||||||
|
[void]$flat.Add($item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [object[]]$flat.ToArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-RdpRepairFirstCertificate {
|
||||||
|
param([object]$Certificates)
|
||||||
|
$arr = @(Get-RdpRepairCertificateArray -Certificates $Certificates)
|
||||||
|
if ($arr.Count -lt 1) { return $null }
|
||||||
|
return $arr[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
function Clear-RdpRepairCertificateStores {
|
function Clear-RdpRepairCertificateStores {
|
||||||
@@ -258,7 +280,7 @@ function Invoke-RdpRepairNativeReset {
|
|||||||
return [PSCustomObject]@{
|
return [PSCustomObject]@{
|
||||||
RebootScheduled = $false
|
RebootScheduled = $false
|
||||||
CertCount = $certs.Count
|
CertCount = $certs.Count
|
||||||
Certificates = $certs
|
Certificates = ,[object[]]$certs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +305,7 @@ function Get-RdpRepairDiagnosticSnapshot {
|
|||||||
TermService = (Get-Service TermService).Status
|
TermService = (Get-Service TermService).Status
|
||||||
RdpAllowed = -not (Get-ItemProperty $script:RdpRepairTsKey).fDenyTSConnections
|
RdpAllowed = -not (Get-ItemProperty $script:RdpRepairTsKey).fDenyTSConnections
|
||||||
CertCount = $certs.Count
|
CertCount = $certs.Count
|
||||||
Certificates = $certs
|
Certificates = ,[object[]]$certs
|
||||||
GpoLicenseSrv = $pol.LicenseServers
|
GpoLicenseSrv = $pol.LicenseServers
|
||||||
GpoLicMode = $pol.LicensingMode
|
GpoLicMode = $pol.LicensingMode
|
||||||
Events1057_24h = $ev1057
|
Events1057_24h = $ev1057
|
||||||
@@ -302,7 +324,7 @@ function Export-RdpRepairDiagnosticReport {
|
|||||||
$snap.PSObject.Properties | ForEach-Object {
|
$snap.PSObject.Properties | ForEach-Object {
|
||||||
if ($_.Name -eq 'Certificates') {
|
if ($_.Name -eq 'Certificates') {
|
||||||
$lines += 'Certificates:'
|
$lines += 'Certificates:'
|
||||||
foreach ($c in $_.Value) {
|
foreach ($c in (Get-RdpRepairCertificateArray $_.Value)) {
|
||||||
$lines += (' - {0}; Thumb={1}; NotAfter={2}; Key={3}' -f $c.Subject, $c.Thumbprint, $c.NotAfter, $c.HasKey)
|
$lines += (' - {0}; Thumb={1}; NotAfter={2}; Key={3}' -f $c.Subject, $c.Thumbprint, $c.NotAfter, $c.HasKey)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user