Windows 11

Windows 11 Upgrade Script | RMM Deployment



Table of Contents

Intro

Windows 10 will reach end of support on October 14, 2025, here is a script to make the upgrade process easier. I first tried to make a process that would utilize the PC Health Check app from Microsoft, the official way to confirm but found the app was not able to scripted and forced to run via command line. Instead I created a script with the help of ChatGPT that will check various attributes in Windows and if they pass will run the upgrade.

Checks

The script will first check if the OS is Windows 10 and not a server OS, exit if Windows 11. Then it will check the following:

  • Check for TPM 2.0.
  • Check for UEFI.
  • Check for Secure Boot.
  • Check for free space.
  • Check for compatible Intel or AMD processor.

The script will output which checks fail for a machine. I configured an Extra Data Value in ConnectWise for this so I can quickly see if a machine can be upgraded or not and why it failed.

The script downloads the Windows11 Installation Assistant to the ConnectWise working directory at: C:\Windows\LTSvc which is automatically created during install of the agent so change that path accordingly for your needs.

Here are multiple examples of what the EDV reports after the script ran on various devices.

Here are the requirements for Windows 11.


Script

PowerShell
# Check if the OS is Windows 10 and not a Server OS
$OSInfo = Get-ComputerInfo
if ($OSInfo.OsArchitecture -eq "64-bit" -and $OSInfo.OsName -match "Windows 10" -and $OSInfo.OsName -notmatch "Server") {
    Write-Host "Windows 10 detected, continuing compatibility check..." -ForegroundColor Cyan
} else {
    Write-Host "This script is intended for Windows 10 only. It will not run on Windows Server or other versions." -ForegroundColor Red
    exit
}

# Check if the OS is Windows 11
if ($OSInfo.OsArchitecture -eq "64-bit" -and $OSInfo.OsName -match "Windows 11") {
    Write-Host "Windows 11 is already installed." -ForegroundColor Green
    exit
}

Function Check-TPM {
    try {
        $TPMDevice = Get-CimInstance -Namespace "Root\CIMv2\Security\MicrosoftTpm" -ClassName Win32_Tpm | Where-Object { $_.SpecVersion -match "2.0" }
        if ($TPMDevice) {
            return $true
        } else {
            Write-Host "TPM 2.0 is not present or enabled." -ForegroundColor Red
            return $false
        }
    } catch {
        Write-Host "Error retrieving TPM information. Ensure the TPM module is accessible." -ForegroundColor Red
        return $false
    }
}

Function Check-UEFI {
    try {
        $FirmwareType = (Get-ComputerInfo -Property BiosFirmwareType | Format-Table -HideTableHeaders | Out-String).Trim().ToUpper()
        if ($FirmwareType -ne "UEFI") {
            Write-Host "System is not using UEFI." -ForegroundColor Red
            return $false
        }
        return $true
    } catch {
        Write-Host "Error checking firmware type. Ensure system supports UEFI." -ForegroundColor Red
        return $false
    }
}

Function Check-SecureBoot {
    try {
        $SecureBootState = Confirm-SecureBootUEFI
        if (-not $SecureBootState) {
            Write-Host "Secure Boot is not enabled." -ForegroundColor Red
            return $false
        }
        return $true
    } catch {
        Write-Host "Error checking Secure Boot state. Ensure Secure Boot is supported." -ForegroundColor Red
        return $false
    }
}

Function Check-FreeSpace {
    try {
        $CDrive = Get-PSDrive -Name C
        $RequiredSpaceGB = 64 # Minimum free space required in GB
        $FreeSpaceGB = [math]::Round($CDrive.Free / 1GB, 2)

        if ($FreeSpaceGB -lt $RequiredSpaceGB) {
            Write-Host "Not enough free space on C: drive ($FreeSpaceGB GB). Required: $RequiredSpaceGB GB." -ForegroundColor Red
            return $false
        }
        return $true
    } catch {
        Write-Host "Error checking disk space. Ensure the C: drive is accessible." -ForegroundColor Red
        return $false
    }
}

Function Check-Processor {
    try {
        $ProcessorName = (Get-CimInstance -ClassName Win32_Processor).Name.Trim()

        # Updated lists of supported processors for Intel and AMD
        $SupportedIntelProcessors = @(
            "Intel.*Core.*i3-8",
            "Intel.*Core.*i5-8",
            "Intel.*Core.*i7-8",
            "Intel.*Core.*i9-8",
            "Intel.*Core.*i3-9",
            "Intel.*Core.*i5-9",
            "Intel.*Core.*i7-9",
            "Intel.*Core.*i9-9",
            "Intel.*Core.*i3-10",
            "Intel.*Core.*i5-10",
            "Intel.*Core.*i7-10",
            "Intel.*Core.*i9-10",
            "Intel.*Core.*i3-11",
            "Intel.*Core.*i5-11",
            "Intel.*Core.*i7-11",
            "Intel.*Core.*i9-11",
            "Intel.*Core.*i3-12",
            "Intel.*Core.*i5-12",
            "Intel.*Core.*i7-12",
            "Intel.*Core.*i9-12",
            "Intel.*Core.*i3-13",
            "Intel.*Core.*i5-13",
            "Intel.*Core.*i7-13",
            "Intel.*Core.*i9-13",
            "Intel.*Xeon.*E-2",
            "Intel.*Xeon.*W-1",
            "Intel.*Pentium.*G",
            "Intel.*Celeron.*G"
        )

        $SupportedAMDProcessors = @(
            "AMD.*Ryzen.*3.*2300",
            "AMD.*Ryzen.*5.*2500",
            "AMD.*Ryzen.*7.*2700",
            "AMD.*Ryzen.*9.*2900",
            "AMD.*Ryzen.*3.*3200",
            "AMD.*Ryzen.*5.*3400",
            "AMD.*Ryzen.*7.*3700",
            "AMD.*Ryzen.*9.*3900",
            "AMD.*Ryzen.*3.*4300",
            "AMD.*Ryzen.*5.*4500",
            "AMD.*Ryzen.*7.*4700",
            "AMD.*Ryzen.*9.*4900",
            "AMD.*Ryzen.*3.*5300",
            "AMD.*Ryzen.*5.*5500",
            "AMD.*Ryzen.*7.*5700",
            "AMD.*Ryzen.*9.*5900",
            "AMD.*EPYC",
            "AMD.*Athlon.*Gold",
            "AMD.*Athlon.*Silver"
        )

        # Combine both Intel and AMD processor lists into a single pattern
        $SupportedProcessors = ($SupportedIntelProcessors + $SupportedAMDProcessors) -join "|"

        # Match the processor name against the combined pattern
        if ($ProcessorName -match $SupportedProcessors) {
            return $true
        } else {
            Write-Host "Unsupported processor detected: $ProcessorName" -ForegroundColor Red
            return $false
        }
    } catch {
        Write-Host "Error checking processor information. Ensure the system allows querying the processor details." -ForegroundColor Red
        return $false
    }
}

Write-Host "Starting Windows 11 Compatibility Check..." -ForegroundColor Cyan

# Run all checks
$TPMCheck = Check-TPM
$UEFICheck = Check-UEFI
$SecureBootCheck = Check-SecureBoot
$FreeSpaceCheck = Check-FreeSpace
$ProcessorCheck = Check-Processor

# Final compatibility evaluation
if ($TPMCheck -and $SecureBootCheck -and $UEFICheck -and $FreeSpaceCheck -and $ProcessorCheck) {
    Write-Host "System is compatible with Windows 11." -ForegroundColor Green

    ########### Begin Script for Windows 11 Upgrade

    $dir = 'C:\Windows\LTSvc\Win11Upgrade'
    $url = 'https://go.microsoft.com/fwlink/?linkid=2171764'
    $file = "$dir\Windows11InstallationAssistant.exe"
    mkdir $dir -force
    cd $dir 
    Start-BitsTransfer -Source $url -Destination $file
    .\Windows11InstallationAssistant.exe /QuietInstall /SkipEULA /NoRestartUI

    ########### End Script

} else {
    Write-Host "System does not meet all requirements for Windows 11." -ForegroundColor Red
}

Conclusion

Hopefully this helps, I tested on a couple machines and it worked great!

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *