All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 4m47s
Found by reading the unencrypted VM disk after run #7: 1. Online branding never ran: Apply-Branding.ps1 had a UTF-8 em-dash in a Write-Warning STRING; Windows PowerShell 5.1 (SetupComplete) reads .ps1 as ANSI, mangled it, broke the string terminator -> whole script failed to parse -> lock/login/wallpaper branding never re-applied. Fix: ASCII-ify the em-dash AND save the branding scripts UTF-8-with-BOM so PS5.1 always decodes them correctly (verified parses under PS5.1 + PS7). 2. sm-bootstrap never removed: TearDownAsync used schtasks /tr with an inline -EncodedCommand, which silently fails past the ~261-char /tr limit, so the cleanup task was never created (confirmed NO_TASK on disk). Fix: Register-ScheduledTask (no length limit). 3. Done step: show a QR code of the BitLocker recovery key (QRCoder) for phone backup, and lay key+QR side-by-side so the Restart button no longer overflows below the fold. Verified: welcome solution builds, 29/29 tests; branding Pester 6/6 unit (offline-integration needs elevation, runs in CI). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
131 lines
4.7 KiB
PowerShell
131 lines
4.7 KiB
PowerShell
Set-StrictMode -Version Latest
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
. "$PSScriptRoot\RegistryHelpers.ps1"
|
|
|
|
|
|
|
|
function Set-OemInformation {
|
|
|
|
param([Parameter(Mandatory)][string]$SoftwareRoot,[Parameter(Mandatory)]$Manifest,[Parameter(Mandatory)][string]$LogoPath)
|
|
|
|
$sub = 'Microsoft\Windows\CurrentVersion\OEMInformation'
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $sub -Name 'Manufacturer' -Type String -Value $Manifest.oem.manufacturer
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $sub -Name 'Model' -Type String -Value $Manifest.oem.model
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $sub -Name 'SupportURL' -Type String -Value $Manifest.oem.supportUrl
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $sub -Name 'SupportHours' -Type String -Value $Manifest.oem.supportHours
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $sub -Name 'Logo' -Type String -Value $LogoPath
|
|
|
|
}
|
|
|
|
|
|
|
|
function Set-LockScreen {
|
|
|
|
param([Parameter(Mandatory)][string]$SoftwareRoot,[Parameter(Mandatory)][string]$ImagePath,[bool]$Lock=$true)
|
|
|
|
# Per-device modern lock-screen image (reliable on Enterprise/IoT).
|
|
|
|
$csp = 'Microsoft\Windows\CurrentVersion\PersonalizationCSP'
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $csp -Name 'LockScreenImagePath' -Type String -Value $ImagePath
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $csp -Name 'LockScreenImageUrl' -Type String -Value $ImagePath
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $csp -Name 'LockScreenImageStatus' -Type DWord -Value 1
|
|
|
|
if ($Lock) {
|
|
|
|
$pol = 'Policies\Microsoft\Windows\Personalization'
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $pol -Name 'LockScreenImage' -Type String -Value $ImagePath
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $pol -Name 'NoChangingLockScreen' -Type DWord -Value 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function Set-DesktopBranding {
|
|
|
|
param([Parameter(Mandatory)][string]$DefaultUserRoot,[Parameter(Mandatory)]$Manifest,[Parameter(Mandatory)][string]$WallpaperPath)
|
|
|
|
Set-SmRegValue -Root $DefaultUserRoot -SubKey 'Control Panel\Desktop' -Name 'WallPaper' -Type String -Value $WallpaperPath
|
|
|
|
Set-SmRegValue -Root $DefaultUserRoot -SubKey 'Control Panel\Desktop' -Name 'WallpaperStyle' -Type String -Value '10' # fill
|
|
|
|
if ($Manifest.desktop.darkMode) {
|
|
|
|
$p = 'Software\Microsoft\Windows\CurrentVersion\Themes\Personalize'
|
|
|
|
Set-SmRegValue -Root $DefaultUserRoot -SubKey $p -Name 'AppsUseLightTheme' -Type DWord -Value 0
|
|
|
|
Set-SmRegValue -Root $DefaultUserRoot -SubKey $p -Name 'SystemUsesLightTheme' -Type DWord -Value 0
|
|
|
|
}
|
|
|
|
# Accent (cyan). DWM uses fully-opaque DWORDs with DIFFERENT byte orders:
|
|
|
|
# ColorizationColor = 0xAARRGGBB (ARGB); AccentColor = 0xAABBGGRR (ABGR).
|
|
|
|
# Manifest holds the plain RGB hex (source of truth); derive both, alpha=FF.
|
|
|
|
# NOTE: exact accent rendering is VM-verified (plan §9 soft spot).
|
|
|
|
$rgb = $Manifest.desktop.accentColor.TrimStart('#')
|
|
|
|
$r = [Convert]::ToInt32($rgb.Substring(0,2),16)
|
|
|
|
$g = [Convert]::ToInt32($rgb.Substring(2,2),16)
|
|
|
|
$b = [Convert]::ToInt32($rgb.Substring(4,2),16)
|
|
|
|
$argb = [int](0xFF000000 -bor ($r -shl 16) -bor ($g -shl 8) -bor $b) # ColorizationColor
|
|
|
|
$abgr = [int](0xFF000000 -bor ($b -shl 16) -bor ($g -shl 8) -bor $r) # AccentColor
|
|
|
|
Set-SmRegValue -Root $DefaultUserRoot -SubKey 'Software\Microsoft\Windows\DWM' -Name 'AccentColor' -Type DWord -Value $abgr
|
|
|
|
Set-SmRegValue -Root $DefaultUserRoot -SubKey 'Software\Microsoft\Windows\DWM' -Name 'ColorizationColor' -Type DWord -Value $argb
|
|
|
|
if (-not $Manifest.desktop.lockWallpaper) { return }
|
|
|
|
Set-SmRegValue -Root $DefaultUserRoot -SubKey 'Software\Microsoft\Windows\CurrentVersion\Policies\ActiveDesktop' -Name 'NoChangingWallPaper' -Type DWord -Value 1
|
|
|
|
}
|
|
|
|
|
|
|
|
function Set-BitLockerPreboot {
|
|
|
|
param([Parameter(Mandatory)][string]$SoftwareRoot,[Parameter(Mandatory)]$Manifest)
|
|
|
|
# GPO "Configure pre-boot recovery message and URL" (ADMX VolumeEncryption).
|
|
|
|
# NOTE: only the BitLocker RECOVERY screen is customisable; the normal PIN-entry
|
|
|
|
# screen text is fixed Windows UI. Exact value names are asserted by the read-back
|
|
|
|
# test; if a name is wrong the offline-apply verify (Task A4) catches it.
|
|
|
|
$fve = 'Policies\Microsoft\FVE'
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $fve -Name 'UseCustomRecoveryMessage' -Type DWord -Value 1
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $fve -Name 'RecoveryMessage' -Type String -Value $Manifest.bitlocker.recoveryMessage
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $fve -Name 'UseCustomRecoveryUrl' -Type DWord -Value 1
|
|
|
|
Set-SmRegValue -Root $SoftwareRoot -SubKey $fve -Name 'RecoveryUrl' -Type String -Value $Manifest.bitlocker.recoveryUrl
|
|
|
|
}
|
|
|