#Requires -Version 5.1 <# SilverMetal Enhanced - Windows | Offline ISO structure assertions. The reliable CI gate that needs NO nested virtualization: proves the built ISO baked the answer file + hardening payload correctly. Full boot-and-Verify (QEMU + OVMF + swtpm) is a follow-on stage that needs nested virt. Exit 0 = all assertions pass; non-zero = failures. #> [CmdletBinding()] param([Parameter(Mandatory)][string]$IsoPath) Set-StrictMode -Version Latest; $ErrorActionPreference = 'Stop' $fail = 0 function Assert { param([string]$Name,[bool]$Cond) if ($Cond) { Write-Host "[PASS] $Name" -ForegroundColor Green } else { Write-Host "[FAIL] $Name" -ForegroundColor Red; $script:fail++ } } if (-not (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator))) { throw 'must run elevated (WIM mount).' } # Discard any stale WIM mounts from a prior aborted run (CI reuses the runner) and # use a fresh, unique mount dir — a leftover mount makes Mount-WindowsImage fail with # "attempted to mount to a directory that is not empty." Mirrors build.ps1 Stage 0. Get-WindowsImage -Mounted -EA SilentlyContinue | ForEach-Object { Dismount-WindowsImage -Path $_.MountPath -Discard -EA SilentlyContinue | Out-Null } Clear-WindowsCorruptMountPoint -EA SilentlyContinue | Out-Null $img = Mount-DiskImage -ImagePath $IsoPath -PassThru $mount = Join-Path $env:TEMP ('sm-assert-wim-' + [guid]::NewGuid().ToString('N')) $null = New-Item -ItemType Directory -Force $mount try { $drive = ($img | Get-Volume).DriveLetter + ':' Assert 'autounattend.xml at ISO root' (Test-Path "$drive\autounattend.xml") $wim = "$drive\sources\install.wim" Assert 'sources\install.wim present' (Test-Path $wim) if (Test-Path $wim) { $idx = (Get-WindowsImage -ImagePath $wim | Where-Object ImageName -match 'IoT Enterprise LTSC' | Select-Object -First 1).ImageIndex if (-not $idx) { $idx = 1 } Mount-WindowsImage -ImagePath $wim -Index $idx -Path $mount -ReadOnly | Out-Null try { $sc = Join-Path $mount 'Windows\Setup\Scripts\SetupComplete.cmd' Assert 'SetupComplete.cmd baked into WIM' (Test-Path $sc) $mods = Get-ChildItem (Join-Path $mount 'Windows\Setup\Scripts\hardening') -Filter *.ps1 -EA SilentlyContinue Assert 'hardening modules baked (>=9 .ps1)' ($mods.Count -ge 9) Assert 'Verify script baked' (Test-Path (Join-Path $mount 'Windows\Setup\Scripts\hardening\Verify-SilverMetalWindows.ps1')) # Welcome app payload assertions (skipped when Welcome is intentionally disabled). if ($env:SILVERMETAL_WELCOME_ENABLED -ne '0') { $welcomeExe = Join-Path $mount 'Program Files\SilverOS\Welcome\SilverOS.Welcome.App.exe' Assert 'Welcome exe baked into WIM' (Test-Path $welcomeExe) $welcomeFlavours = Get-ChildItem (Join-Path $mount 'Program Files\SilverOS\Welcome\flavours') -Filter '*.json' -EA SilentlyContinue Assert 'Welcome flavours baked (>=1 .json)' ($welcomeFlavours.Count -ge 1) $welcomeCatalog = Join-Path $mount 'Program Files\SilverOS\Welcome\apps\catalog.json' Assert 'Welcome app catalog baked' (Test-Path $welcomeCatalog) } } finally { Dismount-WindowsImage -Path $mount -Discard | Out-Null } } # boot.wim must carry the WinPE collector + winpeshl (the pre-config front-end). $bootwim = "$drive\sources\boot.wim" Assert 'boot.wim present' (Test-Path $bootwim) if (Test-Path $bootwim) { $bmount = Join-Path $env:TEMP ('sm-assert-boot-' + [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Force $bmount | Out-Null Mount-WindowsImage -ImagePath $bootwim -Index 2 -Path $bmount -ReadOnly | Out-Null try { Assert 'collector staged in boot.wim' (Test-Path (Join-Path $bmount 'sm\Collector.ps1')) Assert 'winpeshl.ini set' (Test-Path (Join-Path $bmount 'Windows\System32\winpeshl.ini')) Assert 'answer-file generator staged' (Test-Path (Join-Path $bmount 'sm\New-SmAnswerFile.ps1')) } finally { Dismount-WindowsImage -Path $bmount -Discard | Out-Null; Remove-Item $bmount -Recurse -Force -EA SilentlyContinue } } } finally { Dismount-DiskImage -ImagePath $IsoPath | Out-Null Remove-Item $mount -Recurse -Force -EA SilentlyContinue } Write-Host "`n$($fail) assertion(s) failed." exit $fail