public/Invoke-OSDeployHydration.ps1

#Requires -PSEdition Core
#Requires -Version 7.4

function Invoke-OSDeployHydration {
    <#
    .SYNOPSIS
        Hydrates an OSDeploy workstation for the current processor architecture
 
    .DESCRIPTION
        Runs the OSDeploy setup sequence for arm64 when PROCESSOR_ARCHITECTURE is ARM64 and
        amd64 otherwise. It confirms the overall workflow; installs required Windows ADK 25H2
        and 7-Zip components when absent; optionally installs Git, Visual Studio Code, Visual
        Studio Code Insiders, and Hyper-V; downloads and imports the matching Windows Enterprise
        ESD; refreshes matching WinPE drivers; and builds Hydra boot media from the newest WinRE
        source with ADK WinPE fallback.
 
        On a physical host, the workflow can install Hyper-V and can create a test VM when
        Hyper-V is enabled and the generated bootmedia.iso exists. These Hyper-V paths are
        skipped when the command detects that it is running in a VM.
 
        WhatIf is forwarded to the ESD and driver update commands, and the ESD/import pair is
        also gated by this command's ShouldProcess call. Force automatically confirms this and
        delegated ShouldProcess calls. The boot-media build is not passed WhatIf and remains
        independently interactive.
 
    .PARAMETER Force
        Bypasses this function's workflow, component-installation, Hyper-V test, and
        ShouldProcess confirmation prompts. It is forwarded to Update-OSDeployCoreESD and
        Update-OSDeployCoreDrivers, forcing their refresh behavior and automatically confirming
        their ShouldProcess calls. It is not passed to Update-OSDeployCoreRE,
        Build-OSDeployBoot, or New-OSDeployHyperVM. The initial ESD download is automatically
        confirmed; an ESD manual retry prompt can still appear after automatic attempts fail.
 
    .EXAMPLE
        PS> Invoke-OSDeployHydration
 
        Runs hydration for the current architecture with workflow and component prompts.
 
    .EXAMPLE
        PS> Invoke-OSDeployHydration -Force
 
        Accepts hydration-level prompts, automatically starts the ESD download, and forces ESD
        and driver refresh behavior. Boot-media content selectors can still appear.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        System.Management.Automation.PSCustomObject. Passes through software action results and,
        when created, the Hyper-V VM result.
 
        System.IO.FileInfo. Passes through verified ESD files and processed driver packages.
 
        System.IO.DirectoryInfo. Passes through each newly imported OS image directory.
 
        System.String. Native installer output can reach the success stream through delegated
        installation commands.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 1.0.0
        Date: 2026-08-28
 
        Requires Windows 11 25H2 or later, PowerShell 7.4 or later installed from MSI,
        curl.exe, OSDCloud 26.5.24.1 or later, and Administrator rights.
 
        Declining installation of required ADK or 7-Zip terminates the workflow. Force is scoped
        to the behavior described above and does not make the complete child workflow unattended.
 
    .LINK
        Build-OSDeployBoot
 
    .LINK
        Update-OSDeployCoreESD
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [Parameter()]
        [switch]$Force
    )

    if ($Force) {
        $ConfirmPreference = 'None'
    }
    #=================================================
    Write-HostOSDeployBanner
    #=================================================
    # Stop before starting hydration when a required host capability is missing.
    if (-not (Test-IsWindows11)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Windows 11 is required."
    }
    if (-not (Test-IsWindows1125H2)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Windows 11 25H2 (build 26200) is required."
    }
    if (-not (Test-PwshVersionMin)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] PowerShell 7.4 or higher is required."
    }
    if (-not (Test-PwshPSHome)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] The MSI installation of PowerShell 7 is required."
    }
    if (-not (Test-CommandCurl)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] curl.exe is required but was not found in the current PATH. curl.exe ships with Windows 10 1803+."
    }
    if (-not (Test-IsAdministrator)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Administrator rights are required. Re-run PowerShell as Administrator and try again."
    }
    #=================================================
    # Stop when the required OSDCloud version is unavailable.
    # OSDCloud Requirements
    $RequiredOSDCloudVersion = [System.Version]'26.5.24.1'
    if (-not (Get-Command -Name 'Get-OSDCloudModuleVersion' -ErrorAction SilentlyContinue)) {
        Write-Warning "[$(Get-Date -Format s)] OSDCloud module $RequiredOSDCloudVersion or newer is required."
        Write-Warning "[$(Get-Date -Format s)] Install-Module -Name OSDCloud -Force -SkipPublisherCheck"
        return
    }
    $OSDCloudVersion = Get-OSDCloudModuleVersion
    if ($OSDCloudVersion -lt $RequiredOSDCloudVersion) {
        Write-Warning "[$(Get-Date -Format s)] OSDCloud module $RequiredOSDCloudVersion or newer is required. Loaded version: $OSDCloudVersion"
        Write-Warning "[$(Get-Date -Format s)] Install-Module -Name OSDCloud -Force -SkipPublisherCheck"
        return
    }
    #=================================================
    # Ensure directory structure exists
    Initialize-OSDeployCorePaths

    # Detect processor architecture — default to amd64, override only for arm64
    $arch = if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { 'arm64' } else { 'amd64' }
    Write-Verbose "[$($MyInvocation.MyCommand.Name)] Architecture: $arch"

    $adkName = 'adk-25h2'
    Write-Verbose "[$($MyInvocation.MyCommand.Name)] OS: Windows 11 25H2 — ADK: $adkName"

    # Stop before workflow changes when the user declines the hydration confirmation.
    #region Intro — describe workflow and ask for confirmation
    Write-Host ''
    Write-Host -ForegroundColor Cyan  'Invoke-OSDeployHydration will perform the following actions:'
    Write-Host ''
    Write-Host -ForegroundColor DarkCyan 'Install-OSDeploySoftware'
    Write-Host -ForegroundColor Gray  ' Install Windows ADK for Windows 11 25H2 (required)'
    Write-Host -ForegroundColor Gray  ' Install 7-Zip (required)'
    Write-Host -ForegroundColor Gray  ' Install Git for Windows (recommended)'
    Write-Host -ForegroundColor Gray  ' Install Microsoft Visual Studio Code (recommended)'
    Write-Host -ForegroundColor Gray  ' Install Microsoft Visual Studio Code Insiders (recommended)'
    if (-not (Test-IsVM)) {
        Write-Host -ForegroundColor Gray  ' Install Hyper-V (recommended — physical machines only)'
    }
    Write-Host ''
    Write-Host -ForegroundColor DarkCyan 'Update-OSDeployCoreESD'
    Write-Host -ForegroundColor Gray  ' Download the latest Windows 11 25H2 ESD for amd64 and arm64.'
    Write-Host ''
    Write-Host -ForegroundColor DarkCyan 'Update-OSDeployCoreRE'
    Write-Host -ForegroundColor Gray  ' Export Windows RE and stage supporting Windows OS content from the downloaded ESD.'
    Write-Host ''
    Write-Host -ForegroundColor DarkCyan 'Update-OSDeployCoreDrivers'
    Write-Host -ForegroundColor Gray  ' Download and expand the Dell WinPE DriverPack'
    Write-Host -ForegroundColor Gray  ' Download and expand the HP WinPE DriverPack'
    Write-Host -ForegroundColor Gray  ' Download and expand Intel Ethernet Drivers'
    Write-Host -ForegroundColor Gray  ' Download and expand Intel Wireless Drivers'
    Write-Host ''
    Write-Host -ForegroundColor DarkCyan 'Build-OSDeployBoot'
    Write-Host -ForegroundColor Gray  ' Build OSDeploy BootMedia for OSDCloud'
    if (-not (Test-IsVM)) {
    Write-Host ''
    Write-Host -ForegroundColor DarkCyan 'New-OSDeployHyperVM'
        Write-Host -ForegroundColor Gray  ' Test BootMedia in Hyper-V (physical machines only)'
    }
    Write-Host ''

    $caption = 'Invoke-OSDeployHydration – Start Workflow'
    $message = "The steps listed above will be performed in sequence.`nSome steps are interactive and will prompt before making changes.`nWorkflow will terminate if required software is declined.`n`nContinue?"
    if (-not ($Force -or $PSCmdlet.ShouldContinue($message, $caption))) {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Hydration workflow cancelled by user."
        return
    }
    #endregion

    # Install required components or stop; allow optional components to be skipped.
    #region Step 1 — Install software (required and optional components confirmed individually)

    # ── Required: Windows ADK 25H2 ────────────────────────────────────────────
    if (Test-InstallAdk25H2) {
        Write-Host -ForegroundColor Green "[$(Get-Date -Format s)] Windows ADK 25H2 is already installed"
    }
    else {
        $caption = 'Install Windows ADK 25H2 – Required'
        $message = "Component : Windows ADK 25H2`nRequired : Yes`nNote : Required to build WinPE boot images.`n`nInstall Windows ADK 25H2?"
        if ($Force -or $PSCmdlet.ShouldContinue($message, $caption)) {
            Install-OSDeploySoftware -Name 'adk-25h2' -Force
        }
        else {
            throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Windows ADK 25H2 is required. Installation declined — Invoke-OSDeployHydration cannot continue."
        }
    }

    # ── Required: 7-Zip ───────────────────────────────────────────────────────
    if (Test-Install7Zip) {
        Write-Host -ForegroundColor Green "[$(Get-Date -Format s)] 7-Zip is already installed"
    }
    else {
        $caption = 'Install 7-Zip – Required'
        $message = "Component : 7-Zip`nRequired : Yes`nNote : Required to extract WinPE app archives during boot image builds.`n`nInstall 7-Zip?"
        if ($Force -or $PSCmdlet.ShouldContinue($message, $caption)) {
            Install-OSDeploySoftware -Name '7zip' -Force
        }
        else {
            throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] 7-Zip is required. Installation declined — Invoke-OSDeployHydration cannot continue."
        }
    }

    # ── Optional: Git for Windows ─────────────────────────────────────────────
    if (Test-InstallGit) {
        Write-Host -ForegroundColor Green "[$(Get-Date -Format s)] Git for Windows is already installed"
    }
    else {
        $caption = 'Install Git for Windows – Optional'
        $message = "Component : Git for Windows`nRequired : No`nNote : Recommended for source control and OSD repo operations.`n`nInstall Git for Windows?"
        if ($Force -or $PSCmdlet.ShouldContinue($message, $caption)) {
            Install-SoftwareGitForWindows -NonInteractive
        }
        else {
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Git for Windows skipped by user."
        }
    }

    # ── Optional: Visual Studio Code ──────────────────────────────────────────
    if (Test-InstallVSCode) {
        Write-Host -ForegroundColor Green "[$(Get-Date -Format s)] Visual Studio Code is already installed"
    }
    else {
        $caption = 'Install Visual Studio Code – Optional'
        $message = "Component : Visual Studio Code`nRequired : No`nNote : Recommended for editing OSD scripts and configurations.`n`nInstall Visual Studio Code?"
        if ($Force -or $PSCmdlet.ShouldContinue($message, $caption)) {
            Install-OSDeploySoftware -Name 'code' -Force
        }
        else {
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Visual Studio Code skipped by user."
        }
    }

    # ── Optional: Visual Studio Code Insiders ─────────────────────────────────
    if (Test-InstallVSCodeInsiders) {
        Write-Host -ForegroundColor Green "[$(Get-Date -Format s)] Visual Studio Code Insiders is already installed"
    }
    else {
        $caption = 'Install Visual Studio Code Insiders – Optional'
        $message = "Component : Visual Studio Code Insiders`nRequired : No`nNote : Pre-release channel of VS Code with the latest features.`n`nInstall Visual Studio Code Insiders?"
        if ($Force -or $PSCmdlet.ShouldContinue($message, $caption)) {
            Install-OSDeploySoftware -Name 'code-insiders' -Force
        }
        else {
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Visual Studio Code Insiders skipped by user."
        }
    }

    # ── Optional: Hyper-V (physical machines only) ────────────────────────────
    if (-not (Test-IsVM)) {
        if (Test-HyperVEnabled) {
            Write-Host -ForegroundColor Green "[$(Get-Date -Format s)] Hyper-V is already installed"
        }
        else {
            $caption = 'Install Hyper-V – Optional'
            $message = "Component : Hyper-V`nRequired : No`nNote : Recommended for creating test VMs with New-OSDeployHyperVM.`n`nInstall Hyper-V?"
            if ($Force -or $PSCmdlet.ShouldContinue($message, $caption)) {
                Install-OSDeploySoftware -Name 'hyperv' -Force
            }
            else {
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Hyper-V skipped by user."
            }
        }
    }
    #endregion

    # Honor WhatIf and Confirm before downloading and importing Windows image content.
    #region Step 2 — Download and Import Windows ESD
    if ($PSCmdlet.ShouldProcess('Windows Enterprise ESD', 'Download and import Windows ESD')) {
        if ($arch -eq 'amd64') {
            Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] [INFO] ARM64 ESD is not downloaded automatically by Invoke-OSDeployHydration when Windows is running on amd64."
        }
        Update-OSDeployCoreESD -Architecture $arch @PSBoundParameters
        Update-OSDeployCoreRE -Architecture $arch
    }
    #endregion

    #region Step 3 — Update WinPE drivers for the detected architecture
    Update-OSDeployCoreDrivers -Architecture $arch @PSBoundParameters
    #endregion

    # Build from WinRE when available and use ADK WinPE as the child command's fallback.
    #region Step 4 — Build boot image (prefer WinRE, fall back to ADK WinPE)
    Build-OSDeployBoot -Auto
    #endregion

    # Offer the optional Hyper-V test only on physical hosts with a usable ISO.
    #region Step 5 — Test BootMedia in Hyper-V (physical machines only)
    if (-not (Test-IsVM)) {
        $isoPath = Join-Path $global:BuildMedia.MediaRootPath 'bootmedia.iso'
        if ((Test-HyperVEnabled) -and (Test-Path -Path $isoPath -PathType Leaf)) {
            $caption = 'Test BootMedia in Hyper-V – Optional'
            $message = "Action : New-OSDeployHyperVM`nISO : $isoPath`nNote : Creates a Hyper-V VM and boots the WinPE ISO to validate the build.`n`nTest BootMedia in Hyper-V?"
            if ($Force -or $PSCmdlet.ShouldContinue($message, $caption)) {
                New-OSDeployHyperVM -ISO $isoPath
            }
            else {
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] Hyper-V test skipped by user."
            }
        }
        else {
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Hyper-V test skipped — Hyper-V not enabled or ISO not found at '$isoPath'."
        }
    }
    #endregion

    Write-Host ''
    Write-Host -ForegroundColor Green "[$(Get-Date -format s)] [INFO] Invoke-OSDeployHydration completed successfully."
}