private/Step-BuildBootInstallWinPEAppPwsh.ps1
|
function Step-BuildBootInstallWinPEAppPwsh { <# .SYNOPSIS Downloads, caches, and installs PowerShell 7 in the mounted WinPE image .DESCRIPTION Selects the PowerShell archive for global BuildMedia.Architecture, downloads it into the shared microsoft-powershell7 cache when needed, and expands it into Program Files\PowerShell\7 in the mounted image. Downloads use curl.exe when available and otherwise use Invoke-WebRequest. The function appends the application name to global BuildMedia.InstalledApps and loads the mounted SYSTEM registry hive to add PowerShell 7 to Path and PowerShell module locations to PSModulePath. .PARAMETER AppName Specifies the application name recorded in global BuildMedia.InstalledApps. The current implementation resets this value to PowerShell 7.6.4. .PARAMETER amd64Url Specifies an AMD64 download URL. The current implementation uses its built-in architecture-specific PowerShell 7.6.4 URLs instead of this value. .EXAMPLE PS> Step-BuildBootInstallWinPEAppPwsh Caches and installs the PowerShell 7 archive matching the current build architecture. .INPUTS None. This function does not accept pipeline input. .OUTPUTS System.String. Native reg.exe commands can write status text to the success stream. .NOTES Author: David Segura Company: Recast Software Version: 1.0.0 Date: 2026-08-30 Requires global BuildMedia with Architecture, MountPath, and InstalledApps properties and script OSDeployCorePath. Requires network access for cache misses and permission to load the mounted SYSTEM registry hive. Modifies the shared cache, mounted image, mounted registry hive, and global InstalledApps collection. #> [CmdletBinding()] param ( [System.String] $AppName = 'PowerShell 7.6.4', [System.String] $amd64Url = 'https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-x64.zip' ) $Architecture = $global:BuildMedia.Architecture $MountPath = $global:BuildMedia.MountPath $WinPEAppsPath = Join-Path $Script:OSDeployCorePath 'cache' 'winpe-apps' $CacheDir = Join-Path $WinPEAppsPath 'microsoft-powershell7' $AppName = 'PowerShell 7.6.4' if ($Architecture -eq 'amd64') { $DownloadUri = 'https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-x64.zip' } if ($Architecture -eq 'arm64') { $DownloadUri = 'https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-arm64.zip' } if (-not (Test-Path -Path $CacheDir)) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Creating PowerShell 7 cache: $CacheDir" New-Item -Path $CacheDir -ItemType Directory -Force | Out-Null } else { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Using PowerShell 7 cache: $CacheDir" } $DownloadFile = Split-Path $DownloadUri -Leaf if (-not (Test-Path "$CacheDir\$DownloadFile")) { $downloadPath = Join-Path $CacheDir $DownloadFile Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] [$($MyInvocation.MyCommand)] Downloading PowerShell 7: $DownloadFile" if (Get-Command 'curl.exe' -ErrorAction SilentlyContinue) { & curl.exe --silent --location --retry 5 --continue-at - --output $downloadPath $DownloadUri } else { Invoke-WebRequest -UseBasicParsing -Uri $DownloadUri -OutFile $downloadPath } } if (Test-Path "$CacheDir\$DownloadFile") { Expand-Archive -Path "$CacheDir\$DownloadFile" -DestinationPath "$MountPath\Program Files\PowerShell\7" -Force # Record the installed app $global:BuildMedia.InstalledApps += $AppName } #================================================= # Add PowerShell 7 PATH to the WinPE PATH $RegistryHiveLoaded = $false try { & reg.exe LOAD HKLM\Mount "$MountPath\Windows\System32\Config\SYSTEM" Start-Sleep -Seconds 3 if ($LASTEXITCODE -ne 0) { Write-Warning "Failed to load SYSTEM hive from $systemHivePath (exit code $LASTEXITCODE)." -WarningAction Continue } $RegistryHiveLoaded = $true $RegistryKey = 'HKLM:\Mount\ControlSet001\Control\Session Manager\Environment' $CurrentPath = (Get-Item -path $RegistryKey).GetValue('Path', '', 'DoNotExpandEnvironmentNames') $NewPath = $CurrentPath + ';X:\Program Files\WindowsPowerShell\Scripts' $Result = New-ItemProperty -Path $RegistryKey -Name 'Path' -PropertyType ExpandString -Value $NewPath -Force $CurrentPSModulePath = (Get-Item -path $RegistryKey ).GetValue('PSModulePath', '', 'DoNotExpandEnvironmentNames') $NewPSModulePath = $CurrentPSModulePath + ';%ProgramFiles%\PowerShell\;%ProgramFiles%\PowerShell\7\;%SystemRoot%\system32\config\systemprofile\Documents\PowerShell\Modules\' $Result = New-ItemProperty -Path $RegistryKey -Name 'PSModulePath' -PropertyType ExpandString -Value $NewPSModulePath -Force } finally { if ($RegistryHiveLoaded) { Get-Variable Result -ErrorAction SilentlyContinue | Remove-Variable -ErrorAction SilentlyContinue Get-Variable RegistryKey -ErrorAction SilentlyContinue | Remove-Variable -ErrorAction SilentlyContinue [gc]::Collect() Start-Sleep -Seconds 3 & reg.exe UNLOAD HKLM\Mount if ($LASTEXITCODE -ne 0) { Write-Warning "Failed to unload SYSTEM hive from HKLM\Mount (exit code $LASTEXITCODE)." -WarningAction Continue } } } } |