77 lines
4.0 KiB
PowerShell
77 lines
4.0 KiB
PowerShell
#Requires -Version 5.1
|
|
<#
|
|
.SYNOPSIS Apply SilverMetal Windows branding (4 layers), offline (WIM) or online.
|
|
.DESCRIPTION
|
|
Offline: reg-load the mounted image's SOFTWARE + default NTUSER hives, write
|
|
values, stage assets, reg-unload. Online: write live HKLM + default-user hive.
|
|
Design: ../docs/superpowers/specs/2026-06-09-first-boot-branding-design.md
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)][ValidateSet('Offline','Online')][string]$Mode,
|
|
[string]$MountPath, # required for Offline
|
|
[string]$Manifest = "$PSScriptRoot\branding.manifest.json",
|
|
[string]$AssetsDir = "$PSScriptRoot\assets",
|
|
[switch]$PassThru
|
|
)
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
. "$PSScriptRoot\lib\BrandingLayers.ps1"
|
|
function Write-Stage { param($m) Write-Host "==> $m" -ForegroundColor Cyan }
|
|
|
|
if ($Mode -eq 'Offline' -and -not $MountPath) { throw 'Offline mode requires -MountPath.' }
|
|
$m = Get-Content $Manifest -Raw | ConvertFrom-Json
|
|
|
|
# Destination paths (image-relative for offline, live for online).
|
|
$winRoot = if ($Mode -eq 'Offline') { $MountPath } else { 'C:' }
|
|
$logoDest = Join-Path $winRoot 'Windows\System32\oemlogo.bmp'
|
|
$lockDir = Join-Path $winRoot 'Windows\Web\Screen\SilverMetal'
|
|
$wallDir = Join-Path $winRoot 'Windows\Web\Wallpaper\SilverMetal'
|
|
$themeDest = Join-Path $winRoot 'Windows\Resources\Themes'
|
|
$lockLive = 'C:\Windows\Web\Screen\SilverMetal\' + $m.lockScreen.image
|
|
$wallLive = 'C:\Windows\Web\Wallpaper\SilverMetal\' + $m.desktop.wallpaper
|
|
$logoLive = 'C:\Windows\System32\oemlogo.bmp'
|
|
|
|
# --- stage assets ---
|
|
Write-Stage "Stage branding assets ($Mode)"
|
|
New-Item -ItemType Directory -Force $lockDir,$wallDir,$themeDest,(Split-Path $logoDest) | Out-Null
|
|
Copy-Item (Join-Path $AssetsDir $m.oem.logo) $logoDest -Force
|
|
Copy-Item (Join-Path $AssetsDir $m.lockScreen.image) (Join-Path $lockDir $m.lockScreen.image) -Force
|
|
Copy-Item (Join-Path $AssetsDir $m.desktop.wallpaper)(Join-Path $wallDir $m.desktop.wallpaper) -Force
|
|
Copy-Item (Join-Path $AssetsDir $m.desktop.theme) (Join-Path $themeDest $m.desktop.theme) -Force
|
|
|
|
$result = [ordered]@{ OemApplied=$false; LockScreenApplied=$false; DesktopApplied=$false; BitLockerApplied=$false }
|
|
|
|
function Invoke-WithHive {
|
|
param([string]$HivePath,[string]$Name,[scriptblock]$Body)
|
|
& reg load "HKLM\$Name" $HivePath | Out-Null
|
|
if ($LASTEXITCODE -ne 0) { throw "reg load $Name ($HivePath) failed" }
|
|
try { & $Body "Registry::HKEY_LOCAL_MACHINE\$Name" }
|
|
finally { [gc]::Collect(); Start-Sleep -Milliseconds 500; & reg unload "HKLM\$Name" | Out-Null }
|
|
}
|
|
|
|
if ($Mode -eq 'Offline') {
|
|
$swHive = Join-Path $MountPath 'Windows\System32\config\SOFTWARE'
|
|
$duHive = Join-Path $MountPath 'Users\Default\NTUSER.DAT'
|
|
Invoke-WithHive $swHive 'SM_BRAND_SW' {
|
|
param($sw)
|
|
Write-Stage 'OEM About'; Set-OemInformation -SoftwareRoot $sw -Manifest $m -LogoPath $logoLive; $result.OemApplied=$true
|
|
Write-Stage 'Lock screen'; Set-LockScreen -SoftwareRoot $sw -ImagePath $lockLive -Lock:$m.lockScreen.lock; $result.LockScreenApplied=$true
|
|
Write-Stage 'BitLocker preboot';Set-BitLockerPreboot -SoftwareRoot $sw -Manifest $m; $result.BitLockerApplied=$true
|
|
}
|
|
Invoke-WithHive $duHive 'SM_BRAND_DU' {
|
|
param($du)
|
|
Write-Stage 'Desktop'; Set-DesktopBranding -DefaultUserRoot $du -Manifest $m -WallpaperPath $wallLive; $result.DesktopApplied=$true
|
|
}
|
|
} else {
|
|
Set-OemInformation -SoftwareRoot 'HKLM:\SOFTWARE' -Manifest $m -LogoPath $logoLive; $result.OemApplied=$true
|
|
Set-LockScreen -SoftwareRoot 'HKLM:\SOFTWARE' -ImagePath $lockLive -Lock:$m.lockScreen.lock; $result.LockScreenApplied=$true
|
|
Set-BitLockerPreboot -SoftwareRoot 'HKLM:\SOFTWARE' -Manifest $m; $result.BitLockerApplied=$true
|
|
Invoke-WithHive 'C:\Users\Default\NTUSER.DAT' 'SM_BRAND_DU' {
|
|
param($du) Set-DesktopBranding -DefaultUserRoot $du -Manifest $m -WallpaperPath $wallLive; $result.DesktopApplied=$true
|
|
}
|
|
}
|
|
|
|
Write-Host 'Branding applied.' -ForegroundColor Green
|
|
if ($PassThru) { [pscustomobject]$result }
|