> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getprimo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrade from Windows 10 to Windows 11

> Learn how to check Windows 10 device eligibility for an upgrade to Windows 11 and automate the upgrade process using FleetDM policies and PowerShell scripts, including device classification, eligibility assessment, and triggering the upgrade installation.

This article details the process to **check which Windows 10 devices are eligible for an upgrade to Windows 11**, and **automatically initiate that upgrade** using **FleetDM policies and PowerShell scripts**.

The procedure includes:

* Detecting and storing required system components (TPM 2.0 and Secure Boot) in the registry;
* Validating hardware and software requirements to determine upgrade eligibility;
* Running an automated download and silent installation of Windows 11 on compliant devices.

## 1. Classify devices with TPM and Secure Boot present

## 1.1. Create the detection policy (FleetDM registry)

Use the following query to verify the presence of registry keys that will store **TPM** and **Secure Boot** status:

```sql theme={null}
SELECT 1 WHERE EXISTS (
      SELECT 1 FROM registry
      WHERE path = 'HKEY_LOCAL_MACHINE\Software\FleetDM\TPMVersion'
    )
    AND EXISTS (
      SELECT 1 FROM registry
      WHERE path = 'HKEY_LOCAL_MACHINE\Software\FleetDM\SecureBoot'
    );
```

## 1.2. Run the associated script (populate the registry)

Attach this PowerShell script to the policy above. It detects TPM version and Secure Boot status, then writes them to the Windows registry for FleetDM.

```powershell theme={null}
$TPM = Get-WmiObject -Namespace "Root\CIMv2\Security\MicrosoftTpm" -Class Win32_Tpm
$SecureBoot = Confirm-SecureBootUEFI

$TPMVersion = $TPM.SpecVersion
$SecureBootEnabled = if ($SecureBoot) { 1 } else { 0 }

# Write values to the registry (example)
New-Item -Path "HKLM:\Software\FleetDM" -Force | Out-Null
Set-ItemProperty -Path "HKLM:\Software\FleetDM" -Name "TPMVersion" -Value $TPMVersion
Set-ItemProperty -Path "HKLM:\Software\FleetDM" -Name "SecureBoot" -Value $SecureBootEnabled
```

Expected result: `HKLM\Software\FleetDM\TPMVersion` and `HKLM\Software\FleetDM\SecureBoot` are created/updated.

## 2. Assess a Windows 10 device's eligibility for Windows 11

Create the following policy to validate minimum requirements (RAM, CPU cores, architecture, TPM 2.0, Secure Boot) and exclude non-compliant devices.

```sql theme={null}
SELECT 1 FROM os_version as os JOIN system_info as si WHERE
    os.name NOT LIKE 'Microsoft Windows 10%'
    OR si.physical_memory < 4 * 1024 * 1024 * 1024
    OR si.cpu_physical_cores < 2
    OR si.cpu_type NOT LIKE '%x86_64%'
    OR NOT EXISTS (
      SELECT * FROM registry
      WHERE path = 'HKEY_LOCAL_MACHINE\Software\FleetDM\TPMVersion'
        AND data LIKE '2%'
    )
    OR NOT EXISTS (
      SELECT * FROM registry
      WHERE path = 'HKEY_LOCAL_MACHINE\Software\FleetDM\SecureBoot'
        AND data = '1'
    );
```

Interpretation: the query returns 1 if the device is not a compliant Windows 10. Use it as a non-compliance policy to keep only eligible devices (those for which the query does not return 1).

## 3. Trigger the Windows 11 upgrade (eligible devices)

Attach the deployment script below to compliant devices. It downloads the Windows 11 ISO, mounts the image, copies sources locally, and schedules a silent setup under **SYSTEM**.

```powershell theme={null}
# Imports
Import-Module BitsTransfer

# Variables
$uri = "https://production-bucket-public-files.s3.eu-west-3.amazonaws.com/Win11_24H2_French_x64.iso" # French locale — replace with the appropriate locale ISO for your region
$destination = "C:\Win11.iso"
$log = "C:\logs\download_win11_iso.log"

# Create the logs folder if it doesn't exist
if (-not (Test-Path -Path "C:\logs")) {
    New-Item -Path "C:\logs" -ItemType Directory | Out-Null
}

# Start the download
try {
    "`n[$(Get-Date)] Starting download..." | Out-File -Append $log
    if (-not (Test-Path $destination)){
      Start-BitsTransfer -Source $uri -Destination $destination
    }
    "`n[$(Get-Date)] Download completed successfully." | Out-File -Append $log
} catch {
    "`n[$(Get-Date)] Error during download: $_" | Out-File -Append $log
}

# Additional variables
$isoPath = "C:\Win11.iso"
$setupFolder = "C:\Temp\Win11Files"
$taskName = "Win11SilentUpgrade"

# Step 1 - Mount the ISO
Mount-DiskImage -ImagePath $isoPath -PassThru | Out-Null
Start-Sleep -Seconds 2
$vol = Get-Volume -DiskImage (Get-DiskImage -ImagePath $isoPath)
$driveLetter = $vol.DriveLetter

# Step 2 - Copy ISO content locally
New-Item -ItemType Directory -Force -Path $setupFolder | Out-Null
Copy-Item "$driveLetter`:\*" -Destination $setupFolder -Recurse

# Unmount the ISO
Dismount-DiskImage -ImagePath $isoPath

# Step 3 - Create and schedule the installation task
$action = New-ScheduledTaskAction -Execute "C:\Temp\Win11Files\setup.exe" -Argument "/auto upgrade /migratedrivers none /resizerecoverypartition enable /dynamicupdate disable /eula accept /quiet /noreboot /uninstall disable /compat ignorewarning /copylogs C:\logs\WinSetup.log"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$task = New-ScheduledTask -Action $action -Principal $principal -Trigger (New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1))

Register-ScheduledTask -TaskName $taskName -InputObject $task -Force

# Step 4 - Start the task immediately
Start-ScheduledTask -TaskName $taskName
```
