Files
windows-builder/includes/utils/patch-iso-autounattend.ps1
oxmc d38cd32b82 Fix registry ownership/ACL and hang bugs in tiny11maker.ps1, plus build/setup script updates
Fixes several offline-hive build failures: TrustedInstaller-owned keys
(WindowsRuntime\ActivatableClassId, Explorer\Advanced, System\GameConfigStore, Search
SystemIndex) denying writes even to admin-owned processes, a PowerShell 5.1 quirk that drops
empty-string reg.exe arguments and can hang the build on a silent overwrite prompt, a
registry-handle leak that left hives locked and cascaded into DISM cleanup failures, and
relaxed the post-ResetBase health gate to accept 'Repairable' (only abort on
'NonRepairable') since ResetBase makes full repair impossible anyway - documented that
repairing the installed OS needs the original stock ISO, not the tweaked output. Also
rolls in in-progress updates to the other maker scripts and OEM setup/first-boot scripts.
2026-07-31 06:09:58 -07:00

157 lines
5.8 KiB
PowerShell

#Requires -RunAsAdministrator
param(
[string]$IsoPath = "$PSScriptRoot\..\..\tiny10.iso",
[string]$OutputIso = ""
)
$projectRoot = (Resolve-Path "$PSScriptRoot\..\..")
# Detect Windows 10 vs 11 from install.wim/install.esd so the right autounattend variant gets injected
function Get-WindowsVersionFromMedia {
param([string]$MediaRoot)
$srcBase = $MediaRoot
if (-not (Test-Path "$srcBase\sources\install.wim") -and -not (Test-Path "$srcBase\sources\install.esd") -and (Test-Path "$srcBase\x64\sources")) {
$srcBase = "$srcBase\x64"
}
$imagePath = if (Test-Path "$srcBase\sources\install.wim") { "$srcBase\sources\install.wim" }
elseif (Test-Path "$srcBase\sources\install.esd") { "$srcBase\sources\install.esd" }
else { $null }
if (-not $imagePath) { return $null }
try {
$imgInfo = Get-WindowsImage -ImagePath $imagePath -Index 1
$parts = $imgInfo.Version.Split('.')
if ($parts.Count -ge 3) {
$build = [int]$parts[2]
if ($build -ge 22000) { return "11" }
elseif ($build -ge 10240) { return "10" }
}
}
catch {
Write-Warning "Could not read Windows version from media: $($_.Exception.Message)"
}
return $null
}
if (-not (Test-Path $IsoPath)) {
Write-Error "ISO not found: $IsoPath"
exit 1
}
$IsoPath = (Resolve-Path $IsoPath).Path
if (-not $OutputIso) { $OutputIso = $IsoPath }
# Get Windows ADK path from registry(following Visual Studio's winsdk.bat approach).
$WinSDKPath = [Microsoft.Win32.Registry]::GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots", "KitsRoot10", $null)
if ($null -eq $WinSDKPath) {
$WinSDKPath = [Microsoft.Win32.Registry]::GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots", "KitsRoot10", $null)
}
if ($null -ne $WinSDKPath) {
# Trim the following backslash for path concatenation.
$WinSDKPath = $WinSDKPath.TrimEnd('\')
$ADKDepTools = "$WinSDKPath\Assessment and Deployment Kit\Deployment Tools\$hostarchitecture\Oscdimg"
}
$localOSCDIMGPath = "$PSScriptRoot\oscdimg.exe"
if ((Test-Path variable:ADKDepTools) -and (Test-Path "$ADKDepTools\oscdimg.exe" -PathType leaf)) {
Write-Host "Will be using oscdimg.exe from system ADK."
$OSCDIMG = "$ADKDepTools\oscdimg.exe"
}
else {
Write-Host "oscdimg.exe from system ADK not found. Will be using bundled oscdimg.exe."
$url = "https://msdl.microsoft.com/download/symbols/oscdimg.exe/3D44737265000/oscdimg.exe"
if (![System.IO.File]::Exists($localOSCDIMGPath)) {
Write-Host "Downloading oscdimg.exe..."
Invoke-WebRequest -Uri $url -OutFile $localOSCDIMGPath
if ([System.IO.File]::Exists($localOSCDIMGPath)) {
Write-Host "oscdimg.exe downloaded successfully."
}
else {
Write-Error "Failed to download oscdimg.exe."
exit 1
}
}
else {
Write-Host "oscdimg.exe already exists locally."
}
$OSCDIMG = $localOSCDIMGPath
}
# Temp working folder
$tempDir = "$env:TEMP\tiny10_patch_$([System.IO.Path]::GetRandomFileName().Replace('.',''))"
New-Item -ItemType Directory -Path $tempDir | Out-Null
Write-Host "Temp dir: $tempDir"
try {
# Mount ISO
Write-Host "Mounting ISO..."
$diskImage = Mount-DiskImage -ImagePath $IsoPath -PassThru
$driveLetter = ($diskImage | Get-Volume).DriveLetter + ":"
Write-Host "Mounted at $driveLetter"
# Copy contents
Write-Host "Copying ISO contents (this may take a minute)..."
Copy-Item -Path "$driveLetter\*" -Destination $tempDir -Recurse -Force
Dismount-DiskImage -ImagePath $IsoPath | Out-Null
Write-Host "Dismounted."
# Clear read-only flags
Get-ChildItem -Path $tempDir -Recurse | ForEach-Object {
if ($_.Attributes -band [System.IO.FileAttributes]::ReadOnly) {
$_.Attributes = $_.Attributes -bxor [System.IO.FileAttributes]::ReadOnly
}
}
# Detect Windows version so we inject the matching autounattend variant
Write-Host "Detecting Windows version from media..."
$detectedVersion = Get-WindowsVersionFromMedia -MediaRoot $tempDir
if (-not $detectedVersion) {
Write-Host "Could not automatically detect Windows version from media."
do {
$detectedVersion = Read-Host "Enter Windows version (10 or 11)"
} while ($detectedVersion -notin @("10", "11"))
}
else {
Write-Host "Detected Windows $detectedVersion media."
}
# Replace autounattend.xml with the version-matched variant
$newXml = "$projectRoot\includes\autounattend-win$detectedVersion.xml"
if (-not (Test-Path $newXml)) {
Write-Error "autounattend-win$detectedVersion.xml not found at: $newXml"
exit 1
}
Write-Host "Replacing autounattend.xml with autounattend-win$detectedVersion.xml..."
Copy-Item -Path $newXml -Destination "$tempDir\autounattend.xml" -Force
# Detect label from ISO filename, tagged with the detected version
$isoName = [System.IO.Path]::GetFileNameWithoutExtension($IsoPath)
$label = if ($isoName -match 'x86') { "Tiny${detectedVersion}_x86" } else { "Tiny${detectedVersion}_x64" }
$bootEtfs = "$tempDir\boot\etfsboot.com"
$bootEfi = "$tempDir\efi\microsoft\boot\efisys.bin"
Write-Host "Repacking ISO as: $OutputIso"
& "$OSCDIMG" "-l$label" '-m' '-o' '-u2' '-udfver102' `
"-bootdata:2#p0,e,b$bootEtfs#pEF,e,b$bootEfi" `
$tempDir $OutputIso
if ($LASTEXITCODE -eq 0) {
Write-Host "Done. ISO saved to: $OutputIso"
} else {
Write-Error "oscdimg failed with exit code $LASTEXITCODE"
}
} finally {
try { Dismount-DiskImage -ImagePath $IsoPath -ErrorAction SilentlyContinue | Out-Null } catch {}
Write-Host "Cleaning up temp dir..."
Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}