From c8572eb8d43322ea4d7f0c8355c270e5dd405d05 Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Fri, 4 Sep 2026 09:43:45 -0600 Subject: [PATCH] Fix localized enrollment recovery --- scripts/Enable-LabRemoteAccess.ps1 | 24 ++++++++++++++++-------- scripts/Install-CredentialProvider.ps1 | 8 ++++++-- scripts/Install-SguEnrollmentGuard.ps1 | 13 ++++++++++--- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/scripts/Enable-LabRemoteAccess.ps1 b/scripts/Enable-LabRemoteAccess.ps1 index ca3d5d4..73c0d3c 100644 --- a/scripts/Enable-LabRemoteAccess.ps1 +++ b/scripts/Enable-LabRemoteAccess.ps1 @@ -20,6 +20,20 @@ $remoteDesktopUsersSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-32- $remoteDesktopUsersGroup = ($remoteDesktopUsersSid.Translate([Security.Principal.NTAccount]).Value -split '\\', 2)[1] if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Enable RDP and grant $RemoteDesktopPrincipal access")) { + function Invoke-PowerCfgBestEffort { + param([Parameter(Mandatory)][string[]]$Arguments) + + # Start-Process keeps powercfg's policy-override diagnostic on its own + # stderr stream. In PowerShell 7, directly invoking that native command + # turns stderr into a terminating ErrorRecord under $ErrorActionPreference + # = 'Stop', which previously aborted this unrelated remediation work. + $process = Start-Process -FilePath "$env:SystemRoot\System32\powercfg.exe" ` + -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden + if ($process.ExitCode -ne 0) { + Write-Warning "powercfg $($Arguments -join ' ') returned exit code $($process.ExitCode); continuing enrollment repair." + } + } + foreach ($powerChange in @( @('monitor-timeout-ac', '0'), @('monitor-timeout-dc', '0'), @@ -27,15 +41,9 @@ if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Enable RDP and grant $RemoteDesk @('standby-timeout-dc', '0'), @('hibernate-timeout-ac', '0'), @('hibernate-timeout-dc', '0'))) { - & powercfg.exe /change $powerChange[0] $powerChange[1] - if ($LASTEXITCODE -ne 0) { - throw "powercfg /change $($powerChange[0]) failed with exit code $LASTEXITCODE." - } - } - & powercfg.exe /hibernate off - if ($LASTEXITCODE -ne 0) { - throw "powercfg /hibernate off failed with exit code $LASTEXITCODE." + Invoke-PowerCfgBestEffort -Arguments @('/change', $powerChange[0], $powerChange[1]) } + Invoke-PowerCfgBestEffort -Arguments @('/hibernate', 'off') Set-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' ` -Name fDenyTSConnections -Type DWord -Value 0 diff --git a/scripts/Install-CredentialProvider.ps1 b/scripts/Install-CredentialProvider.ps1 index d059bf0..9712f84 100644 --- a/scripts/Install-CredentialProvider.ps1 +++ b/scripts/Install-CredentialProvider.ps1 @@ -179,10 +179,14 @@ if ($PSCmdlet.ShouldProcess($installPath, 'Install and register the SGU Credenti $acl = Get-Acl -LiteralPath (Split-Path $settingsPath -Parent) $acl.SetAccessRuleProtection($true, $false) + # Resolve built-in identities by SID instead of localized display names. + # "BUILTIN\Administrators" is not resolvable on every non-English client. + $systemSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-18') + $administratorsSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-32-544') $acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new( - 'SYSTEM', 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')) + $systemSid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')) $acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new( - 'BUILTIN\Administrators', 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')) + $administratorsSid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')) Set-Acl -LiteralPath (Split-Path $settingsPath -Parent) -AclObject $acl New-Item -Path $classRegistryPath -Force | Out-Null diff --git a/scripts/Install-SguEnrollmentGuard.ps1 b/scripts/Install-SguEnrollmentGuard.ps1 index c24830b..104e242 100644 --- a/scripts/Install-SguEnrollmentGuard.ps1 +++ b/scripts/Install-SguEnrollmentGuard.ps1 @@ -71,7 +71,11 @@ if ($PSCmdlet.ShouldProcess($enrollmentRoot, 'Install the SGU enrollment repair $runtimeDirectory = Join-Path $enrollmentRoot 'prerequisites' New-Item -ItemType Directory -Path $runtimeDirectory -Force | Out-Null $guardRuntimeInstaller = Join-Path $runtimeDirectory (Split-Path $DotNetRuntimeInstallerPath -Leaf) - Copy-Item -LiteralPath $DotNetRuntimeInstallerPath -Destination $guardRuntimeInstaller -Force + $sourceRuntimeInstaller = [IO.Path]::GetFullPath($DotNetRuntimeInstallerPath) + $destinationRuntimeInstaller = [IO.Path]::GetFullPath($guardRuntimeInstaller) + if (-not $sourceRuntimeInstaller.Equals($destinationRuntimeInstaller, [StringComparison]::OrdinalIgnoreCase)) { + Copy-Item -LiteralPath $DotNetRuntimeInstallerPath -Destination $guardRuntimeInstaller -Force + } } $guardConfiguration = [ordered]@{ @@ -92,10 +96,13 @@ if ($PSCmdlet.ShouldProcess($enrollmentRoot, 'Install the SGU enrollment repair $acl = Get-Acl -LiteralPath $enrollmentRoot $acl.SetAccessRuleProtection($true, $false) + # Well-known SIDs are invariant across localized Windows installations. + $systemSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-18') + $administratorsSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-32-544') $acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new( - 'SYSTEM', 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')) + $systemSid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')) $acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new( - 'BUILTIN\Administrators', 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')) + $administratorsSid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')) Set-Acl -LiteralPath $enrollmentRoot -AclObject $acl $repairScript = Join-Path $enrollmentRoot 'Repair-SguClientEnrollment.ps1'