sync: auto-sync from ACG-TECH03L at 2026-04-17 23:51:18
Author: Howard Enos Machine: ACG-TECH03L Timestamp: 2026-04-17 23:51:18
This commit is contained in:
102
clients/cascades-tucson/scripts/hive-cleanup-shellfolders.ps1
Normal file
102
clients/cascades-tucson/scripts/hive-cleanup-shellfolders.ps1
Normal file
@@ -0,0 +1,102 @@
|
||||
# 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%\<Folder> 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\<sam>\
|
||||
#
|
||||
# ROLLBACK
|
||||
# A timestamped backup is written to C:\ProfileBackups\ before any change.
|
||||
# Restore: Copy-Item <backup> <ntuser-path> -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)"
|
||||
Reference in New Issue
Block a user