# Per-user NTUSER.DAT shell-folder cleanup for ProfWiz-migrated Cascades users. # # WHAT IT DOES # Finds the user's offline NTUSER.DAT, backs it up, loads the hive, and resets # any User Shell Folders values that are poisoned with the SYSTEM-profile path # (C:\Windows\system32\config\systemprofile\...) back to the standard # %USERPROFILE%\ REG_EXPAND_SZ defaults. Desktop is intentionally NOT # touched — on machines with a working Desktop reg hack, leaving it alone is # the safe default. # # WHEN TO USE # ProfWiz-migrated user whose Folder Redirection GPO won't apply cleanly, # whose logon hangs at "Welcome," or whose Documents/Downloads sidebar shows # the "this file has no associated app" error. Always verify the hive is # poisoned FIRST by logging in and reading HKCU\...\User Shell Folders. # # HOW TO RUN # - ScreenConnect Backstage PowerShell (runs as SYSTEM) is the most reliable # - User MUST be logged OFF (hive loads from NTUSER.DAT on disk; can't be # locked by an active session) # - Pass the user's profile path as -ProfilePath, or omit to use the default # C:\Users\\ # # ROLLBACK # A timestamped backup is written to C:\ProfileBackups\ before any change. # Restore: Copy-Item -Force (user logged out) [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$ProfilePath, # e.g. 'C:\Users\Sharon Edwards' [string]$BackupDir = 'C:\ProfileBackups', [string]$TempHiveName = 'ProfileFix' ) $ErrorActionPreference = 'Stop' $ntuser = Join-Path $ProfilePath 'NTUSER.DAT' if (-not (Test-Path $ntuser)) { throw "NTUSER.DAT not found at $ntuser" } New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null $stamp = Get-Date -Format 'yyyyMMdd-HHmmss' $leaf = Split-Path $ProfilePath -Leaf $backup = Join-Path $BackupDir "$leaf-NTUSER.DAT.$stamp.bak" Copy-Item $ntuser $backup -Force Write-Host "[OK] Backup -> $backup" if (Test-Path "Registry::HKEY_USERS\$TempHiveName") { reg unload "HKU\$TempHiveName" 2>&1 | Out-Null Start-Sleep 1 } $loadResult = reg load "HKU\$TempHiveName" $ntuser 2>&1 if ($LASTEXITCODE -ne 0) { throw "reg load failed: $loadResult" } Write-Host "[OK] Hive loaded at HKU\$TempHiveName" try { $USF = "Registry::HKEY_USERS\$TempHiveName\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" # Known-poisoned value names + their default REG_EXPAND_SZ targets. # Desktop is deliberately omitted — don't clobber working redirections. # Include BOTH the legacy names and the KnownFolder GUID forms so the # Explorer sidebar resolves to the same place. $resets = [ordered]@{ 'Personal' = '%USERPROFILE%\Documents' 'My Music' = '%USERPROFILE%\Music' 'My Pictures' = '%USERPROFILE%\Pictures' 'My Video' = '%USERPROFILE%\Videos' 'Favorites' = '%USERPROFILE%\Favorites' '{FDD39AD0-238F-46AF-ADB4-6C85480369C7}' = '%USERPROFILE%\Documents' # Documents KF '{374DE290-123F-4565-9164-39C4925E467B}' = '%USERPROFILE%\Downloads' # Downloads KF } # Only touch a value if it's CURRENTLY poisoned (points under systemprofile) # or missing. This keeps working redirections (e.g., UNC paths set by a # functioning CSE) intact. $poisonPrefix = 'C:\Windows\system32\config\systemprofile' foreach ($name in $resets.Keys) { $current = (Get-ItemProperty -Path $USF -Name $name -ErrorAction SilentlyContinue).$name $new = $resets[$name] if ($null -eq $current) { New-ItemProperty -Path $USF -Name $name -Value $new -PropertyType ExpandString -Force | Out-Null Write-Host " [ADDED] $name = $new" } elseif ($current -like "$poisonPrefix*") { Set-ItemProperty -Path $USF -Name $name -Value $new -Type ExpandString Write-Host " [CHANGED] $name : '$current' -> '$new' (was poisoned)" } else { Write-Host " [KEEP] $name = '$current' (not poisoned, leaving alone)" } } } finally { [gc]::Collect() Start-Sleep 2 reg unload "HKU\$TempHiveName" 2>&1 | Out-Null Write-Host "[OK] Hive unloaded" } Write-Host "`nBackup: $backup" Write-Host "Rollback: Copy-Item '$backup' '$ntuser' -Force (while user logged out)"