Powershell Find-And-Replace on registry values?

Publish date: 2023-12-23

In 'HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' I have some paths set to an old server. e.g.:

'My Pictures' is set to '\\DeadServer\RedirectedFolders\%UserName%\My Documents\My Pictures'

I'd like to replace "\\DeadServer\RedirectedFolders" with "C:\Users"

How can this be done in powershell?

I got as far as trying
Get-ItemProperty -path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\*\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" | ? {$_.PSObject.Properties -like "*DeadServer*"} 

But I think I'm getting confused with how the entry I want to change is a 'Property', and not an 'Item', and I don't know how to iterate through properties like I'd do Items.

Before you ask, I've already made this change with Group Policy, but it's not taking. Users are getting a message

The Recycle Bin on \DeadServer\RedirectedFolders\%UserName%\My Documents\My Pictures` is corrupted. Do you want to empty the Recycle Bin for this drive?

upon login which is keeping Folder Redirection from applying.
This is my attempt to force the change back to local storage manually.

2

4 Answers

I figured it out. Took me a long time, but I wrote a rather inelegant script:

Get-Item -ErrorAction SilentlyContinue -path "Microsoft.PowerShell.Core\Registry::HKEY_USERS\*\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" | foreach { Get-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::$_" | foreach { $CurrentUserShellFoldersPath = $_.PSPath $SID = $CurrentUserShellFoldersPath.Split('\')[2] $_.PSObject.Properties | foreach { if ($_.Value -like "*DeadServer*") { write-host "Path:`t`t"$CurrentUserShellFoldersPath write-host "SID:`t`t"$SID write-host "Name:`t`t"$_.Name write-host "Old Value:`t"$_.Value $newValue = $_.Value $newValue = $newValue -replace '\\\\DeadServer\\RedirectedFolders', "C:\Users" $newValue = $newValue -replace "My Documents\\", "" $newValue = $newValue -replace "My ", "" Write-Host "New Value:`t"$newValue Set-ItemProperty -Path $CurrentUserShellFoldersPath -Name $_.Name -Value $newValue Write-host "================================================================" } } } } 

I'd love to learn of a faster or more elegant way to do this if any of you have one.

2

Here's an easy to use registry replace function, which can search a path recursively.

# Replace all registry key values and/or registry key names under a given path. # Example Usage: # RegistryValue-Replace "ExistingValue" "NewValue" 'HKEY_CURRENT_USER\Software\100000_DummyData' # RegistryValue-Replace "ExistingValue" "NewValue" 'HKEY_USERS\*\Software\100000_DummyData' -ReplaceKeyNames $true -CaseSensitive $true # RegistryValue-Replace 'C:\\Program Files\\Microsoft SQL Server' 'E:\Program Files\Microsoft SQL Server' 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server*' -LoggingOn $true function RegistryValue-Replace ( [string]$OldValue = $(throw “OldValue (the current value) required.”), [string]$NewValue = $(throw “NewValue (the replacement value) required.”), [string]$RegkPath = $(throw “RegkPath (The full registry key path) required.”), [bool] $CaseSensitive = $false, # If true, search and replace is case sensitive [bool] $WholeWord = $false, # If true, searches for whole word within the value. [bool] $ExactMatch = $false, # If true, the entire value must match OldValue, and partial replacements are NOT performed [bool] $ReplaceKeyNames = $false, # If true, replaces registry key names [bool] $ReplaceValues = $true, [bool] $LoggingOn = $false ) { $PowershellRegPrefix = 'Microsoft.PowerShell.Core\Registry::' $MatchFor = if ($WholeWord -eq $true) {".*\b$OldValue\b.*"} else { ".*$OldValue.*" } if ($RegkPath -NotLike "$PowershellRegPrefix*") { $RegkPath = $PowershellRegPrefix + $RegkPath } @(Get-Item -ErrorAction SilentlyContinue -path $RegkPath) + @(Get-ChildItem -Recurse $RegkPath -ErrorAction SilentlyContinue) | foreach { Get-ItemProperty -Path "$PowershellRegPrefix$_" | foreach { $CurrentShellFoldersPath = $_.PSPath $SID = $CurrentShellFoldersPath.Split('\')[2] $_.PSObject.Properties | foreach { if ($_.Name -cne "PSChildName" -and (($ExactMatch -eq $true -and $_.Value -clike $OldValue) -or ($ExactMatch -eq $false -and (($CaseSensitive -eq $false -and $_.Value -match $MatchFor) -or ($CaseSensitive -eq $true -and $_.Value -cmatch $MatchFor))))) { $Original = $_.Value $Create_NewValue = $_.Value $SubKeyName = $_.Name if ($CaseSensitive -eq $true){ $Create_NewValue = $Create_NewValue -creplace $OldValue, $NewValue } else { $Create_NewValue = $Create_NewValue -replace $OldValue, $NewValue } if ($_.Name -eq "PSPath" -and $_.Value -eq $CurrentShellFoldersPath) { if ($ReplaceKeyNames -eq $true) { Move-Item -Path $CurrentShellFoldersPath -Destination $Create_NewValue if ($LoggingOn -eq $true){ Write-host "Renamed registry key '$CurrentShellFoldersPath' to '$Create_NewValue'" } } else { if ($LoggingOn -eq $true){ Write-host "....Skipping renaming key '$CurrentShellFoldersPath->$SubKeyName' due to input option!!!" } } } else { if ($ReplaceValues -eq $true) { Set-ItemProperty -Path $CurrentShellFoldersPath -Name $_.Name -Value $Create_NewValue if ($LoggingOn -eq $true){ Write-host "Renamed '$Original' to '$Create_NewValue' for registry key '$CurrentShellFoldersPath->$SubKeyName'" } } else { if ($LoggingOn -eq $true){ Write-host "....Skipping renaming value '$CurrentShellFoldersPath->$SubKeyName' due to input option!!!" } } } } } } } } 
1

Not really happy with this so I will be happy and sad if someone puts this to shame. It's been mostly tested as far as verifying that it is locating the correct keys.

If(!(Test-Path HKU:)){New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS} $registrySearchPath = "HKU:\*\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" $pathToReplace = [regex]::Escape("C:\Users") $newPath = '%USERPROFILE%' Get-Item -path $registrySearchPath -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name | Where-Object{$_ -match "^HKEY_USERS\\S-1-5-21"} | ForEach-Object{ $key = $_ -replace "^HKEY_USERS","HKU:" (Get-ItemProperty $key).psobject.Properties | Where-Object{$_.Value -match $pathToReplace} | Select-Object Name,Value | ForEach-Object{ Set-ItemProperty -Path $key -Name $_.Name -Value ($_.Value -replace $pathToReplace,$newPath) -WhatIf } } 

Use a Psdrive to map HKU since its not a default drive in PowerShell. Get all keys back that have at least a path to "\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders". Omit the default keys and any other localesque accounts by only looking for the ones with "S-1-5-21" as part of the key. Then for each of those that is located find every registry value with data that matchs the path you are looking for.

Set-ItemProperty -Path $key -Name $_.Name -Value ($_.Value -replace $pathToReplace,$newPath) -WhatIf 

Drawing a little more attention on the last part here. With all the values that matched we replace the data with a simple -replace. I have a -WhatIf on there to be sure you test in case something bad happens. I would suggest commenting out that line and outputing just $_.Value -replace $pathToReplace,$newPath to verify that it is doing what you expect it to.

Make sure that you change the values for $pathToReplace and $newPath then Test twice, execute once.

The following is an example I use for after I rename the path of a profile for a user account:

function get-itemproperty2 { # get-childitem skips top level key, use get-item for that # set-alias gp2 get-itemproperty2 param([parameter(ValueFromPipeline)]$key) process { $key.getvaluenames() | foreach-object { $value = $_ [pscustomobject] @{ Path = $Key -replace 'HKEY_CURRENT_USER', 'HKCU:' -replace 'HKEY_LOCAL_MACHINE','HKLM:' Name = $Value Value = $Key.GetValue($Value) Type = $Key.GetValueKind($Value) } } } } ls -r hkcu: | get-itemproperty2 | where Value -match "(.*)C:\\Users\\Admin(.*)" | ForEach-Object { $newkey = $_.Value -replace '(.*)C:\\Users\\Admin(.*)', '$1C:\Users\dennisg$2'; if ($_.Name -eq '') { set-itemproperty -Path $_.Path -Name '(Default)' -Value $newkey -Type $_.Type ; $outInfo = '****' + $_.Path + " | " + $_.Name + " | " + $newkey; } else { set-itemproperty -Path $_.Path -Name $_.Name -Value $newkey -Type $_.Type ; $outInfo = $_.Path + " | " + $_.Name + " | " + $newkey; } echo $outInfo; } 

Function is from this SO question Use PowerShell to search for string in registry keys and values

ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJoaW9uaGWBcnyOqaawnaKotaa4y2adoqaUYq6vsIyrnKmkkZiybrvNZqmen5mowbPFjK%2BYpa2VqA%3D%3D

You Might Also Like