public/Invoke-OSDeployHydration.ps1

#Requires -PSEdition Core
#Requires -Version 7.4

<#
.SYNOPSIS
    Runs the complete OSDeploy hydration workflow end-to-end.
 
.DESCRIPTION
    Automates the full OSDeploy setup sequence on a supported Windows 11 25H2 machine:
 
      1. Validates system requirements (OS version, architecture, PowerShell edition and version).
      2. Prompts to install each software component individually. Windows ADK 25H2 and 7-Zip
         are required; Git for Windows, Visual Studio Code, and VS Code Insiders are optional.
         Required components that are declined will terminate the function.
      3. Downloads and imports the latest Windows Enterprise ESD for the detected architecture.
      4. Downloads WinPE drivers for the detected architecture.
      5. Builds a WinPE boot image named 'Hydra', using the newest available WinRE.
    Pass -Force to skip all software install prompts and run every step non-interactively.
    WinPE driver downloads process all matching sources automatically. If no WinRE source
    is found the ADK WinPE is used without prompting.
 
.EXAMPLE
    Invoke-OSDeployHydration
 
    Runs the full hydration workflow with interactive pickers at each step.
 
.EXAMPLE
    Invoke-OSDeployHydration -Force
 
    Runs the full hydration workflow non-interactively end-to-end.
 
.INPUTS
    None. This function does not accept pipeline input.
 
.OUTPUTS
    None.
 
.NOTES
    Author: David Segura
    Company: Recast Software
    Requires: Windows 11 25H2 (build 26200), PowerShell 7.4+ (MSI install), Windows ADK 25H2, Run as Administrator
#>

function Invoke-OSDeployHydration {
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [Parameter()]
        [switch]$Force
    )

    Write-OSDeployBanner

    #region Pre-flight checks
    if (-not (Test-IsWindows11)) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Windows 11 is required."
        return
    }
    if (-not (Test-IsWindows1125H2)) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Windows 11 25H2 (build 26200) is required."
        return
    }
    if (-not (Test-IsAdministrator)) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Administrator rights are required. Re-run PowerShell as Administrator and try again."
        return
    }
    if (-not (Test-PwshVersionMin)) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] PowerShell 7.4 or higher is required."
        return
    }
    if (-not (Test-PwshPSHome)) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] The MSI installation of PowerShell 7 is required."
        return
    }
    if (-not (Test-CommandCurl)) {
        Write-Warning "[$(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+."
        return
    }
    $RequiredOSDCloudVersion = [System.Version]'26.5.24.1'
    if (-not (Get-Command -Name 'Get-OSDCloudModuleVersion' -ErrorAction SilentlyContinue)) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDCloud module $RequiredOSDCloudVersion or newer is required."
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Install-Module -Name OSDCloud -Force -SkipPublisherCheck"
        return
    }
    $OSDCloudVersion = Get-OSDCloudModuleVersion
    if ($OSDCloudVersion -lt $RequiredOSDCloudVersion) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDCloud module $RequiredOSDCloudVersion or newer is required. Loaded version: $OSDCloudVersion"
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Install-Module -Name OSDCloud -Force -SkipPublisherCheck"
        return
    }
    #endregion

    # 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 "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Architecture: $arch"

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

    #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-OSDeployCoreOS'
    Write-Host -ForegroundColor Gray  ' Convert the downloaded Windows ESD and convert to Windows OS and WinRE sources for OSDeploy.'
    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 "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Hydration workflow cancelled by user."
        return
    }
    #endregion

    #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-GitForWindows -NonInteractive
        }
        else {
            Write-Verbose "[$(Get-Date -format s)] [$($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 "[$(Get-Date -format s)] [$($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 "[$(Get-Date -format s)] [$($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 "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Hyper-V skipped by user."
            }
        }
    }
    #endregion

    #region Step 2 — Download and Import Windows ESD
    if ($PSCmdlet.ShouldProcess('Windows Enterprise ESD', 'Download and import Windows ESD')) {
        Update-OSDeployCoreESD -Architecture $arch @PSBoundParameters
        Update-OSDeployCoreOS -Architecture $arch
    }
    #endregion

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

    #region Step 4 — Build boot image (prefer WinRE, fall back to ADK WinPE)
    Build-OSDeployBoot -Name 'Hydra' -Auto
    #endregion

    #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 "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Hyper-V test skipped by user."
            }
        }
        else {
            Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Hyper-V test skipped — Hyper-V not enabled or ISO not found at '$isoPath'."
        }
    }
    #endregion
}