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 } |