Powershell Find-And-Replace on registry values?
Solution 1
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.
Solution 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!!!" } }
}
}
}
}
}
}
Solution 3
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.
Related videos on Youtube
Derek
Updated on June 04, 2022Comments
-
Derek almost 2 years
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 tryingGet-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 messageThe 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. -
Thomas over 3 yearstrows me the follow error:
Get-ItemProperty : Die angegebene Umwandlung ist ungültig. In C:\Install\find-replace-reg.ps1:24 Zeichen:9 + Get-ItemProperty -Path "$PowershellRegPrefix$_" | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Get-ItemProperty], InvalidCastException + FullyQualifiedErrorId : System.InvalidCastException,Microsoft.PowerShell.Commands.GetItemPropertyCommand
-
Mike 'Pomax' Kamermans about 2 yearsit's been a while, but can you still flag this as accepted answer if this solved the problem? Accepting answers is still the backbone of how stackexchange sites work =)