Skip to main content

How to upgrade to Windows 11 with scripts

Updated over a week ago

SuperOps supports two methods to help you upgrade Windows devices to the latest Windows 11 version (currently 24H2):

  • Using the Windows 11 Installation Assistant

  • Using a Windows ISO file

Before you get started, here’s everything you need to know.


⚠️ Disclaimer: Using these scripts may result in data loss; run these scripts at your own risk and discretion.


Pre-upgrade checklist

System requirements

Please make sure the device(s) meet these minimum specs:

  • Processor: 1GHz or faster, 2+ cores, 64-bit

  • RAM: At least 4 GB

  • Storage: At least 64 GB of free space on system drive

  • TPM: Version 2.0 enabled

  • Firmware: UEFI with Secure Boot

Backup recommendations (Optional but strongly advised)

To avoid data loss:

  • Back up your files to OneDrive, Google Drive, or any external cloud storage.

  • Alternatively, move critical files to a non-system drive (e.g., D: or E:) to keep them safe from data loss.

1. Upgrading via Windows 11 Installation Assistant

This method uses Microsoft’s official assistant tool to perform the upgrade with minimal configuration.

How to run the script

1. Upload the script

Go to Modules > Scripts > New Script and upload the Windows Installation Assistant script below:


$bypassCheck = $false
$LogDir = "$env:ProgramData\_Windows_Upgrade\logs"
$UseTempFolder=$false
$DownloadDir ="$env:ProgramData\_Windows_Upgrade"
$File = "$DownloadDir\Windows11InstallationAssistant.exe"
$Url = "https://go.microsoft.com/fwlink/?linkid=2171764"

$regKeyPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\MoSetup",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\LabConfig",
"HKLM:\SYSTEM\Setup"
)
# If UseTempFolder switch is provided, override LogDir to use temp folder
if ($UseTempFolder) {
$LogDir = "$env:TEMP\_Windows_Upgrade\logs"
}
$LogFilePath = "$LogDir\Win11Compatibility_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
function Write-Log {
param (
[Parameter(Mandatory = $true)]
[string]$Message,
[string]$Type = "Information"
)
$DateTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$LogMessage = "$DateTime - $Type - $Message"
Add-Content -Path $LogFilePath -Value $LogMessage
Write-Host $LogMessage
}
function createLogFolder {
try {
if (-not (Test-Path $LogDir)) {
New-Item -ItemType Directory -Path $LogDir | Out-Null
Write-Log "Created log directory: $LogDir"
}
} catch {
Write-Log "Error creating log directory: $($_.Exception.Message)" "Error"
exit 1
}
}

function Set-CustomRegistryValue {
param (
[string]$regValueName,
[int]$regValueData = 1
)
if ( !$bypassCheck ) {
return
}
foreach ($path in $regKeyPaths) {
try {
if (-not (Test-Path $path)) {
Write-Log "Creating registry path: $path"
New-Item -Path $path -Force | Out-Null
}
Set-ItemProperty -Path $path -Name $regValueName -Value $regValueData -Type DWord
Write-Log "Set '$regValueName' to '$regValueData' at '$path'"
} catch {
Write-Log "Failed to set registry value at $path. Error: $_"
}
}
}
function Check-CPU {
param (
[ref]$Issues
)
Write-Log "Checking processor..."
try {
$cpu = Get-WmiObject -Class Win32_Processor
$cpuName = $cpu.Name
$cpuCores = $cpu.NumberOfCores
$cpuSpeed = [math]::Round($cpu.MaxClockSpeed / 1000, 2) # GHz
Write-Log "CPU: $cpuName, Cores: $cpuCores, Speed: $cpuSpeed GHz"
if ($cpuCores -lt 2 -or $cpuSpeed -lt 1) {
$Issues.Value += "CPU does not meet requirements (needs 2+ cores, 1+ GHz)."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedCPU"
return $false
} else {
Write-Log "CPU meets basic requirements. Verify against Microsoft's supported CPU list."
return $false
}
} catch {
Write-Log "Error checking CPU: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check CPU."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedCPU"
return $false
}
}
function Check-RAM {
param (
[ref]$Issues
)
Write-Log "Checking RAM..."
try {
$ram = [math]::Round((Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
Write-Log "RAM: $ram GB"
if ($ram -lt 4) {
$Issues.Value += "RAM is less than 4 GB."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedRAM"
return $false
}
return $true
} catch {
Write-Log "Error checking RAM: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check RAM."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedRAM"
return $false
}
}
function Check-Storage {
param (
[ref]$Issues
)
Write-Log "Checking storage..."
try {
$systemDrive = $env:SystemDrive
$disk = Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceID='$systemDrive'"
if ($disk) {
$freeSpace = [math]::Round($disk.FreeSpace / 1GB, 2)
Write-Log "$systemDrive Free Space: $freeSpace GB"
if ($freeSpace -lt 64) {
$Issues.Value += "Free storage on $systemDrive is less than 64 GB."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedDisk"
return $false
}
return $true
} else {
throw "System drive $systemDrive not found."
}
} catch {
Write-Log "Error checking storage: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check storage on $systemDrive."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedDisk"
return $false
}
}
function Check-TPM {
param (
[ref]$Issues
)
Write-Log "Checking TPM..."
try {
$tpm = Get-WmiObject -Namespace "Root\CIMV2\Security\MicrosoftTpm" -Class Win32_Tpm
if ($tpm) {
$tpmVersion = $tpm.SpecVersion
Write-Log "TPM Version: $tpmVersion"
if ($tpmVersion -notlike "*2.0*") {
$Issues.Value += "TPM version is not 2.0."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedTPMOrCPU"
return $false
}
return $true
} else {
$Issues.Value += "No TPM detected."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedTPMOrCPU"
Write-Log "TPM not found. Check BIOS/UEFI settings."
return $false
}
} catch {
Write-Log "Error checking TPM: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check TPM."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedTPMOrCPU"
return $false
}
}
function Check-SecureBoot {
param (
[ref]$Issues
)
Write-Log "Checking Secure Boot..."
try {
$secureBoot = Confirm-SecureBootUEFI
Write-Log "Secure Boot Enabled: $secureBoot"
if (-not $secureBoot) {
$Issues.Value += "Secure Boot is not enabled."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedSecureBoot"
return $false
}
return $true
} catch {
Write-Log "Error checking Secure Boot: $($_.Exception.Message)" "Error"
$Issues.Value += "Secure Boot not supported or disabled. Check BIOS/UEFI."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedSecureBoot"
return $false
}
}
function Check-UEFI {
param (
[ref]$Issues
)
Write-Log "Checking UEFI firmware..."
try {
$firmware = Get-WmiObject -Class Win32_ComputerSystem
$bootMode = $firmware.BootupState
Write-Log "Boot Mode: $bootMode"
if ($bootMode -notlike "*UEFI*") {
$Issues.Value += "System is not using UEFI firmware."
return $false
}
return $true
} catch {
Write-Log "Error checking firmware: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check firmware."
return $false
}
}
function Check-WindowsVersion {
param (
[ref]$Issues
)
Write-Log "Checking Windows version..."
try {
$os = Get-WmiObject -Class Win32_OperatingSystem
$osVersion = $os.Version
Write-Log "Windows Version: $osVersion"
if ([version]$osVersion -lt [version]"10.0.19041") {
$Issues.Value += "Windows 10 version is older than 2004."
return $false
}
return $true
} catch {
Write-Log "Error checking Windows version: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check Windows version."
return $false
}
}
function checkCompatibility {
# Initialize compatibility status and issues list
$IsCompatible = $true
$Issues = @()
# Run individual checks
$cpuResult = Check-CPU -Issues ([ref]$Issues)
$ramResult = Check-RAM -Issues ([ref]$Issues)
$storageResult = Check-Storage -Issues ([ref]$Issues)
$tpmResult = Check-TPM -Issues ([ref]$Issues)
$secureBootResult = Check-SecureBoot -Issues ([ref]$Issues)
$uefiResult = Check-UEFI -Issues ([ref]$Issues)
$windowsVersionResult = Check-WindowsVersion -Issues ([ref]$Issues)

# Combine results to determine overall compatibility
$IsCompatible = $cpuResult -and $ramResult -and $storageResult -and $tpmResult -and $secureBootResult -and $uefiResult -and $windowsVersionResult
# Summary
Write-Log "Compatibility Check Summary:"
if ($IsCompatible) {
Write-Log "System appears compatible with Windows 11." "Success"
} else {
Write-Log "System is NOT compatible with Windows 11." "Error"
Write-Log "Issues found:"
foreach ($issue in $Issues) {
Write-Log " - $issue" "Error"
}
}
}
function DownloadExe {
try {
Write-Log "Downloading using invoke-WebRequest"
invoke-WebRequest -Uri "$Url" -Outfile "$File"
if (!(Test-Path $File)){
Write-Log "Downloaded File doesn't exists"
[Environment]::Exit(1)
}
Write-Log "Downloaded Successfully"
}
catch {
Write-Log "Error while downloading superops"
Write-Log $_.Exception.Message
[Environment]::Exit(1)
}

}


function upgradeProcess{

Write-Log "Starting silent upgrade to Windows 11..."
$Arguments = "/Install /QuietInstall /SkipEULA /copylogs $LogDir"
try {
$process = Start-Process -FilePath $File -ArgumentList $Arguments -Wait -PassThru -NoNewWindow
if ($process.ExitCode -eq 0) {
Write-Log "Upgrade process completed successfully."
Remove-Item $File -Force
} else {
Write-Log "Upgrade process failed with exit code: $($process.ExitCode)" "Error"
exit $process.ExitCode
}
} catch {
Write-Log "Error during upgrade: $($_.Exception.Message)" "Error"
exit 1
}
}
function main {
createLogFolder
checkCompatibility
DownloadExe
upgradeProcess
Write-Log "Script execution completed. It automatically reboot the machine to get upgraded Windows"
}
main

2. Set script timeout
Recommended timeout: a maximum of 300 minutes Adjust this timeout period depending on device performance.

3. Run as System User
Make sure to run the script with System-level permissions.

4. Ensure uninterrupted internet connectivity
A stable connection for at least 2 hours is required.

🚨 Important:

The system will automatically reboot after the upgrade. Please ensure you warn users in advance to avoid any potential data loss.


2. Upgrading via ISO File

This method is useful if you want to upgrade to a specific Windows version using a downloaded ISO.

How to run the script

1. Download the ISO

  • Download the desired ISO from Microsoft’s official site.

  • Save it to a shared or accessible location.

2. Upload the Script

Go to Modules > Scripts > Add Script and upload the ISO-based upgrade script.

$bypassCheck = $false
$LogDir = "$env:ProgramData\_Windows_Upgrade\logs"
$UseTempFolder=$false
$regKeyPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\MoSetup",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\LabConfig",
"HKLM:\SYSTEM\Setup"
)
# If UseTempFolder switch is provided, override LogDir to use temp folder
if ($UseTempFolder) {
$LogDir = "$env:TEMP\_Windows_Upgrade\logs"
}
$LogFilePath = "$LogDir\Win11Compatibility_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
if ([string]::IsNullOrEmpty($IsoPath)) {
Write-Host "IsoPath field is mandatory"
[Environment]::Exit(1)
}
function Write-Log {
param (
[Parameter(Mandatory = $true)]
[string]$Message,
[string]$Type = "Information"
)
$DateTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$LogMessage = "$DateTime - $Type - $Message"
Add-Content -Path $LogFilePath -Value $LogMessage
Write-Host $LogMessage
}
function createLogFolder {
try {
if (-not (Test-Path $LogDir)) {
New-Item -ItemType Directory -Path $LogDir | Out-Null
Write-Log "Created log directory: $LogDir"
}
} catch {
Write-Log "Error creating log directory: $($_.Exception.Message)" "Error"
exit 1
}
}

function Set-CustomRegistryValue {
param (
[string]$regValueName,
[int]$regValueData = 1
)
if ( !$bypassCheck ) {
return
}
foreach ($path in $regKeyPaths) {
try {
if (-not (Test-Path $path)) {
Write-Log "Creating registry path: $path"
New-Item -Path $path -Force | Out-Null
}
Set-ItemProperty -Path $path -Name $regValueName -Value $regValueData -Type DWord
Write-Log "Set '$regValueName' to '$regValueData' at '$path'"
} catch {
Write-Log "Failed to set registry value at $path. Error: $_"
}
}
}
function Check-CPU {
param (
[ref]$Issues
)
Write-Log "Checking processor..."
try {
$cpu = Get-WmiObject -Class Win32_Processor
$cpuName = $cpu.Name
$cpuCores = $cpu.NumberOfCores
$cpuSpeed = [math]::Round($cpu.MaxClockSpeed / 1000, 2) # GHz
Write-Log "CPU: $cpuName, Cores: $cpuCores, Speed: $cpuSpeed GHz"
if ($cpuCores -lt 2 -or $cpuSpeed -lt 1) {
$Issues.Value += "CPU does not meet requirements (needs 2+ cores, 1+ GHz)."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedCPU"
return $false
} else {
Write-Log "CPU meets basic requirements. Verify against Microsoft's supported CPU list."
return $false
}
} catch {
Write-Log "Error checking CPU: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check CPU."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedCPU"
return $false
}
}
function Check-RAM {
param (
[ref]$Issues
)
Write-Log "Checking RAM..."
try {
$ram = [math]::Round((Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
Write-Log "RAM: $ram GB"
if ($ram -lt 4) {
$Issues.Value += "RAM is less than 4 GB."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedRAM"
return $false
}
return $true
} catch {
Write-Log "Error checking RAM: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check RAM."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedRAM"
return $false
}
}
function Check-Storage {
param (
[ref]$Issues
)
Write-Log "Checking storage..."
try {
$systemDrive = $env:SystemDrive
$disk = Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceID='$systemDrive'"
if ($disk) {
$freeSpace = [math]::Round($disk.FreeSpace / 1GB, 2)
Write-Log "$systemDrive Free Space: $freeSpace GB"
if ($freeSpace -lt 64) {
$Issues.Value += "Free storage on $systemDrive is less than 64 GB."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedDisk"
return $false
}
return $true
} else {
throw "System drive $systemDrive not found."
}
} catch {
Write-Log "Error checking storage: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check storage on $systemDrive."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedDisk"
return $false
}
}
function Check-TPM {
param (
[ref]$Issues
)
Write-Log "Checking TPM..."
try {
$tpm = Get-WmiObject -Namespace "Root\CIMV2\Security\MicrosoftTpm" -Class Win32_Tpm
if ($tpm) {
$tpmVersion = $tpm.SpecVersion
Write-Log "TPM Version: $tpmVersion"
if ($tpmVersion -notlike "*2.0*") {
$Issues.Value += "TPM version is not 2.0."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedTPMOrCPU"
return $false
}
return $true
} else {
$Issues.Value += "No TPM detected."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedTPMOrCPU"
Write-Log "TPM not found. Check BIOS/UEFI settings."
return $false
}
} catch {
Write-Log "Error checking TPM: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check TPM."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedTPMOrCPU"
return $false
}
}
function Check-SecureBoot {
param (
[ref]$Issues
)
Write-Log "Checking Secure Boot..."
try {
$secureBoot = Confirm-SecureBootUEFI
Write-Log "Secure Boot Enabled: $secureBoot"
if (-not $secureBoot) {
$Issues.Value += "Secure Boot is not enabled."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedSecureBoot"
return $false
}
return $true
} catch {
Write-Log "Error checking Secure Boot: $($_.Exception.Message)" "Error"
$Issues.Value += "Secure Boot not supported or disabled. Check BIOS/UEFI."
Set-CustomRegistryValue "AllowUpgradesWithUnsupportedSecureBoot"
return $false
}
}
function Check-UEFI {
param (
[ref]$Issues
)
Write-Log "Checking UEFI firmware..."
try {
$firmware = Get-WmiObject -Class Win32_ComputerSystem
$bootMode = $firmware.BootupState
Write-Log "Boot Mode: $bootMode"
if ($bootMode -notlike "*UEFI*") {
$Issues.Value += "System is not using UEFI firmware."
return $false
}
return $true
} catch {
Write-Log "Error checking firmware: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check firmware."
return $false
}
}
function Check-WindowsVersion {
param (
[ref]$Issues
)
Write-Log "Checking Windows version..."
try {
$os = Get-WmiObject -Class Win32_OperatingSystem
$osVersion = $os.Version
Write-Log "Windows Version: $osVersion"
if ([version]$osVersion -lt [version]"10.0.19041") {
$Issues.Value += "Windows 10 version is older than 2004."
return $false
}
return $true
} catch {
Write-Log "Error checking Windows version: $($_.Exception.Message)" "Error"
$Issues.Value += "Failed to check Windows version."
return $false
}
}
function checkCompatibility {
# Initialize compatibility status and issues list
$IsCompatible = $true
$Issues = @()
# Run individual checks
$cpuResult = Check-CPU -Issues ([ref]$Issues)
$ramResult = Check-RAM -Issues ([ref]$Issues)
$storageResult = Check-Storage -Issues ([ref]$Issues)
$tpmResult = Check-TPM -Issues ([ref]$Issues)
$secureBootResult = Check-SecureBoot -Issues ([ref]$Issues)
$uefiResult = Check-UEFI -Issues ([ref]$Issues)
$windowsVersionResult = Check-WindowsVersion -Issues ([ref]$Issues)

# Combine results to determine overall compatibility
$IsCompatible = $cpuResult -and $ramResult -and $storageResult -and $tpmResult -and $secureBootResult -and $uefiResult -and $windowsVersionResult
# Summary
Write-Log "Compatibility Check Summary:"
if ($IsCompatible) {
Write-Log "System appears compatible with Windows 11." "Success"
} else {
Write-Log "System is NOT compatible with Windows 11." "Error"
Write-Log "Issues found:"
foreach ($issue in $Issues) {
Write-Log " - $issue" "Error"
}
}
}
function mountAndUpgradeProcess {
try {
$mount = Mount-DiskImage -ImagePath $IsoPath -PassThru
$mountDrive = ($mount | Get-Volume).DriveLetter
$setupPath = "$($mountDrive):\setup.exe"
Write-Log "ISO mounted at drive $mountDrive"
Write-Log "SetupPath $setupPath"
} catch {
Write-Log "Error mounting ISO: $($_.Exception.Message)" "Error"
exit 1
}
if (-not (Test-Path "$setupPath")) {
Write-Log "setup.exe not found at $setupPath"
exit 1
}
Write-Log "Starting silent upgrade to Windows 11..."
Write-Host "setup $setupPath"
$Arguments = "/auto upgrade /quiet /noreboot /eula accept /compat ignorewarning /copylogs $LogDir"
try {
Write-Host "setup $setupPath"
$process = Start-Process -FilePath "$setupPath" -ArgumentList $Arguments -Wait -PassThru -NoNewWindow
if ($process.ExitCode -eq 0) {
Write-Log "Upgrade process completed successfully."
} else {
Write-Log "Upgrade process failed with exit code: $($process.ExitCode)" "Error"
exit $process.ExitCode
}
} catch {
Write-Log "Error during upgrade: $($_.Exception.Message)" "Error"
exit 1
} finally {
Write-Log "Dismounting ISO..."
Dismount-DiskImage -ImagePath $IsoPath
}
}
function main {
createLogFolder
checkCompatibility
mountAndUpgradeProcess
Write-Log "Script execution completed. Reboot the machine to get upgraded Windows"
}
main

3. Set a Runtime Variable

Define a variable named IsoPath and enter the full file path (e.g., D:\ISOs\Win11.iso).

4. Set script timeout

Recommended timeout: minimum of 180 minutes; adjust this timeout period based on system performance

5. Run as System User
Ensure that the script runs with System-level permissions.

6. Ensure Network Stability

Keep the network stable during the script execution, especially if ISO is on a shared location.

Reboot info:

This script does not auto-reboot. You'll need to reboot the machine manually after the upgrade is complete.


Bypassing compatibility checks

If your devices don’t meet minimum requirements, you can optionally bypass those checks. Here’s how:

Open the script and set $bypassCheck = $true

⚠️ Note: Use this only when absolutely necessary. Skipping checks could result in unstable or unsupported installations.


Logging & troubleshooting

Both scripts:

  • Create detailed log files under:

    • C:\ProgramData\_Windows_Upgrade\logs (default)

    • Or %TEMP%\_Windows_Upgrade\logs if UseTempFolder is enabled.

  • Provide output on device compatibility, upgrade status, and error codes (if any).


Best practices

  • Always test scripts on a small batch of devices before mass deployment.

  • Inform users about potential reboots and data safety steps.

  • Monitor the upgrade status using SuperOps’ script reports.

Did this answer your question?