Fix unenroll Windows instructions (#47725)

- @noahtalerman: For Windows, I think we want to squeeze turning off MDM
and uninstalling fleetd into one script.
- Why? Because Fleet automatically turns on Windows MDM, two scripts
means Fleet could beat the IT admin and turn MDM back on before they
uninstall fleetd.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Windows fleetd uninstall now proactively detects and disables MDM
enrollment before removal to help ensure a cleaner device unenrollment.
* **Bug Fixes**
* Improved uninstall error reporting by surfacing the underlying failure
message and exiting with a clear non-zero code.
* **Chores**
* Updated fleet testing and workstation configurations: removed the
Windows uninstall/MDM-related steps from QA and workstation controls,
and added new Windows security/setup scripts plus additional
cross-platform post-install and extension installation tasks.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Steven Palmesano <3100993+spalmesano0@users.noreply.github.com>
This commit is contained in:
Noah Talerman
2026-06-19 13:15:00 -04:00
committed by GitHub
co-authored by Steven Palmesano
parent d7800eb5c5
commit 96ca0fd967
7 changed files with 92 additions and 115 deletions
+5 -5
View File
@@ -160,12 +160,12 @@ In the Google Admin console:
1. Determine if your host has MDM features turned on by looking at the **MDM status** on the host's **Host details** page.
2. If MDM is turned on, for macOS, Windows, iOS/iPadOS, and Android hosts:
- For macOS hosts, select **Actions > Turn off MDM** on the host's details page to turn MDM off.
- For Windows hosts, download the [turn off MDM script](https://github.com/fleetdm/fleet/blob/main/it-and-security/lib/windows/scripts/turn-off-mdm.ps1), add it to the host's fleet on the **Scripts** page in Fleet, and run the script via **Actions > Run script** on the host's details page.
- For iOS/iPadOS and Android hosts, select **Actions > Unenroll**.
2. If MDM is turned on, turn it off:
- Windows: Skip to step 3 (Uninstall Fleet's agent).
- macOS: On the **Host details** page, select **Actions > Turn off MDM**.
- iOS/iPadOS & Android: On the **Host details** page, select **Actions > Unenroll**.
3. [Uninstall fleetd](https://fleetdm.com/guides/how-to-uninstall-fleetd) for macOS, Windows, and Linux hosts.
3. For macOS, Windows, and Linux hosts, [uninstall Fleet's agent (fleetd)](https://fleetdm.com/guides/how-to-uninstall-fleetd).
4. Select **Actions > Delete** to delete the host from Fleet.
+2 -2
View File
@@ -18,7 +18,7 @@ To remove fleetd from a Mac:
To remove fleetd from a Windows device:
1. Download the [Windows uninstall script](https://github.com/fleetdm/fleet/blob/main/it-and-security/lib/windows/scripts/uninstall-fleetd-windows.ps1).
1. Download the [Windows uninstall script](https://github.com/fleetdm/fleet/blob/main/docs/solutions/windows/scripts/uninstall-fleetd-windows.ps1). This script turns off MDM and uninstalls fleetd.
2. Open **PowerShell** as administrator (right-click and select **Run as administrator**).
3. Navigate to where you saved the script: `cd C:\path\to\your\script`
4. Run the script: `.\uninstall-fleetd-windows.ps1`
@@ -41,7 +41,7 @@ To remove fleetd from a Linux device:
To remove fleetd from a device through Fleet:
1. Add the uninstall script for [macOS](https://github.com/fleetdm/fleet/blob/main/it-and-security/lib/macos/scripts/uninstall-fleetd-macos.sh), [Windows](https://github.com/fleetdm/fleet/blob/main/it-and-security/lib/windows/scripts/uninstall-fleetd-windows.ps1), or [Linux](https://github.com/fleetdm/fleet/blob/main/it-and-security/lib/linux/scripts/uninstall-fleetd-linux.sh) to Fleet as a script.
1. Add the uninstall script for [macOS](https://github.com/fleetdm/fleet/blob/main/it-and-security/lib/macos/scripts/uninstall-fleetd-macos.sh), [Windows](https://github.com/fleetdm/fleet/blob/main/docs/solutions/windows/scripts/uninstall-fleetd-windows.ps1), or [Linux](https://github.com/fleetdm/fleet/blob/main/it-and-security/lib/linux/scripts/uninstall-fleetd-linux.sh) to Fleet as a script.
2. Go to the device's **Host details** page.
3. Select **Actions > Run script** and choose the uninstall script.
+1 -1
View File
@@ -261,7 +261,7 @@ The Autopilot service may need a few minutes to sync after the device record cle
## Turn off Windows MDM
1. Turn off MDM for each host by running [this script](https://github.com/fleetdm/fleet/blob/main/it-and-security/lib/windows/scripts/turn-off-mdm.ps1) from Fleet on all your Windows hosts.
1. Turn off MDM for each host by running [this script](https://github.com/fleetdm/fleet/blob/main/docs/solutions/windows/scripts/uninstall-fleetd-windows.ps1) from Fleet on all your Windows hosts. Note that this script will also remove fleetd from the hosts.
2. Head to **Settings > Integrations > MDM**.
@@ -1,4 +1,22 @@
# Please don't delete. This script is referenced in the guide here: https://fleetdm.com/guides/how-to-uninstall-fleetd
# Please don't delete. This script is referenced in the guides here:
# - https://fleetdm.com/guides/windows-mdm-setup#turn-off-windows-mdm
# - https://fleetdm.com/guides/how-to-uninstall-fleetd
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class MdmRegistration
{
[DllImport("mdmregistration.dll", SetLastError = true)]
public static extern int UnregisterDeviceWithManagement(IntPtr pDeviceID);
public static int UnregisterDevice()
{
return UnregisterDeviceWithManagement(IntPtr.Zero);
}
}
"@ -Language CSharp
function Test-Administrator
{
@@ -66,7 +84,7 @@ function Force-Remove-Orbit {
return
}
}
# Write success log
"Fleetd successfully removed at $(Get-Date)" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt"
}
@@ -89,18 +107,16 @@ function Main {
Exit -1
}
Write-Host "About to uninstall fleetd..."
if ($args[0] -eq "remove") {
# "remove" is received as argument to the script when called as the
# sub-process that will actually remove the fleet agent.
# Log the start of removal process
"Starting removal process at $(Get-Date)" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt"
# sleep to give time to fleetd to send the script results to Fleet
Start-Sleep -Seconds 20
if (Force-Remove-Orbit) {
Write-Host "fleetd was uninstalled."
Exit 0
@@ -109,26 +125,81 @@ function Main {
Exit -1
}
} else {
# Turn off MDM first so Fleet cannot re-enable it before fleetd is removed.
# Check 1: Fleet-specific enrollment (ProviderID + EnrollmentState)
$enrollmentKey = Get-Item -Path HKLM:\SOFTWARE\Microsoft\Enrollments\* -ErrorAction SilentlyContinue | Get-ItemProperty | Where-Object {$_.ProviderID -eq 'Fleet'} | Where-Object {$_.EnrollmentState -match '1|3|6|13'}
$mdmEnrolled = $null -ne $enrollmentKey
# Check 2: fallback via DiscoveryServiceFullURL
$enrollmentsPath = "HKLM:\SOFTWARE\Microsoft\Enrollments"
if (-not $mdmEnrolled) {
if (Test-Path $enrollmentsPath) {
$enrollmentKeys = Get-ChildItem -Path $enrollmentsPath -ErrorAction SilentlyContinue
foreach ($key in $enrollmentKeys) {
if ($null -ne (Get-ItemProperty -Path $key.PSPath -Name "DiscoveryServiceFullURL" -ErrorAction SilentlyContinue)) {
$mdmEnrolled = $true
break
}
}
}
}
if ($mdmEnrolled) {
$result = [MdmRegistration]::UnregisterDevice()
if ($result -ne 0) {
throw "UnregisterDeviceWithManagement failed with error code: $result"
}
Write-Host "Device unregistration called successfully."
$clearedCount = 0
if (Test-Path $enrollmentsPath) {
$enrollmentKeys = Get-ChildItem -Path $enrollmentsPath -ErrorAction SilentlyContinue
foreach ($key in $enrollmentKeys) {
if ($null -ne (Get-ItemProperty -Path $key.PSPath -Name "DiscoveryServiceFullURL" -ErrorAction SilentlyContinue)) {
try {
Remove-ItemProperty -Path $key.PSPath -Name "DiscoveryServiceFullURL" -ErrorAction Stop
$clearedCount++
Write-Host "Cleared DiscoveryServiceFullURL from enrollment key: $($key.PSChildName)"
} catch {
Write-Warning "Failed to clear DiscoveryServiceFullURL from $($key.PSChildName): $_"
}
}
}
}
if ($clearedCount -gt 0) {
Write-Host "Cleared DiscoveryServiceFullURL from $clearedCount enrollment key(s)."
} else {
Write-Host "Turning off MDM completed. The UnregisterDeviceWithManagement API automatically cleared the registry values."
}
} else {
Write-Host "MDM is not turned on. Skipping MDM unregistration."
}
# when this script is executed from fleetd, it does not immediately
# remove the agent. Instead, it starts a new detached process that
# will do the actual removal.
Write-Host "Removing fleetd, system will be unenrolled in 20 seconds..."
Write-Host "Executing detached child process"
$execName = $MyInvocation.ScriptName
$proc = Start-Process -PassThru -FilePath "powershell" -WindowStyle Hidden -ArgumentList "-MTA", "-ExecutionPolicy", "Bypass", "-File", "`"$execName`"", "remove"
# Log the process ID
"Started removal process with ID: $($proc.Id) at $(Get-Date)" | Out-File -Append -FilePath "$env:TEMP\fleet_remove_log.txt"
Start-Sleep -Seconds 5 # give time to process to start running
Write-Host "Removal process started: $($proc.Id)."
}
} catch {
Write-Host "Error: Entry point"
Write-Host "$(Resolve-Error-Detailed)"
Exit -1
Write-Error "Error running fleetd unenrollment script: $_"
exit 1
}
}
@@ -83,7 +83,6 @@ controls:
# macOS scripts
- path: ../lib/macos/scripts/uninstall-fleetd-macos.sh
# Windows scripts
- path: ../lib/windows/scripts/uninstall-fleetd-windows.ps1
# Linux scripts
- path: ../lib/linux/scripts/uninstall-fleetd-linux.sh
- path: ../lib/linux/scripts/install-fleet-desktop-required-extension.sh
-2
View File
@@ -148,8 +148,6 @@ controls:
grace_period_days: 2
scripts:
- path: ../lib/macos/scripts/uninstall-fleetd-macos.sh
- path: ../lib/windows/scripts/uninstall-fleetd-windows.ps1
- path: ../lib/windows/scripts/turn-off-mdm.ps1
- path: ../lib/windows/scripts/enable-ms-defender.ps1
- path: ../lib/windows/scripts/create-admin-user.ps1
- path: ../lib/linux/scripts/uninstall-fleetd-linux.sh
@@ -1,91 +0,0 @@
# Please don't delete. This script is referenced in the guide here: https://fleetdm.com/guides/windows-mdm-setup#turn-off-windows-mdm
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class MdmRegistration
{
[DllImport("mdmregistration.dll", SetLastError = true)]
public static extern int UnregisterDeviceWithManagement(IntPtr pDeviceID);
public static int UnregisterDevice()
{
return UnregisterDeviceWithManagement(IntPtr.Zero);
}
}
"@ -Language CSharp
try {
# Step 1: Check for DiscoveryServiceFullURL values before unregistering
# This helps us provide clearer output about what happened
$enrollmentsPath = "HKLM:\SOFTWARE\Microsoft\Enrollments"
$foundBeforeUnregister = $false
if (Test-Path $enrollmentsPath) {
$enrollmentKeys = Get-ChildItem -Path $enrollmentsPath -ErrorAction SilentlyContinue
foreach ($key in $enrollmentKeys) {
$upnPath = Join-Path $key.PSPath "UPN"
$discoveryUrlPath = Join-Path $key.PSPath "DiscoveryServiceFullURL"
if (Test-Path $upnPath) {
if (Test-Path $discoveryUrlPath) {
$foundBeforeUnregister = $true
break
}
}
}
}
# Step 2: Unregister the device from MDM using the Windows API
$result = [MdmRegistration]::UnregisterDevice()
if ($result -ne 0) {
throw "UnregisterDeviceWithManagement failed with error code: $result"
}
Write-Host "Device unregistration called successfully."
# Step 3: Clear any remaining DiscoveryServiceFullURL registry values to ensure Fleet detects
# the device as unenrolled on the next refetch. The UnregisterDeviceWithManagement API
# may have already cleared these values, but we check and clear any remaining ones to be safe.
$clearedCount = 0
if (Test-Path $enrollmentsPath) {
$enrollmentKeys = Get-ChildItem -Path $enrollmentsPath -ErrorAction SilentlyContinue
foreach ($key in $enrollmentKeys) {
# Only clear DiscoveryServiceFullURL from enrollment keys that have a UPN
# (these are the ones Fleet's query checks). This matches Fleet's query logic
# which filters by entries with UPN values.
$upnPath = Join-Path $key.PSPath "UPN"
$discoveryUrlPath = Join-Path $key.PSPath "DiscoveryServiceFullURL"
if (Test-Path $upnPath) {
if (Test-Path $discoveryUrlPath) {
try {
Remove-ItemProperty -Path $key.PSPath -Name "DiscoveryServiceFullURL" -ErrorAction Stop
$clearedCount++
Write-Host "Cleared DiscoveryServiceFullURL from enrollment key: $($key.PSChildName)"
} catch {
Write-Warning "Failed to clear DiscoveryServiceFullURL from $($key.PSChildName): $_"
}
}
}
}
}
# Provide clearer output based on what we found
if ($clearedCount -gt 0) {
Write-Host "Cleared DiscoveryServiceFullURL from $clearedCount enrollment key(s). Fleet will detect the device as unenrolled on the next refetch."
} elseif ($foundBeforeUnregister) {
Write-Host "MDM unregistration completed. The UnregisterDeviceWithManagement API automatically cleared the registry values."
Write-Host "Fleet will detect the device as unenrolled on the next refetch."
} else {
Write-Host "MDM unregistration completed. No DiscoveryServiceFullURL registry values were found (device was not enrolled or values were already cleared)."
}
} catch {
Write-Error "Error calling UnregisterDeviceWithManagement: $_"
exit 1
}