Powershell/Private/WindowsMDM/Remove-WindowsMDMProvider.ps1
|
function Remove-WindowsMDMProvider { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string]$EnrollmentGUID, [Parameter(Mandatory = $false)] [switch]$ForcePrune ) begin { $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 throw "Registry path 'HKLM:\SOFTWARE\Microsoft\Enrollments\' does not exist." } } 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 } 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 Warning 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 return } } } # --- 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" } } # 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" try { if (Test-Path $TaskFolder) { Remove-Item -Path $TaskFolder -Force -Recurse Write-ToLog "Removed Task Folder: $TaskFolder" } } catch { Write-ToLog "Error removing task folder. Error: $($_.Exception.Message)" -Level Error } # 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 } } } # 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 Warning } } } # 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 } } else { Write-ToLog "No certificates found matching ProviderID: $providerIdValue" } } catch { Write-ToLog "Error processing certificates: $($_.Exception.Message)" -Level Warning } } 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 { $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) { Write-ToLog "MDM enrollment keys still exist after cleanup. Please check the log for details." -Level Warning } 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 } } |