Skip to main content
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:
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.
$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.
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.
# 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