ResettoOOBE.ps1


<#PSScriptInfo
 
.VERSION 1.0.1
 
.GUID 9d7d5c78-88d0-4d39-baa8-91a7e7f0f8aa
 
.AUTHOR Dylan Descamps
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
#>


<#
 
.DESCRIPTION
 Prepare Windows for Autopilot OOBE after SCCM Task Sequence
 
#>
 

Param()


$ScriptBlock = @'
$LogPath = "C:\Windows\Temp\PrepareOOBE.log"
 
function Write-Log {
    param(
        [string]$Message,
        [string]$Level = "Info"
    )
 
    $Stamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
 
    Add-Content `
        -Path $LogPath `
        -Value "[$Stamp] [$Level] $Message" `
        -ErrorAction SilentlyContinue
}
 
Write-Log "PrepareOOBE started"
 
# Leave Azure AD registration
Write-Log "Running dsregcmd /leave"
 
Start-Process `
    -FilePath "dsregcmd.exe" `
    -ArgumentList "/leave" `
    -Wait `
    -NoNewWindow `
    -ErrorAction SilentlyContinue
 
# ---------------------------------------------------------------
# STEP 1 - Kill all CCM processes so nothing holds file locks
# ---------------------------------------------------------------
Write-Log "Killing CCM processes"
@(
    "CcmExec", "ccmsetup", "CMRcService", "CmRcService",
    "smsexec", "smsswd", "SensorWDService", "SensorManagedProvider",
    "ccmrestart", "SCNotification"
) | ForEach-Object {
    $procs = Get-Process -Name $_ -ErrorAction SilentlyContinue
    if ($procs) {
        $procs | Stop-Process -Force -ErrorAction SilentlyContinue
        Write-Log "Killed process: $_"
    }
}
 
# ---------------------------------------------------------------
# STEP 2 - Stop and delete CCM services
# ---------------------------------------------------------------
Write-Log "Stopping and removing CCM services"
@("CcmExec", "ccmsetup", "smstsmgr", "CmRcService") | ForEach-Object {
    $svc = Get-Service -Name $_ -ErrorAction SilentlyContinue
    if ($svc) {
        Stop-Service -Name $_ -Force -ErrorAction SilentlyContinue
        Start-Process "sc.exe" -ArgumentList "delete $_" -Wait -NoNewWindow -ErrorAction SilentlyContinue
        Write-Log "Removed service: $_"
    }
}
 
# ---------------------------------------------------------------
# STEP 3 - Uninstall SCCM client and wait for it to fully finish
# ---------------------------------------------------------------
if (Test-Path "C:\Windows\ccmsetup\ccmsetup.exe") {
    Write-Log "Starting ccmsetup /Uninstall"
    Start-Process `
        -FilePath "C:\Windows\ccmsetup\ccmsetup.exe" `
        -ArgumentList "/Uninstall" `
        -NoNewWindow `
        -ErrorAction SilentlyContinue
 
    # ccmsetup /Uninstall launches a child process and exits immediately.
    # Poll until CcmExec service is gone and no ccmsetup child process remains.
    $TimeoutSec = 300
    $Elapsed = 0
    $Interval = 5
    Write-Log "Waiting for CCM uninstall to complete (timeout: ${TimeoutSec}s)"
 
    do {
        Start-Sleep -Seconds $Interval
        $Elapsed += $Interval
        $ServiceGone = -not (Get-Service -Name "CcmExec" -ErrorAction SilentlyContinue)
        $ProcessGone = -not (Get-Process -Name "ccmsetup" -ErrorAction SilentlyContinue)
        $SetupGone = -not (Test-Path "C:\Windows\ccmsetup\ccmsetup.exe")
        Write-Log "Uninstall check at ${Elapsed}s - Service gone: $ServiceGone | Process gone: $ProcessGone | Setup exe gone: $SetupGone"
    } until (($ServiceGone -and $ProcessGone) -or $Elapsed -ge $TimeoutSec)
 
    if ($ServiceGone -and $ProcessGone) {
        Write-Log "CCM uninstall confirmed complete after ${Elapsed}s"
    } else {
        Write-Log "WARNING: CCM uninstall did not complete within ${TimeoutSec}s - continuing anyway" -Level Warning
    }
} else {
    Write-Log "ccmsetup.exe not found - skipping uninstall" -Level Warning
}
 
# ---------------------------------------------------------------
# STEP 4 - Remove CCM WMI namespaces (these hold DLL locks)
# ---------------------------------------------------------------
Write-Log "Removing CCM WMI namespaces"
@("root\ccm", "root\ccm\Policy", "root\ccm\SoftwareMeteringAgent",
  "root\SmsDm", "root\cimv2\SMS") | ForEach-Object {
    try {
        $ns = $_
        Get-WmiObject -Namespace $ns -Class "__Namespace" -ErrorAction Stop | Out-Null
        ([wmiclass]"\\.\$ns`:__Namespace").Delete() | Out-Null
        Write-Log "Removed WMI namespace: $ns"
    } catch {
        Write-Log "WMI namespace not found (OK): $_"
    }
}
 
# ---------------------------------------------------------------
# STEP 5 - Remove CCM scheduled tasks
# ---------------------------------------------------------------
Write-Log "Removing CCM scheduled tasks"
Get-ScheduledTask -ErrorAction SilentlyContinue |
    Where-Object { $_.TaskPath -match 'Microsoft\\Configuration Manager' -or $_.TaskName -match 'CCM|SMS|SCCM' } |
    ForEach-Object {
        Unregister-ScheduledTask -TaskName $_.TaskName -TaskPath $_.TaskPath -Confirm:$false -ErrorAction SilentlyContinue
        Write-Log "Removed scheduled task: $($_.TaskName)"
    }
 
# ---------------------------------------------------------------
# STEP 6 - Remove registry keys
# ---------------------------------------------------------------
Write-Log "Removing SCCM registry entries"
@(
    "HKLM:\SOFTWARE\Microsoft\CCM",
    "HKLM:\SOFTWARE\Microsoft\CCMSetup",
    "HKLM:\SOFTWARE\Microsoft\SMS",
    "HKLM:\SOFTWARE\Microsoft\SystemCertificates\SMS",
    "HKLM:\SOFTWARE\Wow6432Node\Microsoft\CCM",
    "HKLM:\SOFTWARE\Wow6432Node\Microsoft\SMS",
    "HKLM:\SYSTEM\CurrentControlSet\Services\CcmExec",
    "HKLM:\SYSTEM\CurrentControlSet\Services\ccmsetup",
    "HKLM:\SYSTEM\CurrentControlSet\Services\smstsmgr",
    "HKLM:\SYSTEM\CurrentControlSet\Services\CmRcService"
) | ForEach-Object {
    if (Test-Path $_) {
        Remove-Item -Path $_ -Recurse -Force -ErrorAction SilentlyContinue
        if (-not (Test-Path $_)) {
            Write-Log "Verified removed: $_"
        } else {
            Write-Log "WARNING: Failed to remove: $_" -Level Warning
        }
    } else {
        Write-Log "Not found (OK): $_"
    }
}
 
# ---------------------------------------------------------------
# STEP 7 - Remove CCM directories using robocopy empty-folder trick
# (bypasses locks that Remove-Item cannot break)
# ---------------------------------------------------------------
Write-Log "Removing SCCM directories"
 
function Remove-LockedDirectory {
    param([string]$Path)
    if (-not (Test-Path $Path)) {
        Write-Log "Not found (OK): $Path"
        return
    }
    # Robocopy mirrors an empty folder over the target, clearing contents without lock issues
    $Empty = "$env:TEMP\__empty_robocopy"
    New-Item -ItemType Directory -Path $Empty -Force -ErrorAction SilentlyContinue | Out-Null
    Start-Process "robocopy.exe" -ArgumentList "`"$Empty`" `"$Path`" /MIR /NFL /NDL /NJH /NJS /R:1 /W:1" -Wait -NoNewWindow -ErrorAction SilentlyContinue
    Remove-Item $Empty -Force -ErrorAction SilentlyContinue
 
    # Now take ownership and delete the emptied shell
    Start-Process "takeown.exe" -ArgumentList "/f `"$Path`" /r /d y" -Wait -NoNewWindow -ErrorAction SilentlyContinue
    Start-Process "icacls.exe" -ArgumentList "`"$Path`" /grant administrators:F /t" -Wait -NoNewWindow -ErrorAction SilentlyContinue
    Remove-Item -Path $Path -Recurse -Force -ErrorAction SilentlyContinue
 
    if (Test-Path $Path) {
        Write-Log "WARNING: $Path still present after forced removal" -Level Warning
    } else {
        Write-Log "Verified removed: $Path"
    }
}
 
@(
    "C:\Windows\CCM",
    "C:\Windows\CCMCache",
    "C:\Windows\CCMSetup",
    "C:\MININT"
) | ForEach-Object { Remove-LockedDirectory $_ }
 
# Remove single files
@("C:\Windows\SMSCFG.ini") | ForEach-Object {
    if (Test-Path $_) {
        Remove-Item -Path $_ -Force -ErrorAction SilentlyContinue
        Write-Log "Removed: $_"
    }
}
 
# Remove SMS .mif files
$MifFiles = Get-ChildItem -Path "C:\Windows" -Filter "sms*.mif" -ErrorAction SilentlyContinue
if ($MifFiles.Count -gt 0) {
    $MifFiles | Remove-Item -Force -ErrorAction SilentlyContinue
    Write-Log "Removed $($MifFiles.Count) SMS .mif files"
} else {
    Write-Log "No SMS .mif files found"
}
 
# ---------------------------------------------------------------
# STEP 8 - Remove MDM / co-management enrollment leftovers
# These prevent Autopilot ZTID from being recognised
# ---------------------------------------------------------------
Write-Log "Removing MDM and co-management enrollment leftovers"
 
# Remove all existing MDM enrollment GUIDs
$EnrollmentRoot = "HKLM:\SOFTWARE\Microsoft\Enrollments"
if (Test-Path $EnrollmentRoot) {
    Get-ChildItem $EnrollmentRoot -ErrorAction SilentlyContinue | ForEach-Object {
        Remove-Item -Path $_.PSPath -Recurse -Force -ErrorAction SilentlyContinue
        Write-Log "Removed enrollment: $($_.PSChildName)"
    }
}
 
# Remove enrollment status and tracking keys
@(
    "HKLM:\SOFTWARE\Microsoft\Enrollments",
    "HKLM:\SOFTWARE\Microsoft\EnterpriseResourceManager",
    "HKLM:\SOFTWARE\Microsoft\PolicyManager\current",
    "HKLM:\SOFTWARE\Microsoft\PolicyManager\device",
    "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\MDMCommon",
    "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\MDM",
    "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\MDM",
    "HKLM:\SOFTWARE\Microsoft\CCM\CoManagementHandler",
    "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin"
) | ForEach-Object {
    if (Test-Path $_) {
        Remove-Item -Path $_ -Recurse -Force -ErrorAction SilentlyContinue
        Write-Log "Removed: $_"
    } else {
        Write-Log "Not found (OK): $_"
    }
}
 
# Remove MDM enrollment scheduled tasks
Write-Log "Removing MDM enrollment scheduled tasks"
@(
    "\Microsoft\Windows\EnterpriseMgmt",
    "\Microsoft\Windows\EnterpriseMgmtNoncritical"
) | ForEach-Object {
    Get-ScheduledTask -TaskPath "$_\*" -ErrorAction SilentlyContinue | ForEach-Object {
        Unregister-ScheduledTask -TaskName $_.TaskName -TaskPath $_.TaskPath -Confirm:$false -ErrorAction SilentlyContinue
        Write-Log "Removed MDM task: $($_.TaskPath)$($_.TaskName)"
    }
}
 
# Remove MDM device certificates that belonged to the old enrollment
Write-Log "Removing old MDM device certificates"
Get-ChildItem "Cert:\LocalMachine\My" -ErrorAction SilentlyContinue | Where-Object {
    $_.Issuer -match "MS-Organization-Access|Microsoft Intune MDM Device CA|MDM"
} | ForEach-Object {
    Remove-Item -Path "Cert:\LocalMachine\My\$($_.Thumbprint)" -Force -ErrorAction SilentlyContinue
    Write-Log "Removed MDM certificate: $($_.Subject) [$($_.Thumbprint)]"
}
 
# Re-run dsregcmd /leave to ensure AAD join state is fully cleared after above cleanup
Write-Log "Re-running dsregcmd /leave to flush AAD state"
Start-Process "dsregcmd.exe" -ArgumentList "/leave" -Wait -NoNewWindow -ErrorAction SilentlyContinue
 
# ---------------------------------------------------------------
# STEP 9 - Remove TS post action, unattend files, setup remnants
# ---------------------------------------------------------------
Write-Log "Removing SMSTSPostAction"
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\SMS\Task Sequence" -Name "SMSTSPostAction" -ErrorAction SilentlyContinue
 
Write-Log "Removing cached unattend files"
@("C:\Windows\Panther\Unattend.xml", "C:\Windows\Panther\unattend") | ForEach-Object {
    if (Test-Path $_) {
        Remove-Item -Path $_ -Recurse -Force -ErrorAction SilentlyContinue
        Write-Log "Removed $_"
    }
}
 
Write-Log "Removing setup state remnants"
Remove-ItemProperty -Path "HKLM:\SYSTEM\Setup" -Name "CmdLine" -ErrorAction SilentlyContinue
Remove-ItemProperty -Path "HKLM:\SYSTEM\Setup" -Name "SetupType" -ErrorAction SilentlyContinue
 
$TagPath = "C:\Windows\Setup\Scripts\DisableCMDRequest.TAG"
if (Test-Path $TagPath) {
    Remove-Item -Path $TagPath -Force -ErrorAction SilentlyContinue
    Write-Log "Removed DisableCMDRequest.TAG"
} else {
    Write-Log "DisableCMDRequest.TAG not found"
}
 
# Launch OOBE
Write-Log "Launching sysprep /oobe /reboot /quiet"
 
$Sysprep = Start-Process `
    -FilePath "C:\Windows\System32\Sysprep\Sysprep.exe" `
    -ArgumentList "/oobe /reboot /quiet" `
    -NoNewWindow `
    -Wait `
    -PassThru
 
Write-Log "Sysprep exited with code: $($Sysprep.ExitCode)"
 
if ($Sysprep.ExitCode -ne 0) {
    Write-Log "Sysprep failed - dumping Panther log" -Level Warning
    $PantherLog = "C:\Windows\System32\Sysprep\Panther\setupact.log"
    if (Test-Path $PantherLog) {
        Get-Content $PantherLog -Tail 40 -ErrorAction SilentlyContinue |
            ForEach-Object { Add-Content -Path $LogPath -Value " [Panther] $_" -ErrorAction SilentlyContinue }
    }
}
 
Write-Log "PrepareOOBE finished"
'@


New-Item `
    -ItemType Directory `
    -Path "C:\Windows\Temp" `
    -Force `
    -ErrorAction SilentlyContinue | Out-Null

New-Item `
    -ItemType File `
    -Path "C:\Windows\Temp\PrepareOOBE.ps1" `
    -Value $ScriptBlock `
    -Force | Out-Null

Write-Output "PrepareOOBE.ps1 created at C:\Windows\Temp\PrepareOOBE.ps1"