From 06a48c0dc548ee080bf5dd4446ababdb6fd13398 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Fri, 30 Jan 2026 09:34:28 -0700 Subject: [PATCH 01/10] refactor remove_windowsMDM --- scripts/windows/remove_windowsMDM.ps1 | 410 ++++++++++++++------------ 1 file changed, 221 insertions(+), 189 deletions(-) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index 465706f2f..997b22318 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -9,7 +9,7 @@ 1. Check Task Scheduler first for GUIDs. (Stuck devices usually have tasks left behind). 2. Scrape the Registry for known Enrollment IDs. 3. Perform a targeted cleanup of the IDs we found. - 4. CleanSweep: Go to specific registry locations and remove an orphaned GUIDs + 4. ForcePrune: Go to specific registry locations and remove an orphaned GUIDs ============================================================================= #> @@ -19,26 +19,26 @@ $Script:AdminDebug = $false #### End Edit #### # Just grabbing the system drive (usually C:) so we aren't hardcoding paths. -Function Get-WindowsDrive { +function Get-WindowsDrive { $drive = (Get-WmiObject Win32_OperatingSystem).SystemDrive return $drive } # Standard logging wrapper. -Function Write-ToLog { +function Write-ToLog { [CmdletBinding()] - Param + param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][ValidateNotNullOrEmpty()][Alias("LogContent")][string]$Message , [Parameter(Mandatory = $false)][Alias('LogPath')][string]$Path = "$(Get-WindowsDrive)\Windows\Temp\jcMDMCleanup.log" , [Parameter(Mandatory = $false)][ValidateSet("Error", "Warn", "Info", "Verbose")][string]$Level = "Info" ) - Begin { + begin { $VerbosePreference = 'Continue' } - Process { + process { # Make sure the log file actually exists before we write to it - If (!(Test-Path $Path)) { + if (!(Test-Path $Path)) { Write-Verbose "Creating $Path." New-Item $Path -Force -ItemType File | Out-Null } @@ -46,7 +46,7 @@ Function Write-ToLog { # Decide how much information to output to the console if ($Script:AdminDebug) { - Switch ($Level) { + switch ($Level) { 'Error' { Write-Error $Message; $LevelText = 'ERROR:' } 'Warn' { Write-Warning $Message; $LevelText = 'WARNING:' } 'Info' { Write-Verbose $Message; $LevelText = 'INFO:' } @@ -54,7 +54,7 @@ Function Write-ToLog { } } else { # Quiet mode: only errors show up in console, but we still tag the log file correctly - Switch ($Level) { + switch ($Level) { 'Error' { Write-Error $Message; $LevelText = 'ERROR:' } 'Warn' { $LevelText = 'WARNING:' } 'Info' { $LevelText = 'INFO:' } @@ -68,6 +68,7 @@ Function Write-ToLog { # This is our primary way to find the GUID. # We look at the "EnterpriseMgmt" folder in Task Scheduler. +#startregion Get-MdmEnrollmentGuidFromTaskScheduler function Get-MdmEnrollmentGuidFromTaskScheduler { [CmdletBinding()] param() @@ -100,8 +101,10 @@ function Get-MdmEnrollmentGuidFromTaskScheduler { } return $foundGuids | Sort-Object -Unique } +#endregion Get-MdmEnrollmentGuidFromTaskScheduler # Helper to verify if our cleanup worked at the end. +#startregion Get-MdmEnrollmentInfo function Get-MdmEnrollmentInfo { [CmdletBinding()] param ( @@ -145,210 +148,239 @@ function Get-MdmEnrollmentInfo { return $null } } +#endregion Get-MdmEnrollmentInfo -# ========================================== -# LET'S GET STARTED -# ========================================== -try { - Write-ToLog "Script execution started: $(Get-Date)" -Level Verbose - Write-ToLog "Logging to: C:\Windows\Temp\jcMDMCleanup.log" -Level Verbose - Write-ToLog "-----------------------------------------" -Level Verbose - - $valueName = "ProviderID" - $mdmEnrollmentKey = "HKLM:\SOFTWARE\Microsoft\Enrollments" - $GuidsToProcess = @() - - if (-not (Test-Path -Path $mdmEnrollmentKey)) { - Write-ToLog "Registry path 'HKLM:\SOFTWARE\Microsoft\Enrollments\' does not exist. Exiting." -Level Error - exit 1 - } - - # --- Phase 1: GUIDs Discovery --- - Write-ToLog "####### Discovery Phase #######" -Level Verbose - - # Try Task Scheduler first. - $taskSchedulerGuids = Get-MdmEnrollmentGuidFromTaskScheduler - if ($taskSchedulerGuids.Count -gt 0) { - Write-ToLog "Using GUIDs discovered via Task Scheduler." - $GuidsToProcess = $taskSchedulerGuids - } else { - # Fallback to Registry scan if no tasks exist. - Write-ToLog "No GUIDs found in Task Scheduler. Falling back to Registry discovery." -Level Warn - Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Enrollments\" -Recurse -ErrorAction SilentlyContinue | ForEach-Object { - $EnrollID = $_.PSChildName - if ($EnrollID -match '^[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}$') { - if (Get-ItemProperty -LiteralPath $_.PsPath -Name $valueName -ErrorAction SilentlyContinue) { - if ($EnrollID -notin $GuidsToProcess) { - $GuidsToProcess += $EnrollID - } - } - } - } - } - - if ($GuidsToProcess.Count -eq 0) { - Write-ToLog "No MDM Enrollment GUIDs found via Tasks or Registry. Moving to Scorched Earth sweep." -Level Info - } - - # --- Phase 2: Targeted Cleanup --- - Write-ToLog "####### Targeted Cleanup Phase #######" -Level Verbose +#startregion Remove-WindowsMDMProvider +function Remove-WindowsMDMProvider { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] + [string]$EnrollmentGUID, + [Parameter(Mandatory = $false)] + [switch]$ForcePrune + ) + begin { + Write-ToLog "Script execution started: $(Get-Date)" -Level Verbose + Write-ToLog "Logging to: C:\Windows\Temp\jcMDMCleanup.log" -Level Verbose + Write-ToLog "-----------------------------------------" -Level Verbose - foreach ($EnrollID in $GuidsToProcess) { - Write-ToLog "Processing Enrollment ID: $EnrollID" + $valueName = "ProviderID" + $mdmEnrollmentKey = "HKLM:\SOFTWARE\Microsoft\Enrollments" + $GuidsToProcess = @() - # Grab ProviderID for Cert cleanup later - $regPath = "HKLM:\SOFTWARE\Microsoft\Enrollments\$EnrollID" - $providerIdValue = $null - if (Test-Path $regPath) { - $providerIdValue = Get-ItemProperty -LiteralPath $regPath -Name "ProviderID" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty ProviderID -ErrorAction SilentlyContinue - if ($providerIdValue) { Write-ToLog "ProviderID associated with this enrollment: $providerIdValue" } + if (-not (Test-Path -Path $mdmEnrollmentKey)) { + Write-ToLog "Registry path 'HKLM:\SOFTWARE\Microsoft\Enrollments\' does not exist. Exiting." -Level Error + exit 1 } - - # 1. Remove the Scheduled Tasks - Write-ToLog "--- Step 1: Removing Scheduled Tasks ---" - $Tasks = Get-ScheduledTask | Where-Object { $psitem.TaskPath -like "*$EnrollID*" -and $psitem.TaskPath -like "\Microsoft\Windows\EnterpriseMgmt\*" } - if ($Tasks) { - Try { - $Tasks | ForEach-Object { - $taskName = $_.TaskName - Write-ToLog "Removing task: $taskName" - Unregister-ScheduledTask -InputObject $psitem -Confirm:$false -ErrorAction Stop - } - Write-ToLog "Successfully removed scheduled tasks." - } catch { - Write-ToLog "Error removing task: $($taskName). Error: $($_.Exception.Message)" -Level Error - } - } else { - Write-ToLog "No active scheduled tasks objects found." - } - - # 2. Delete the Task Folder - Write-ToLog "--- Step 2: Removing Task Folders ---" - $TaskFolder = "C:\windows\System32\Tasks\Microsoft\Windows\EnterpriseMgmt\$EnrollID" + } + process { try { - if (Test-Path $TaskFolder) { - Remove-Item -Path $TaskFolder -Force -Recurse - Write-ToLog "Removed Task Folder: $TaskFolder" + if ($EnrollmentGUID) { + $GuidsToProcess += $EnrollmentGUID + Write-ToLog "Specific Enrollment GUID provided: $EnrollmentGUID. Proceeding with targeted cleanup." -Level Info + } else { + Write-ToLog "No specific Enrollment GUID provided. Proceeding with discovery." -Level Info + # --- Phase 1: GUIDs Discovery --- + Write-ToLog "####### Discovery Phase #######" -Level Verbose + + # Try Task Scheduler first. + $taskSchedulerGuids = Get-MdmEnrollmentGuidFromTaskScheduler + if ($taskSchedulerGuids.Count -gt 0) { + Write-ToLog "Using GUIDs discovered via Task Scheduler." + $GuidsToProcess = $taskSchedulerGuids + } else { + # Fallback to Registry scan if no tasks exist. + Write-ToLog "No GUIDs found in Task Scheduler. Falling back to Registry discovery." -Level Warn + Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Enrollments\" -Recurse -ErrorAction SilentlyContinue | ForEach-Object { + $EnrollID = $_.PSChildName + if ($EnrollID -match '^[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}$') { + if (Get-ItemProperty -LiteralPath $_.PsPath -Name $valueName -ErrorAction SilentlyContinue) { + if ($EnrollID -notin $GuidsToProcess) { + $GuidsToProcess += $EnrollID + } + } + } + } + } + if ($GuidsToProcess.Count -eq 0) { + if ($ForcePrune) { + Write-ToLog "No MDM Enrollment GUIDs found via Tasks or Registry. Moving to ForcePrune sweep." -Level Info + } else { + Write-ToLog "No MDM Enrollment GUIDs found via Tasks or Registry. Exiting." -Level Info + exit 0 + } + } } - } catch { - Write-ToLog "Error removing task folder. Error: $($_.Exception.Message)" -Level Error - } + # --- Phase 2: Targeted Cleanup --- + Write-ToLog "####### Targeted Cleanup Phase #######" -Level Verbose + + foreach ($EnrollID in $GuidsToProcess) { + Write-ToLog "Processing Enrollment ID: $EnrollID" + + # Grab ProviderID for Cert cleanup later + $regPath = "HKLM:\SOFTWARE\Microsoft\Enrollments\$EnrollID" + $providerIdValue = $null + if (Test-Path $regPath) { + $providerIdValue = Get-ItemProperty -LiteralPath $regPath -Name "ProviderID" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty ProviderID -ErrorAction SilentlyContinue + if ($providerIdValue) { Write-ToLog "ProviderID associated with this enrollment: $providerIdValue" } + } - # 3. Clean up the known Registry Keys - Write-ToLog "--- Step 3: Removing Registry Keys ---" - $keysToRemove = @( - "HKLM:\SOFTWARE\Microsoft\Enrollments\Status\$EnrollID", - "HKLM:\SOFTWARE\Microsoft\Enrollments\Context\$EnrollID", - "HKLM:\SOFTWARE\Microsoft\EnterpriseResourceManager\Tracked\$EnrollID", - "HKLM:\SOFTWARE\Microsoft\PolicyManager\AdmxInstalled\$EnrollID", - "HKLM:\SOFTWARE\Microsoft\PolicyManager\Providers\$EnrollID", - "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts\$EnrollID", - "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Logger\$EnrollID", - "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Sessions\$EnrollID", - "HKLM:\SOFTWARE\Microsoft\Enrollments\$EnrollID" - ) - - foreach ($key in $keysToRemove) { - if (Test-Path -Path $key) { - try { - Remove-Item -Path $key -Recurse -Force -ErrorAction Stop - Write-ToLog "Removed key: $key" - } catch { - Write-ToLog "Failed to remove key: $key. Error: $($_.Exception.Message)" -Level Error + # 1. Remove the Scheduled Tasks + Write-ToLog "--- Step 1: Removing Scheduled Tasks ---" + $Tasks = Get-ScheduledTask | Where-Object { $psitem.TaskPath -like "*$EnrollID*" -and $psitem.TaskPath -like "\Microsoft\Windows\EnterpriseMgmt\*" } + if ($Tasks) { + try { + $Tasks | ForEach-Object { + $taskName = $_.TaskName + Write-ToLog "Removing task: $taskName" + Unregister-ScheduledTask -InputObject $psitem -Confirm:$false -ErrorAction Stop + } + Write-ToLog "Successfully removed scheduled tasks." + } catch { + Write-ToLog "Error removing task: $($taskName). Error: $($_.Exception.Message)" -Level Error + } + } else { + Write-ToLog "No active scheduled tasks objects found." } - } - } - # 4. Remove WNS References - Write-ToLog "--- Step 4: Removing Push Notification Keys ---" - $pushKeyBase = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications\Applications\Windows.SystemToast.Background.Management" - if (Test-Path $pushKeyBase) { - Get-ChildItem $pushKeyBase -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -eq $EnrollID } | ForEach-Object { + # 2. Delete the Task Folder + Write-ToLog "--- Step 2: Removing Task Folders ---" + $TaskFolder = "C:\windows\System32\Tasks\Microsoft\Windows\EnterpriseMgmt\$EnrollID" try { - Write-ToLog "Removing WNS Push Key: $($_.PSPath)" - Remove-Item -Path $_.PSPath -Recurse -Force -ErrorAction Stop + if (Test-Path $TaskFolder) { + Remove-Item -Path $TaskFolder -Force -Recurse + Write-ToLog "Removed Task Folder: $TaskFolder" + } } catch { - Write-ToLog "Failed to remove WNS key. Error: $($_.Exception.Message)" -Level Warn + Write-ToLog "Error removing task folder. Error: $($_.Exception.Message)" -Level Error } - } - } - # 5. Delete Client Certificates - Write-ToLog "--- Step 5: Checking for Client Certificates ---" - if ($providerIdValue) { - try { - $certs = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object { $_.Issuer -match $providerIdValue } - if ($certs) { - foreach ($cert in $certs) { - Write-ToLog "Removing Certificate associated with Provider $providerIdValue. Subject: $($cert.Subject)" - Remove-Item -Path $cert.PSPath -Force -ErrorAction Stop + # 3. Clean up the known Registry Keys + Write-ToLog "--- Step 3: Removing Registry Keys ---" + $keysToRemove = @( + "HKLM:\SOFTWARE\Microsoft\Enrollments\Status\$EnrollID", + "HKLM:\SOFTWARE\Microsoft\Enrollments\Context\$EnrollID", + "HKLM:\SOFTWARE\Microsoft\EnterpriseResourceManager\Tracked\$EnrollID", + "HKLM:\SOFTWARE\Microsoft\PolicyManager\AdmxInstalled\$EnrollID", + "HKLM:\SOFTWARE\Microsoft\PolicyManager\Providers\$EnrollID", + "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts\$EnrollID", + "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Logger\$EnrollID", + "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Sessions\$EnrollID", + "HKLM:\SOFTWARE\Microsoft\Enrollments\$EnrollID" + ) + + foreach ($key in $keysToRemove) { + if (Test-Path -Path $key) { + try { + Remove-Item -Path $key -Recurse -Force -ErrorAction Stop + Write-ToLog "Removed key: $key" + } catch { + Write-ToLog "Failed to remove key: $key. Error: $($_.Exception.Message)" -Level Error + } } - } else { - Write-ToLog "No certificates found matching ProviderID: $providerIdValue" } - } catch { - Write-ToLog "Error processing certificates: $($_.Exception.Message)" -Level Warn - } - } else { - Write-ToLog "Skipping certificate removal (No ProviderID found to match against)." - } - - Write-ToLog "Finished processing Enrollment ID $EnrollID" -Level Verbose - Write-ToLog "-----------------------------------------" -Level Verbose - - } # End of the targeted loop - - # --- Phase 3: Clean Sweep --- - # This checks specific registry locations for ANY orphaned keys with a GUID format. - Write-ToLog "####### Phase 3: Clean Sweep - Generic GUID Sweep #######" -Level Verbose - - # 3. Sweep standard GUID keys - $sweepLocations = @( - "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts", - "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Logger", - "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Sessions" - ) - - # Regex for standard GUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - $guidRegex = '^[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}$' - foreach ($parentPath in $sweepLocations) { - Write-ToLog "Sweeping path for orphaned GUIDs: $parentPath" - if (Test-Path $parentPath) { - # Get all subkeys - $subKeys = Get-ChildItem -Path $parentPath -ErrorAction SilentlyContinue + # 4. Remove WNS References + Write-ToLog "--- Step 4: Removing Push Notification Keys ---" + $pushKeyBase = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications\Applications\Windows.SystemToast.Background.Management" + if (Test-Path $pushKeyBase) { + Get-ChildItem $pushKeyBase -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -eq $EnrollID } | ForEach-Object { + try { + Write-ToLog "Removing WNS Push Key: $($_.PSPath)" + Remove-Item -Path $_.PSPath -Recurse -Force -ErrorAction Stop + } catch { + Write-ToLog "Failed to remove WNS key. Error: $($_.Exception.Message)" -Level Warn + } + } + } - foreach ($key in $subKeys) { - # Check if the folder name is a GUID - if ($key.PSChildName -match $guidRegex) { - Write-ToLog "Found orphaned GUID key in sweep: $($key.PSChildName). Force removing." + # 5. Delete Client Certificates + Write-ToLog "--- Step 5: Checking for Client Certificates ---" + if ($providerIdValue) { try { - Remove-Item -Path $key.PSPath -Recurse -Force -ErrorAction Stop - Write-ToLog "Deleted: $($key.PSPath)" - } catch { - if ($parentPath -match "TaskCache") { - Write-ToLog "Skipped locked key in TaskCache: $($key.PSChildName) (Expected/Ignorable)" -Level Verbose + $certs = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object { $_.Issuer -match $providerIdValue } + if ($certs) { + foreach ($cert in $certs) { + Write-ToLog "Removing Certificate associated with Provider $providerIdValue. Subject: $($cert.Subject)" + Remove-Item -Path $cert.PSPath -Force -ErrorAction Stop + } } else { - Write-ToLog "Failed to delete $($key.PSPath). Error: $($_.Exception.Message)" -Level Error + Write-ToLog "No certificates found matching ProviderID: $providerIdValue" + } + } catch { + Write-ToLog "Error processing certificates: $($_.Exception.Message)" -Level Warn + } + } else { + Write-ToLog "Skipping certificate removal (No ProviderID found to match against)." + } + + Write-ToLog "Finished processing Enrollment ID $EnrollID" -Level Verbose + Write-ToLog "-----------------------------------------" -Level Verbose + + } # End of the targeted loop + + # --- Phase 3: Force Prune Sweep --- + if ($ForcePrune) { + # This checks specific registry locations for ANY orphaned keys with a GUID format. + Write-ToLog "####### Phase 3: Force Prune - Generic GUID Sweep #######" -Level Verbose + + # 3. Sweep standard GUID keys + $sweepLocations = @( + "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts", + "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Logger", + "HKLM:\SOFTWARE\Microsoft\Provisioning\OMADM\Sessions" + ) + + # Regex for standard GUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + $guidRegex = '^[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}$' + + foreach ($parentPath in $sweepLocations) { + Write-ToLog "Sweeping path for orphaned GUIDs: $parentPath" + if (Test-Path $parentPath) { + # Get all subkeys + $subKeys = Get-ChildItem -Path $parentPath -ErrorAction SilentlyContinue + + foreach ($key in $subKeys) { + # Check if the folder name is a GUID + if ($key.PSChildName -match $guidRegex) { + Write-ToLog "Found orphaned GUID key in sweep: $($key.PSChildName). Force removing." + try { + Remove-Item -Path $key.PSPath -Recurse -Force -ErrorAction Stop + Write-ToLog "Deleted: $($key.PSPath)" + } catch { + if ($parentPath -match "TaskCache") { + Write-ToLog "Skipped locked key in TaskCache: $($key.PSChildName) (Expected/Ignorable)" -Level Verbose + } else { + Write-ToLog "Failed to delete $($key.PSPath). Error: $($_.Exception.Message)" -Level Error + } + } + } } + } else { + Write-ToLog "Path not found (skipping): $parentPath" -Level Info } } } + } catch { + Write-ToLog "A terminating error occurred: $($_.Exception.Message)" -Level Error + Write-ToLog "Script execution failed: $(Get-Date)" -Level Error + } + } + end { + # --- Phase 4: Final Verification --- + $mdmEnrollmentDetails = Get-MdmEnrollmentInfo + if ($mdmEnrollmentDetails) { + Write-ToLog "MDM enrollment keys still exist after cleanup. Please check the log for details." -Level Warn } else { - Write-ToLog "Path not found (skipping): $parentPath" -Level Info + Write-ToLog "####### No MDM enrollment keys found after cleanup. Cleanup was successful! ######" -Level Verbose } + Write-ToLog "-----------------------------------------" -Level Verbose + Write-ToLog "Script execution finished: $(Get-Date)" -Level Verbose } +} +#endregion Remove-WindowsMDMProvider - # --- Phase 4: Final Verification --- - $mdmEnrollmentDetails = Get-MdmEnrollmentInfo - if ($mdmEnrollmentDetails) { - Write-ToLog "MDM enrollment keys still exist after cleanup. Please check the log for details." -Level Warn - } else { - Write-ToLog "####### No MDM enrollment keys found after cleanup. Cleanup was successful! ######" -Level Verbose - } - Write-ToLog "-----------------------------------------" -Level Verbose - Write-ToLog "Script execution finished: $(Get-Date)" -Level Verbose -} catch { - Write-ToLog "A terminating error occurred: $($_.Exception.Message)" -Level Error - Write-ToLog "Script execution failed: $(Get-Date)" -Level Error -} \ No newline at end of file +# ========================================== +# LET'S GET STARTED +# ========================================== +Remove-WindowsMDMProvider -ForcePrune \ No newline at end of file From a8d69d7461ae0b5350b8b9209a06185ef5ebfe77 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Fri, 30 Jan 2026 09:54:44 -0700 Subject: [PATCH 02/10] mmpcEnrollmentFlag check --- scripts/windows/remove_windowsMDM.ps1 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index 997b22318..a095d176c 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -319,6 +319,31 @@ function Remove-WindowsMDMProvider { } # End of the targeted loop + Write-ToLog "--- Step 6: Set MmpcEnrollmentFlag Key ---" + $MmpcEnrollmentFlagKeyBase = "HKLM:\SOFTWARE\Microsoft\Enrollments" + if (Test-Path $MmpcEnrollmentFlagKeyBase) { + $currentValue = Get-ItemProperty -Path $MmpcEnrollmentFlagKeyBase -Name "MmpcEnrollmentFlag" -ErrorAction SilentlyContinue + if ($null -ne $currentValue) { + Write-ToLog "Current MmpcEnrollmentFlag is: $($currentValue.MmpcEnrollmentFlag)" + # 3. If the value is NOT 0, set it to 0 + if ($currentValue.MmpcEnrollmentFlag -ne 0) { + Write-ToLog "Value is not 0. Resetting to 0..." + try { + Set-ItemProperty -Path $MmpcEnrollmentFlagKeyBase -Name "MmpcEnrollmentFlag" -Value 0 -Type DWord + Write-ToLog "Successfully set MmpcEnrollmentFlag to 0." + } catch { + Write-ToLog "Failed to set registry value. Ensure you are running as Administrator." -Level Error + } + } else { + Write-ToLog "MmpcEnrollmentFlag is already 0. No action needed." + } + } else { + Write-ToLog "Value 'MmpcEnrollmentFlag' does not exist in $MmpcEnrollmentFlagKeyBase. Nothing to reset." + } + } else { + Write-ToLog "Registry path $MmpcEnrollmentFlagKeyBase not found." -Level Warn + } + # --- Phase 3: Force Prune Sweep --- if ($ForcePrune) { # This checks specific registry locations for ANY orphaned keys with a GUID format. From 6f6d0756c21c30988b5f3a0c00dcb6d5403b264f Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Fri, 30 Jan 2026 09:59:57 -0700 Subject: [PATCH 03/10] remove basepath check - this is done early in function --- scripts/windows/remove_windowsMDM.ps1 | 32 ++++++++++++--------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index a095d176c..e641adfb2 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -319,29 +319,25 @@ function Remove-WindowsMDMProvider { } # End of the targeted loop + # 6. Reset MmpcEnrollmentFlag if needed Write-ToLog "--- Step 6: Set MmpcEnrollmentFlag Key ---" - $MmpcEnrollmentFlagKeyBase = "HKLM:\SOFTWARE\Microsoft\Enrollments" - if (Test-Path $MmpcEnrollmentFlagKeyBase) { - $currentValue = Get-ItemProperty -Path $MmpcEnrollmentFlagKeyBase -Name "MmpcEnrollmentFlag" -ErrorAction SilentlyContinue - if ($null -ne $currentValue) { - Write-ToLog "Current MmpcEnrollmentFlag is: $($currentValue.MmpcEnrollmentFlag)" - # 3. If the value is NOT 0, set it to 0 - if ($currentValue.MmpcEnrollmentFlag -ne 0) { - Write-ToLog "Value is not 0. Resetting to 0..." - try { - Set-ItemProperty -Path $MmpcEnrollmentFlagKeyBase -Name "MmpcEnrollmentFlag" -Value 0 -Type DWord - Write-ToLog "Successfully set MmpcEnrollmentFlag to 0." - } catch { - Write-ToLog "Failed to set registry value. Ensure you are running as Administrator." -Level Error - } - } else { - Write-ToLog "MmpcEnrollmentFlag is already 0. No action needed." + $currentValue = Get-ItemProperty -Path $mdmEnrollmentKey -Name "MmpcEnrollmentFlag" -ErrorAction SilentlyContinue + if ($null -ne $currentValue) { + Write-ToLog "Current MmpcEnrollmentFlag is: $($currentValue.MmpcEnrollmentFlag)" + # 3. If the value is NOT 0, set it to 0 + if ($currentValue.MmpcEnrollmentFlag -ne 0) { + Write-ToLog "Value is not 0. Resetting to 0..." + try { + Set-ItemProperty -Path $mdmEnrollmentKey -Name "MmpcEnrollmentFlag" -Value 0 -Type DWord + Write-ToLog "Successfully set MmpcEnrollmentFlag to 0." + } catch { + Write-ToLog "Failed to set registry value. Ensure you are running as Administrator." -Level Error } } else { - Write-ToLog "Value 'MmpcEnrollmentFlag' does not exist in $MmpcEnrollmentFlagKeyBase. Nothing to reset." + Write-ToLog "MmpcEnrollmentFlag is already 0. No action needed." } } else { - Write-ToLog "Registry path $MmpcEnrollmentFlagKeyBase not found." -Level Warn + Write-ToLog "Value 'MmpcEnrollmentFlag' does not exist in $mdmEnrollmentKey. Nothing to reset." } # --- Phase 3: Force Prune Sweep --- From 030944cb244cf15d5d2127e4ecd821d65e5b0427 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Fri, 30 Jan 2026 10:00:16 -0700 Subject: [PATCH 04/10] move path logging out of function --- scripts/windows/remove_windowsMDM.ps1 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index e641adfb2..824615cd3 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -160,10 +160,6 @@ function Remove-WindowsMDMProvider { [switch]$ForcePrune ) begin { - Write-ToLog "Script execution started: $(Get-Date)" -Level Verbose - Write-ToLog "Logging to: C:\Windows\Temp\jcMDMCleanup.log" -Level Verbose - Write-ToLog "-----------------------------------------" -Level Verbose - $valueName = "ProviderID" $mdmEnrollmentKey = "HKLM:\SOFTWARE\Microsoft\Enrollments" $GuidsToProcess = @() @@ -404,4 +400,7 @@ function Remove-WindowsMDMProvider { # ========================================== # LET'S GET STARTED # ========================================== +Write-ToLog "Script execution started: $(Get-Date)" -Level Verbose +Write-ToLog "Logging to: C:\Windows\Temp\jcMDMCleanup.log" -Level Verbose +Write-ToLog "-----------------------------------------" -Level Verbose Remove-WindowsMDMProvider -ForcePrune \ No newline at end of file From 517549bd722ffcffa807ca6be27797019170436c Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Mon, 2 Feb 2026 14:10:38 -0700 Subject: [PATCH 05/10] rename Get-WindowsMDMProvider --- scripts/windows/remove_windowsMDM.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index 824615cd3..5c3f639b5 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -104,8 +104,8 @@ function Get-MdmEnrollmentGuidFromTaskScheduler { #endregion Get-MdmEnrollmentGuidFromTaskScheduler # Helper to verify if our cleanup worked at the end. -#startregion Get-MdmEnrollmentInfo -function Get-MdmEnrollmentInfo { +#startregion Get-WindowsMDMProvider +function Get-WindowsMDMProvider { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] @@ -148,7 +148,7 @@ function Get-MdmEnrollmentInfo { return $null } } -#endregion Get-MdmEnrollmentInfo +#endregion Get-WindowsMDMProvider #startregion Remove-WindowsMDMProvider function Remove-WindowsMDMProvider { @@ -385,7 +385,7 @@ function Remove-WindowsMDMProvider { } end { # --- Phase 4: Final Verification --- - $mdmEnrollmentDetails = Get-MdmEnrollmentInfo + $mdmEnrollmentDetails = Get-WindowsMDMProvider if ($mdmEnrollmentDetails) { Write-ToLog "MDM enrollment keys still exist after cleanup. Please check the log for details." -Level Warn } else { From de4a81a97004477f9a045cb4d29295c5896e7e88 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Mon, 2 Feb 2026 14:40:36 -0700 Subject: [PATCH 06/10] if error occurred - end block return --- scripts/windows/remove_windowsMDM.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index 5c3f639b5..62ac8d07c 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -163,6 +163,7 @@ function Remove-WindowsMDMProvider { $valueName = "ProviderID" $mdmEnrollmentKey = "HKLM:\SOFTWARE\Microsoft\Enrollments" $GuidsToProcess = @() + $HadError = $false if (-not (Test-Path -Path $mdmEnrollmentKey)) { Write-ToLog "Registry path 'HKLM:\SOFTWARE\Microsoft\Enrollments\' does not exist. Exiting." -Level Error @@ -379,11 +380,15 @@ function Remove-WindowsMDMProvider { } } } catch { + $HadError = $true Write-ToLog "A terminating error occurred: $($_.Exception.Message)" -Level Error Write-ToLog "Script execution failed: $(Get-Date)" -Level Error } } end { + if ($HadError) { + return + } # --- Phase 4: Final Verification --- $mdmEnrollmentDetails = Get-WindowsMDMProvider if ($mdmEnrollmentDetails) { From 10d0e78904f45b76d5bafa7ca9cd53e09637f656 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Tue, 3 Feb 2026 08:20:32 -0700 Subject: [PATCH 07/10] Update remove_windowsMDM.ps1 --- scripts/windows/remove_windowsMDM.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index 62ac8d07c..11a6d5bb0 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -14,7 +14,7 @@ #> #### Edit to output verbose messages #### -# Flip this to $true if you want to see everything happening in the console window. +# Flip this to $true if you want to see everything happening in the console window $Script:AdminDebug = $false #### End Edit #### From a74dd8631e745c7c8a88edc1b6b1306e95ad9ee9 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Tue, 3 Feb 2026 08:42:28 -0700 Subject: [PATCH 08/10] can't use exit in function - change to return --- scripts/windows/remove_windowsMDM.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index 11a6d5bb0..13fba5770 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -167,7 +167,7 @@ function Remove-WindowsMDMProvider { if (-not (Test-Path -Path $mdmEnrollmentKey)) { Write-ToLog "Registry path 'HKLM:\SOFTWARE\Microsoft\Enrollments\' does not exist. Exiting." -Level Error - exit 1 + return } } process { @@ -204,7 +204,7 @@ function Remove-WindowsMDMProvider { Write-ToLog "No MDM Enrollment GUIDs found via Tasks or Registry. Moving to ForcePrune sweep." -Level Info } else { Write-ToLog "No MDM Enrollment GUIDs found via Tasks or Registry. Exiting." -Level Info - exit 0 + return } } } From 4a2ff0bf26a87cc9139010c7d86b45a1d952aa92 Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Tue, 3 Feb 2026 09:04:21 -0700 Subject: [PATCH 09/10] move mmpcEnrollment check to beginning of function --- scripts/windows/remove_windowsMDM.ps1 | 41 +++++++++++++-------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index 13fba5770..ee5cd047f 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -172,6 +172,26 @@ function Remove-WindowsMDMProvider { } process { try { + # --- Step 0: Reset MmpcEnrollmentFlag first (device may still consider itself MDM enrolled if non-zero; runs in all code paths including early return) --- + Write-ToLog "--- Step 0: Reset MmpcEnrollmentFlag ---" + $currentValue = Get-ItemProperty -Path $mdmEnrollmentKey -Name "MmpcEnrollmentFlag" -ErrorAction SilentlyContinue + if ($null -ne $currentValue) { + Write-ToLog "Current MmpcEnrollmentFlag is: $($currentValue.MmpcEnrollmentFlag)" + if ($currentValue.MmpcEnrollmentFlag -ne 0) { + Write-ToLog "Value is not 0. Resetting to 0..." + try { + Set-ItemProperty -Path $mdmEnrollmentKey -Name "MmpcEnrollmentFlag" -Value 0 -Type DWord + Write-ToLog "Successfully set MmpcEnrollmentFlag to 0." + } catch { + Write-ToLog "Failed to set registry value. Ensure you are running as Administrator." -Level Error + } + } else { + Write-ToLog "MmpcEnrollmentFlag is already 0. No action needed." + } + } else { + Write-ToLog "Value 'MmpcEnrollmentFlag' does not exist in $mdmEnrollmentKey. Nothing to reset." + } + if ($EnrollmentGUID) { $GuidsToProcess += $EnrollmentGUID Write-ToLog "Specific Enrollment GUID provided: $EnrollmentGUID. Proceeding with targeted cleanup." -Level Info @@ -316,27 +336,6 @@ function Remove-WindowsMDMProvider { } # End of the targeted loop - # 6. Reset MmpcEnrollmentFlag if needed - Write-ToLog "--- Step 6: Set MmpcEnrollmentFlag Key ---" - $currentValue = Get-ItemProperty -Path $mdmEnrollmentKey -Name "MmpcEnrollmentFlag" -ErrorAction SilentlyContinue - if ($null -ne $currentValue) { - Write-ToLog "Current MmpcEnrollmentFlag is: $($currentValue.MmpcEnrollmentFlag)" - # 3. If the value is NOT 0, set it to 0 - if ($currentValue.MmpcEnrollmentFlag -ne 0) { - Write-ToLog "Value is not 0. Resetting to 0..." - try { - Set-ItemProperty -Path $mdmEnrollmentKey -Name "MmpcEnrollmentFlag" -Value 0 -Type DWord - Write-ToLog "Successfully set MmpcEnrollmentFlag to 0." - } catch { - Write-ToLog "Failed to set registry value. Ensure you are running as Administrator." -Level Error - } - } else { - Write-ToLog "MmpcEnrollmentFlag is already 0. No action needed." - } - } else { - Write-ToLog "Value 'MmpcEnrollmentFlag' does not exist in $mdmEnrollmentKey. Nothing to reset." - } - # --- Phase 3: Force Prune Sweep --- if ($ForcePrune) { # This checks specific registry locations for ANY orphaned keys with a GUID format. From 0502a9d9ad85ae5e885b624a5cc2b322ae73460f Mon Sep 17 00:00:00 2001 From: Geoffrey Wein Date: Tue, 3 Feb 2026 09:04:32 -0700 Subject: [PATCH 10/10] change to throw instead of return to indicate error --- scripts/windows/remove_windowsMDM.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/windows/remove_windowsMDM.ps1 b/scripts/windows/remove_windowsMDM.ps1 index ee5cd047f..e885b9d7c 100644 --- a/scripts/windows/remove_windowsMDM.ps1 +++ b/scripts/windows/remove_windowsMDM.ps1 @@ -167,7 +167,7 @@ function Remove-WindowsMDMProvider { if (-not (Test-Path -Path $mdmEnrollmentKey)) { Write-ToLog "Registry path 'HKLM:\SOFTWARE\Microsoft\Enrollments\' does not exist. Exiting." -Level Error - return + throw "Registry path 'HKLM:\SOFTWARE\Microsoft\Enrollments\' does not exist." } } process {