dist/temp/WindowsUpdateTools/Public/Invoke-BuildUpgrade.ps1

function Invoke-BuildUpgrade {
    <#
    .SYNOPSIS
        Installs Windows build upgrades using the official Microsoft upgrade tool or ISO.
 
    .DESCRIPTION
        Downloads and executes the Microsoft Windows 11 upgrade assistant to perform
        build upgrades, or downloads and mounts a Windows 11 ISO for upgrade installation.
        This function can upgrade Windows 10 to Windows 11 or upgrade between Windows 11
        builds. The upgrade is performed silently with minimal user interaction.
 
    .PARAMETER DownloadPath
        The directory where the upgrade tool will be downloaded.
        Default: C:\temp
 
    .PARAMETER SkipCompatibilityCheck
        Skip the Windows 11 compatibility check before attempting upgrade.
        Use with caution - the upgrade may fail if hardware requirements aren't met.
 
    .PARAMETER LogPath
        Path to the log file for detailed operation logging.
        If not specified, logs will be written to the download directory.
 
    .PARAMETER CopyLogs
        Specify whether to copy upgrade logs to the download directory.
        Default: $true
 
    .PARAMETER Force
        Force the upgrade even if compatibility issues are detected.
        This parameter is passed to the upgrade tool as /auto upgrade.
 
    .PARAMETER UseISO
        Use ISO-based upgrade instead of the online upgrade assistant.
        Downloads Windows 11 ISO using Fido and performs upgrade from mounted ISO.
 
    .PARAMETER Edition
        Windows 11 edition to download when using ISO method.
        Valid values: Home, Pro, Enterprise, Education. Default: Pro
 
    .PARAMETER Language
        Language for the Windows 11 ISO when using ISO method.
        If not specified, the system's current display language will be detected automatically.
        Valid values: Arabic, Brazilian Portuguese, Bulgarian, Chinese (Simplified), Chinese (Traditional),
        Croatian, Czech, Danish, Dutch, English, English International, Estonian, Finnish, French,
        French Canadian, German, Greek, Hebrew, Hungarian, Italian, Japanese, Korean, Latvian,
        Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, Serbian Latin, Slovak,
        Slovenian, Spanish, Spanish (Mexico), Swedish, Thai, Turkish, Ukrainian
 
    .EXAMPLE
        Invoke-BuildUpgrade
         
        Performs a standard Windows build upgrade using default settings.
 
    .EXAMPLE
        Invoke-BuildUpgrade -DownloadPath "D:\Updates" -LogPath "D:\Updates\upgrade.log"
         
        Downloads the upgrade tool to D:\Updates and logs operations to upgrade.log.
 
    .EXAMPLE
        Invoke-BuildUpgrade -SkipCompatibilityCheck -Force -LogPath "C:\Logs\wu.log"
         
        Forces an upgrade while skipping compatibility checks.
 
    .EXAMPLE
        Invoke-BuildUpgrade -WhatIf
         
        Shows what would happen without actually performing the upgrade.
 
    .EXAMPLE
        Invoke-BuildUpgrade -UseISO -Edition "Pro" -DownloadPath "D:\Updates"
         
        Downloads Windows 11 Pro ISO and performs upgrade from mounted ISO.
 
    .EXAMPLE
        Invoke-BuildUpgrade -UseISO -Force -LogPath "C:\Logs\wu.log"
         
        Forces an ISO-based upgrade while bypassing compatibility checks.
 
    .OUTPUTS
        Returns a PSCustomObject with the following properties:
        - Success: Boolean indicating if the upgrade was initiated successfully
        - DownloadSuccess: Boolean indicating if the upgrade tool downloaded successfully
        - UpgradeInitiated: Boolean indicating if the upgrade process started
        - ToolPath: Path to the downloaded upgrade tool or mounted ISO
        - CommandLine: The command line used to execute the upgrade
        - ProcessId: Process ID of the upgrade process (if started)
        - LogPath: Path where upgrade logs will be stored
        - CompatibilityIssues: Array of any compatibility issues detected
        - ActionsPerformed: Array of actions that were performed
        - ErrorMessage: Error message if operation failed
        - UpgradeMethod: Method used for upgrade ("Assistant" or "ISO")
        - ISOPath: Path to downloaded ISO (when using ISO method)
        - MountedDrive: Drive letter of mounted ISO (when using ISO method)
 
    .NOTES
        - Requires Administrator privileges for optimal operation
        - The upgrade process may take several hours to complete
        - System will reboot multiple times during the upgrade process
        - Ensure adequate free disk space (at least 20GB recommended)
        - Network connectivity required to download the upgrade tool
        - This function initiates the upgrade but does not wait for completion
        - Monitor system progress through Windows Update or upgrade logs
         
        Author: WindowsUpdateTools Module
        Version: 1.0
         
    .LINK
        https://docs.microsoft.com/en-us/windows/deployment/upgrade/
    #>


    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter()]
        [ValidateScript({
            if (-not (Test-Path (Split-Path $_ -Parent))) {
                throw "Parent directory of '$_' does not exist"
            }
            $true
        })]
        [string]$DownloadPath = "C:\temp",

        [Parameter()]
        [switch]$SkipCompatibilityCheck,

        [Parameter()]
        [string]$LogPath,

        [Parameter()]
        [bool]$CopyLogs = $true,

        [Parameter()]
        [switch]$Force,

        [Parameter()]
        [switch]$UseISO,

        [Parameter()]
        [ValidateSet('Home', 'Pro', 'Enterprise', 'Education')]
        [string]$Edition = "Pro",

        [Parameter()]
        [ValidateSet('Arabic', 'Brazilian Portuguese', 'Bulgarian', 'Chinese (Simplified)', 'Chinese (Traditional)', 
                     'Croatian', 'Czech', 'Danish', 'Dutch', 'English', 'English International', 'Estonian', 
                     'Finnish', 'French', 'French Canadian', 'German', 'Greek', 'Hebrew', 'Hungarian', 'Italian', 
                     'Japanese', 'Korean', 'Latvian', 'Lithuanian', 'Norwegian', 'Polish', 'Portuguese', 'Romanian', 
                     'Russian', 'Serbian Latin', 'Slovak', 'Slovenian', 'Spanish', 'Spanish (Mexico)', 'Swedish', 
                     'Thai', 'Turkish', 'Ukrainian')]
        [string]$Language
    )

    # Initialize result object
    $result = [PSCustomObject]@{
        Success = $false
        DownloadSuccess = $false
        UpgradeInitiated = $false
        ToolPath = $null
        CommandLine = $null
        ProcessId = $null
        LogPath = $null
        CompatibilityIssues = @()
        ActionsPerformed = @()
        ErrorMessage = $null
        RebootRequired = $true  # Upgrades always require reboot
        UpgradeMethod = if ($UseISO) { "ISO" } else { "Assistant" }
        ISOPath = $null
        MountedDrive = $null
    }

    # Set up logging
    if (-not $LogPath) {
        $LogPath = Join-Path $DownloadPath "WU-BuildUpgrade-$(Get-Date -Format 'yyyyMMdd-HHmmss').log"
    }
    $result.LogPath = $LogPath

    Write-WULog -Message "Starting Windows Build Upgrade process" -LogPath $LogPath
    Write-WULog -Message "Upgrade Method: $($result.UpgradeMethod)" -LogPath $LogPath
    Write-WULog -Message "Download Path: $DownloadPath" -LogPath $LogPath
    Write-WULog -Message "Log Path: $LogPath" -LogPath $LogPath
    if ($UseISO) {
        Write-WULog -Message "Edition: $Edition" -LogPath $LogPath
        if ($Language) {
            Write-WULog -Message "Language: $Language" -LogPath $LogPath
        } else {
            Write-WULog -Message "Language: Auto-detect from system" -LogPath $LogPath
        }
    }

    try {
        # Check if running as administrator
        $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
        $isElevated = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
        
        if (-not $isElevated) {
            Write-WULog -Message "WARNING: Not running as Administrator - upgrade may have limited functionality" -Level Warning -LogPath $LogPath
            $result.ActionsPerformed += "Detected non-elevated execution"
        } else {
            Write-WULog -Message "Running with Administrator privileges" -LogPath $LogPath
            $result.ActionsPerformed += "Verified Administrator privileges"
        }

        # Create download directory if it doesn't exist
        if (-not (Test-Path $DownloadPath)) {
            Write-WULog -Message "Creating download directory: $DownloadPath" -LogPath $LogPath
            if ($PSCmdlet.ShouldProcess($DownloadPath, "Create Directory")) {
                New-Item -Path $DownloadPath -ItemType Directory -Force | Out-Null
                $result.ActionsPerformed += "Created download directory"
            }
        } else {
            Write-WULog -Message "Download directory exists: $DownloadPath" -LogPath $LogPath
        }

        # Check compatibility unless skipped
        if (-not $SkipCompatibilityCheck) {
            Write-WULog -Message "Performing Windows 11 compatibility check..." -LogPath $LogPath
            
            try {
                # Use existing compatibility check function if available
                if (Get-Command Test-WUWindows11Compatibility -ErrorAction SilentlyContinue) {
                    $compatCheck = Test-WUWindows11Compatibility -LogPath $LogPath
                    if (-not $compatCheck.Compatible -and -not $Force) {
                        $result.ErrorMessage = "Compatibility check failed: $($compatCheck.OverallAssessment)"
                        $result.CompatibilityIssues = $compatCheck.Issues
                        Write-WULog -Message "Compatibility check failed - use -Force to override" -Level Error -LogPath $LogPath
                        return $result
                    } elseif (-not $compatCheck.Compatible -and $Force) {
                        Write-WULog -Message "Compatibility issues detected but Force specified - proceeding" -Level Warning -LogPath $LogPath
                        $result.CompatibilityIssues = $compatCheck.Issues
                        $result.ActionsPerformed += "Bypassed compatibility check with Force"
                    }
                } else {
                    Write-WULog -Message "Compatibility check function not available - proceeding" -Level Warning -LogPath $LogPath
                }
            }
            catch {
                Write-WULog -Message "Error during compatibility check: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
                if (-not $Force) {
                    $result.ErrorMessage = "Compatibility check failed with error - use -Force to override"
                    return $result
                }
            }
        } else {
            Write-WULog -Message "Skipping compatibility check as requested" -LogPath $LogPath
            $result.ActionsPerformed += "Skipped compatibility check"
        }

        # Set up TLS 1.2 for download
        Write-WULog -Message "Configuring secure download protocol (TLS 1.2)" -LogPath $LogPath
        [Net.ServicePointManager]::SecurityProtocol = [Enum]::ToObject([Net.SecurityProtocolType], 3072)
        $result.ActionsPerformed += "Configured TLS 1.2 for download"

        # Branch logic based on upgrade method
        if ($UseISO) {
            # ISO-based upgrade path
            Write-WULog -Message "Starting ISO-based upgrade process..." -LogPath $LogPath
            
            try {
                # Download Windows 11 ISO using Fido
                Write-WULog -Message "Downloading Windows 11 ISO using Fido..." -LogPath $LogPath
                
                # Build parameters for Get-WUWindows11ISO
                $isoParams = @{
                    OutputPath = $DownloadPath
                    Edition = $Edition
                    LogPath = $LogPath
                }
                
                # Only add Language parameter if it was specified
                if ($Language) {
                    $isoParams.Language = $Language
                }
                
                $isoPath = Get-WUWindows11ISO @isoParams
                
                if (-not $isoPath -or -not (Test-Path $isoPath)) {
                    $result.ErrorMessage = "Failed to download Windows 11 ISO"
                    Write-WULog -Message $result.ErrorMessage -Level Error -LogPath $LogPath
                    return $result
                }
                
                $result.ISOPath = $isoPath
                $result.DownloadSuccess = $true
                $result.ActionsPerformed += "Downloaded Windows 11 ISO"
                Write-WULog -Message "ISO downloaded successfully: $isoPath" -LogPath $LogPath
                
                if ($PSCmdlet.ShouldProcess($isoPath, "Mount ISO and Run Setup")) {
                    # Mount the ISO
                    Write-WULog -Message "Mounting ISO: $isoPath" -LogPath $LogPath
                    $mountResult = Mount-DiskImage -ImagePath $isoPath -PassThru -ErrorAction Stop
                    $driveLetter = ($mountResult | Get-Volume).DriveLetter
                    $result.MountedDrive = "${driveLetter}:"
                    
                    Write-WULog -Message "ISO mounted successfully to drive: $($result.MountedDrive)" -LogPath $LogPath
                    $result.ActionsPerformed += "Mounted ISO to $($result.MountedDrive)"
                    
                    # Build setup command
                    $setupPath = "${driveLetter}:\setup.exe"
                    $result.ToolPath = $setupPath
                    
                    # Build setup arguments
                    $setupArgs = @("/auto", "upgrade", "/quiet", "/DynamicUpdate", "disable", "/showoobe", "None")
                    
                    if ($Force) {
                        # Additional force parameters can be added here if needed
                        Write-WULog -Message "Force parameter specified for ISO upgrade" -LogPath $LogPath
                    }
                    
                    $argumentString = $setupArgs -join " "
                    $result.CommandLine = "$setupPath $argumentString"
                    
                    Write-WULog -Message "Starting Windows setup from ISO..." -LogPath $LogPath
                    Write-WULog -Message "Setup command: $($result.CommandLine)" -LogPath $LogPath
                    Write-WULog -Message "WARNING: System will reboot multiple times during this process" -Level Warning -LogPath $LogPath
                    
                    # Start the setup process
                    try {
                        $process = Start-Process -FilePath $setupPath -ArgumentList $argumentString -PassThru -ErrorAction Stop
                        
                        $result.ProcessId = $process.Id
                        $result.UpgradeInitiated = $true
                        $result.Success = $true
                        $result.ActionsPerformed += "Started Windows setup from ISO"
                        
                        Write-WULog -Message "Setup process started successfully - Process ID: $($process.Id)" -LogPath $LogPath
                        
                        # Give the process a moment to initialize
                        Start-Sleep -Seconds 3
                        
                        # Check if process is still running
                        if (Get-Process -Id $process.Id -ErrorAction SilentlyContinue) {
                            Write-WULog -Message "Setup process is running normally" -LogPath $LogPath
                        } else {
                            Write-WULog -Message "Warning: Setup process exited quickly - check for user interaction or errors" -Level Warning -LogPath $LogPath
                        }
                    }
                    catch {
                        # Unmount ISO on setup failure
                        try {
                            Dismount-DiskImage -ImagePath $isoPath -ErrorAction SilentlyContinue
                            Write-WULog -Message "ISO unmounted due to setup failure" -LogPath $LogPath
                        }
                        catch {
                            Write-WULog -Message "Warning: Failed to unmount ISO after setup failure" -Level Warning -LogPath $LogPath
                        }
                        
                        $result.ErrorMessage = "Failed to start setup from ISO: $($_.Exception.Message)"
                        Write-WULog -Message $result.ErrorMessage -Level Error -LogPath $LogPath
                        return $result
                    }
                } else {
                    Write-WULog -Message "WhatIf: Would mount ISO and start Windows setup" -LogPath $LogPath
                    $result.ActionsPerformed += "WhatIf: Would mount ISO and start setup"
                    $result.Success = $true
                    return $result
                }
            }
            catch {
                $result.ErrorMessage = "Error during ISO-based upgrade: $($_.Exception.Message)"
                Write-WULog -Message $result.ErrorMessage -Level Error -LogPath $LogPath
                
                # Check if this is a Microsoft server restriction
                if ($_.Exception.Message -match "715-123130" -or $_.Exception.Message -match "banned from using this service") {
                    Write-WULog -Message "ISO download blocked by Microsoft servers - this is usually temporary" -Level Warning -LogPath $LogPath
                    Write-WULog -Message "Consider trying again later or using the standard upgrade assistant method" -Level Warning -LogPath $LogPath
                    Write-WULog -Message "To use standard method, run: Invoke-BuildUpgrade (without -UseISO parameter)" -Level Warning -LogPath $LogPath
                }
                
                return $result
            }
        } else {
            # Original upgrade assistant path
            Write-WULog -Message "Using Windows 11 upgrade assistant..." -LogPath $LogPath
            
            $webClient = New-Object System.Net.WebClient
            $url = "https://go.microsoft.com/fwlink/?linkid=2171764"
            $upgradeToolPath = Join-Path $DownloadPath "Win11Upgrade.exe"
            $result.ToolPath = $upgradeToolPath

            Write-WULog -Message "Downloading Windows 11 upgrade tool from Microsoft..." -LogPath $LogPath
            Write-WULog -Message "Source URL: $url" -LogPath $LogPath
            Write-WULog -Message "Destination: $upgradeToolPath" -LogPath $LogPath

            if ($PSCmdlet.ShouldProcess($url, "Download Windows 11 Upgrade Tool")) {
                try {
                    $webClient.DownloadFile($url, $upgradeToolPath)
                    
                    # Verify download
                    if (Test-Path $upgradeToolPath) {
                        $fileInfo = Get-Item $upgradeToolPath
                        Write-WULog -Message "Download successful - Size: $([math]::Round($fileInfo.Length / 1MB, 2)) MB" -LogPath $LogPath
                        $result.DownloadSuccess = $true
                        $result.ActionsPerformed += "Downloaded Windows 11 upgrade tool"
                    } else {
                        throw "Downloaded file not found at expected location"
                    }
                }
                catch {
                    $result.ErrorMessage = "Failed to download upgrade tool: $($_.Exception.Message)"
                    Write-WULog -Message $result.ErrorMessage -Level Error -LogPath $LogPath
                    return $result
                }
                finally {
                    $webClient.Dispose()
                }
            } else {
                Write-WULog -Message "WhatIf: Would download Windows 11 upgrade tool" -LogPath $LogPath
                $result.ActionsPerformed += "WhatIf: Would download upgrade tool"
                return $result
            }

            # Build command line arguments
            $arguments = @("/quietinstall", "/skipeula")
            
            if ($Force) {
                $arguments += "/auto"
                $arguments += "upgrade"
            } else {
                $arguments += "/auto"
                $arguments += "upgrade"
            }
            
            if ($CopyLogs) {
                $arguments += "/copylogs"
                $arguments += $DownloadPath
            }

            $argumentString = $arguments -join " "
            $result.CommandLine = "$upgradeToolPath $argumentString"

            Write-WULog -Message "Prepared upgrade command: $($result.CommandLine)" -LogPath $LogPath

            # Start the upgrade process
            Write-WULog -Message "Initiating Windows build upgrade..." -LogPath $LogPath
            Write-WULog -Message "WARNING: System will reboot multiple times during this process" -Level Warning -LogPath $LogPath

            if ($PSCmdlet.ShouldProcess($upgradeToolPath, "Start Windows Build Upgrade")) {
                try {
                    $process = Start-Process -FilePath $upgradeToolPath -ArgumentList $argumentString -PassThru -ErrorAction Stop
                    
                    $result.ProcessId = $process.Id
                    $result.UpgradeInitiated = $true
                    $result.Success = $true
                    $result.ActionsPerformed += "Started Windows build upgrade process"
                    
                    Write-WULog -Message "Upgrade process started successfully - Process ID: $($process.Id)" -LogPath $LogPath
                    Write-WULog -Message "Monitor progress through Windows Update or check upgrade logs in: $DownloadPath" -LogPath $LogPath
                    
                    # Give the process a moment to initialize
                    Start-Sleep -Seconds 3
                    
                    # Check if process is still running (it should be)
                    if (Get-Process -Id $process.Id -ErrorAction SilentlyContinue) {
                        Write-WULog -Message "Upgrade process is running normally" -LogPath $LogPath
                    } else {
                        Write-WULog -Message "Warning: Upgrade process exited quickly - check logs for details" -Level Warning -LogPath $LogPath
                    }
                }
                catch {
                    $result.ErrorMessage = "Failed to start upgrade process: $($_.Exception.Message)"
                    Write-WULog -Message $result.ErrorMessage -Level Error -LogPath $LogPath
                    return $result
                }
            } else {
                Write-WULog -Message "WhatIf: Would start Windows build upgrade process" -LogPath $LogPath
                $result.ActionsPerformed += "WhatIf: Would start upgrade process"
                $result.Success = $true  # WhatIf is considered successful
                return $result
            }
        }

        # Final success logging (applies to both upgrade methods)
        if ($UseISO) {
            Write-WULog -Message "Windows ISO upgrade initiated successfully" -LogPath $LogPath
            Write-WULog -Message "The upgrade will continue in the background and require multiple reboots" -LogPath $LogPath
            Write-WULog -Message "ISO mounted at: $($result.MountedDrive)" -LogPath $LogPath
        } else {
            Write-WULog -Message "Windows build upgrade initiated successfully" -LogPath $LogPath
            Write-WULog -Message "The upgrade will continue in the background and require multiple reboots" -LogPath $LogPath
            Write-WULog -Message "Logs will be available in: $DownloadPath" -LogPath $LogPath
        }

    }
    catch {
        $result.ErrorMessage = "Unexpected error during build upgrade: $($_.Exception.Message)"
        Write-WULog -Message $result.ErrorMessage -Level Error -LogPath $LogPath
        Write-WULog -Message "Stack trace: $($_.ScriptStackTrace)" -Level Error -LogPath $LogPath
    }

    return $result
}