private/Step-BuildBootInstallWinPEAppSysinternals.ps1

#Requires -PSEdition Core

function Step-BuildBootInstallWinPEAppSysinternals {
    <#
    .SYNOPSIS
        Downloads, caches, and injects configured Sysinternals tools into WinPE
 
    .DESCRIPTION
        Creates or reuses the Sysinternals cache, then enumerates the configured tools
        in global OSDeployModule.BootImage.winpeapps.sysinternals. Tools without a URL
        for global BuildMedia.Architecture are skipped. Missing binaries are downloaded
        with curl.exe when available and Invoke-WebRequest otherwise.
 
        Each available cached binary is copied to its configured name under
        Windows\System32 in the mounted image. The tool name is then appended to global
        BuildMedia.InstalledApps. Download failures and missing cached files produce
        warnings.
 
    .EXAMPLE
        PS> Step-BuildBootInstallWinPEAppSysinternals
 
        Caches and injects each configured tool for the current build architecture.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        None. Native download output is silent and web requests write directly to files.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 0.1.0
        Date: 2026-08-28
 
        Requires global BuildMedia with Architecture, MountPath, and InstalledApps
        properties; global OSDeployModule.BootImage.winpeapps.sysinternals; and script
        OSDeployCorePath. Requires network access for cache misses. Modifies the shared
        cache, mounted image, and global InstalledApps collection.
    #>

    [CmdletBinding()]
    param ()

    $Architecture  = $global:BuildMedia.Architecture
    $MountPath     = $global:BuildMedia.MountPath
    $WinPEAppsPath = Join-Path $Script:OSDeployCorePath 'cache' 'winpe-apps'
    $CacheDir      = Join-Path $WinPEAppsPath 'microsoft-sysinternals'
    $sysinternals  = $global:OSDeployModule.BootImage.winpeapps.sysinternals

    if (-not (Test-Path -Path $CacheDir)) {
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Creating Sysinternals cache: $CacheDir"
        New-Item -Path $CacheDir -ItemType Directory -Force | Out-Null
    }
    else {
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Using Sysinternals cache: $CacheDir"
    }

    foreach ($tool in $sysinternals.PSObject.Properties) {
        $toolName   = $tool.Name
        $toolConfig = $tool.Value

        # Select arch-appropriate URL; skip tool entirely if absent/empty for this arch
        $DownloadUri = $toolConfig.$Architecture
        if ([string]::IsNullOrEmpty($DownloadUri)) {
            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Skipping Sysinternals $toolName (no $Architecture URL)"
            continue
        }

        $CachedFile = Join-Path $CacheDir (Split-Path -Leaf $DownloadUri)

        if (-not (Test-Path -Path $CachedFile)) {
            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Downloading Sysinternals $toolName ($Architecture): $DownloadUri"
            try {
                if (Get-Command 'curl.exe' -ErrorAction SilentlyContinue) {
                    & curl.exe --silent --location --retry 5 --continue-at - --output $CachedFile $DownloadUri
                }
                else {
                    Invoke-WebRequest -UseBasicParsing -Uri $DownloadUri -OutFile $CachedFile
                }
            }
            catch {
                Write-Warning "[$(Get-Date -Format s)] Failed to download Sysinternals $toolName ($Architecture): $_"
                continue
            }
        }

        if (-not (Test-Path -Path $CachedFile)) {
            Write-Warning "[$(Get-Date -Format s)] Sysinternals $toolName binary not found at $CachedFile"
            continue
        }

        $Destination = Join-Path $MountPath "Windows\System32\$($toolConfig.destination)"
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Injecting $($toolConfig.destination) into WinPE"
        Copy-Item -Path $CachedFile -Destination $Destination -Force
        $global:BuildMedia.InstalledApps += $toolName
    }
}