namespace SilverOS.Welcome.Core.Apply; public sealed class BootstrapService(IProcessRunner runner) : IBootstrapService { // Kiosk revert is BEST-EFFORT (like TearDownAsync): -EA SilentlyContinue throughout. // If WESL is unavailable the real user still gets Explorer (no custom shell for their // SID). Intentional: don't fail the apply over a missing WMI class. Must run BEFORE // TearDownAsync so the sm-bootstrap SID still resolves. public async Task RevertKioskAsync(CancellationToken ct = default) { // Remove sm-bootstrap custom shell entry + disable Shell Launcher. WESL_UserSetting // methods are STATIC on the class — call class-level (-Namespace/-ClassName), NOT // via -InputObject on a Get-CimInstance result (which is always null). await Ps( "$c='root\\\\standardcimv2\\\\embedded';" + "$sid=(New-Object System.Security.Principal.NTAccount('sm-bootstrap')).Translate([System.Security.Principal.SecurityIdentifier]).Value;" + "Invoke-CimMethod -Namespace $c -ClassName WESL_UserSetting -MethodName RemoveCustomShell -Arguments @{Sid=$sid} -EA SilentlyContinue | Out-Null;" + "Invoke-CimMethod -Namespace $c -ClassName WESL_UserSetting -MethodName SetEnabled -Arguments @{Enabled=$false} -EA SilentlyContinue | Out-Null", ct); // Revert escape policies set by Configure-Kiosk.ps1. await Ps( "$s='HKLM:\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\System';" + "Remove-ItemProperty $s -Name DisableTaskMgr,DisableLockWorkstation,HideFastUserSwitching -EA SilentlyContinue", ct); } // Teardown is BEST-EFFORT (unlike Account/BitLocker which are strict): the answer file's // AutoLogon LogonCount=1 already neutralises auto-logon after the first logon (Windows clears // AutoAdminLogon itself), so these Winlogon cleanups must not fail the whole apply. The op that // matters — removing the sm-bootstrap account — runs regardless and is tolerant too. public async Task TearDownAsync(string bootstrapUser, CancellationToken ct = default) { const string w = "'HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon'"; await Ps($"Set-ItemProperty -Path {w} -Name AutoAdminLogon -Value '0' -EA SilentlyContinue; " + $"Remove-ItemProperty -Path {w} -Name DefaultPassword -EA SilentlyContinue; " + $"Remove-ItemProperty -Path {w} -Name DefaultUserName -EA SilentlyContinue; " + $"Remove-ItemProperty -Path {w} -Name DefaultDomainName -EA SilentlyContinue", ct); var u = Esc(bootstrapUser); await Ps($"Remove-LocalUser -Name '{u}' -EA SilentlyContinue", ct); } private static string Esc(string s) => s.Replace("'", "''"); private Task Ps(string s, CancellationToken ct) => runner.RunAsync("powershell.exe", $"-NoProfile -ExecutionPolicy Bypass -Command \"{s}\"", ct); }