I am trying to use the value of a switch parameter as the trigger to write to a csv file if the parameter is called with the script from the command line. However, with my current code the csv file is created whether I include the parameter or not. What's up with that?
Also, is there a better way to handle my else/if else/if else section?
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string]$dir,
[Parameter(Mandatory=$true)]
[int]$days,
[switch]$csv=$false
)
Process {
Clear-Host
$totSize = 0
$totFiles = 0
$modDate = (Get-date).AddDays(-$days).Date
$modfiles = Get-ChildItem -Path $dir -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -ge $modDate }
If ($csv = $true){
$modfiles | Select-Object -Property FullName, Length,LastWriteTime | Export-Csv -Path .\modFiles.csv -NoTypeInformation
}
foreach ($file in $modfiles){
$totFiles = $totFiles + 1
$totSize = $totSize + $file.Length
}
If ($totSize -lt 1MB){
$outSize = $totSize/1KB
$unit = "KB"
}
elseif (($totSize -ge 1MB) -and ($totSize -lt 1GB)){
$outSize = $totSize/1MB
$unit = "MB"
}
elseif ($totSize -ge 1GB){
$outSize = $totSize/1GB
$unit = "GB"
}
$outRound = [math]::Round($outSize,2)
Write-Host $totFiles "Files"
Write-Host $outRound $unit
}
Two problems.
Do not specify a default value for a [switch] parameter. It will mess you up. Leave it off, and it will be $true if specified, and $false if
not.
When testing a logical value, such as an If statement, do not use the assignment equals (=), use the comparison equals (-eq).
If ($csv -eq $true){
$modfiles | Select-Object -Property FullName, Length,LastWriteTime | Export-Csv -Path .\modFiles.csv -NoTypeInformation
}
EDIT (Thanks #Scepticalist): Further, if the variable you are testing already holds a [bool] value, or can be implicitly converted to [bool], you don't even need the -eq $true part of the comparison, so:
If ($csv){
$modfiles | Select-Object -Property FullName, Length,LastWriteTime | Export-Csv -Path .\modFiles.csv -NoTypeInformation
}
Related
I am trying to run the below code to import a list of directories from a .csv file (if chosen as a parameter) and then do a foreach loop to determined what files have been modified in the last XX days in the directories listed in the .csv file.
However it appears that the $modfiles variable is overwritten in each iteration as opposed to being appended.
The .csv being imported has 3 directories in it but my output only shows the relevant files from the last directory imported. Is their away to append $modfiles?
CSV Contents:
$importDirs = Import-Csv $importCsv
foreach ($importDir in $importDirs){
$modfiles = Get-ChildItem -Path $importDir.Directory -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -ge $modDate }
}
Output:
Here is the entire script:
[CmdletBinding(DefaultParameterSetName='ManualDirectory')]
param (
[Parameter(Mandatory = $true, ParameterSetName = 'ManualDirectory')]
[System.String]
$dir,
[Parameter(Mandatory = $true, ParameterSetName = 'ImportDirectory')]
[System.String]
$importCsv,
[Parameter(Mandatory=$true)]
[int]$days,
[switch]$exportCsv,
[switch]$console
)
Process {
#Clear-Host
$totSize = 0
$totFiles = 0
$modDate = (Get-date).AddDays(-$days).Date
If ($dir){
$modfiles = Get-ChildItem -Path $dir -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -ge $modDate }
}
If ($importCsv){
$importDirs = Import-Csv $importCsv
foreach ($importDir in $importDirs){
$modfiles = Get-ChildItem -Path $importDir.Directory -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -ge $modDate }
}
}
If ($exportCsv){
$modfiles | Select-Object -Property FullName, Length,LastWriteTime | Export-Csv -Path .\modFiles.csv -NoTypeInformation
}
foreach ($file in $modfiles){
$totFiles = $totFiles + 1
$totSize = $totSize + $file.Length
If ($console -eq $true){
Write-Host $file.FullName
}
}
If ($totSize -lt 1MB){
$outSize = $totSize/1KB
$unit = "KB"
}
elseif (($totSize -ge 1MB) -and ($totSize -lt 1GB)){
$outSize = $totSize/1MB
$unit = "MB"
}
elseif ($totSize -ge 1GB){
$outSize = $totSize/1GB
$unit = "GB"
}
$outRound = [math]::Round($outSize,2)
Write-Host $totFiles "Files"
Write-Host $outRound $unit
}
$importDirs = Import-Csv $importCsv
foreach ($importDir in $importDirs){
$modfiles = Get-ChildItem -Path $importDir.Directory -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -ge $modDate }
}
Each time you loop through $importDirs, you assign a value to $modfiles. Each iteration, you overwrite the values from previous iteration.
If you need to keep a list of all the values, append the data by using
$modfiles = $modfiles, (Get-Childitem ...)
# or
$modfiles += Get-ChildItem ...
My objective is to write a powershell script that will recursively check a file server for any directories that are "x" (insert days) old or older.
I ran into a few issues initially, and I think I got most of it worked out. One of the issues I ran into was with the path limitation of 248 characters. I found a custom function that I am implementing in my code to bypass this limitation.
The end result is I would like to output the path and LastAccessTime of the folder and export the information into an easy to read csv file.
Currently everything is working properly, but for some reason I get some paths output several times (duplicates, triples, even 4 times). I just want it output once for each directory and subdirectory.
I'd appreciate any guidance I can get. Thanks in advance.
Here's my code
#Add the import and snapin in order to perform AD functions
Add-PSSnapin Quest.ActiveRoles.ADManagement -ea SilentlyContinue
Import-Module ActiveDirectory
#Clear Screen
CLS
Function Get-FolderItem
{
[cmdletbinding(DefaultParameterSetName='Filter')]
Param (
[parameter(Position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('FullName')]
[string[]]$Path = $PWD,
[parameter(ParameterSetName='Filter')]
[string[]]$Filter = '*.*',
[parameter(ParameterSetName='Exclude')]
[string[]]$ExcludeFile,
[parameter()]
[int]$MaxAge,
[parameter()]
[int]$MinAge
)
Begin
{
$params = New-Object System.Collections.Arraylist
$params.AddRange(#("/L","/S","/NJH","/BYTES","/FP","/NC","/NFL","/TS","/XJ","/R:0","/W:0"))
If ($PSBoundParameters['MaxAge'])
{
$params.Add("/MaxAge:$MaxAge") | Out-Null
}
If ($PSBoundParameters['MinAge'])
{
$params.Add("/MinAge:$MinAge") | Out-Null
}
}
Process
{
ForEach ($item in $Path)
{
Try
{
$item = (Resolve-Path -LiteralPath $item -ErrorAction Stop).ProviderPath
If (-Not (Test-Path -LiteralPath $item -Type Container -ErrorAction Stop))
{
Write-Warning ("{0} is not a directory and will be skipped" -f $item)
Return
}
If ($PSBoundParameters['ExcludeFile'])
{
$Script = "robocopy `"$item`" NULL $Filter $params /XF $($ExcludeFile -join ',')"
}
Else
{
$Script = "robocopy `"$item`" NULL $Filter $params"
}
Write-Verbose ("Scanning {0}" -f $item)
Invoke-Expression $Script | ForEach {
Try
{
If ($_.Trim() -match "^(?<Children>\d+)\s+(?<FullName>.*)")
{
$object = New-Object PSObject -Property #{
ParentFolder = $matches.fullname -replace '(.*\\).*','$1'
FullName = $matches.FullName
Name = $matches.fullname -replace '.*\\(.*)','$1'
}
$object.pstypenames.insert(0,'System.IO.RobocopyDirectoryInfo')
Write-Output $object
}
Else
{
Write-Verbose ("Not matched: {0}" -f $_)
}
}
Catch
{
Write-Warning ("{0}" -f $_.Exception.Message)
Return
}
}
}
Catch
{
Write-Warning ("{0}" -f $_.Exception.Message)
Return
}
}
}
}
Function ExportFolders
{
#================ Global Variables ================
#Path to folders
$Dir = "\\myFileServer\somedir\blah"
#Get all folders
$ParentDir = Get-ChildItem $Dir | Where-Object {$_.PSIsContainer -eq $True}
#Export file to our destination
$ExportedFile = "c:\temp\dirFolders.csv"
#Duration in Days+ the file hasn't triggered "LastAccessTime"
$duration = 800
$cutOffDate = (Get-Date).AddDays(-$duration)
#Used to hold our information
$results = #()
#=============== Done with Variables ===============
ForEach ($SubDir in $ParentDir)
{
$FolderPath = $SubDir.FullName
$folders = Get-ChildItem -Recurse $FolderPath -force -directory| Where-Object { ($_.LastAccessTimeUtc -le $cutOffDate)} | Select-Object FullName, LastAccessTime
ForEach ($folder in $folders)
{
$folderPath = $folder.fullname
$fixedFolderPaths = ($folderPath | Get-FolderItem).fullname
ForEach ($fixedFolderPath in $fixedFolderPaths)
{
#$fixedFolderPath
$getLastAccessTime = $(Get-Item $fixedFolderPath -force).lastaccesstime
#$getLastAccessTime
$details = #{ "Folder Path" = $fixedFolderPath; "LastAccessTime" = $getLastAccessTime}
$results += New-Object PSObject -Property $details
$results
}
}
}
}
ExportFolders
I updated my code a bit and simplified it. Here is the new code.
#Add the import and snapin in order to perform AD functions
Add-PSSnapin Quest.ActiveRoles.ADManagement -ea SilentlyContinue
Import-Module ActiveDirectory
#Clear Screen
CLS
Function ExportFolders
{
#================ Global Variables ================
#Path to user profiles in Barrington
$Dir = "\\myFileServer\somedir\blah"
#Get all user folders
$ParentDir = Get-ChildItem $Dir | Where-Object {$_.PSIsContainer -eq $True} | where {$_.GetFileSystemInfos().Count -eq 0 -or $_.GetFileSystemInfos().Count -gt 0}
#Export file to our destination
$ExportedFile = "c:\temp\dirFolders.csv"
#Duration in Days+ the file hasn't triggered "LastAccessTime"
$duration = 1
$cutOffDate = (Get-Date).AddDays(-$duration)
#Used to hold our information
$results = #()
$details = $null
#=============== Done with Variables ===============
ForEach ($SubDir in $ParentDir)
{
$FolderName = $SubDir.FullName
$FolderInfo = $(Get-Item $FolderName -force) | Select-Object FullName, LastAccessTime #| ft -HideTableHeaders
$FolderLeafs = gci -Recurse $FolderName -force -directory | Where-Object {$_.PSIsContainer -eq $True} | where {$_.GetFileSystemInfos().Count -eq 0 -or $_.GetFileSystemInfos().Count -gt 0} | Select-Object FullName, LastAccessTime #| ft -HideTableHeaders
$details = #{ "LastAccessTime" = $FolderInfo.LastAccessTime; "Folder Path" = $FolderInfo.FullName}
$results += New-Object PSObject -Property $details
ForEach ($FolderLeaf in $FolderLeafs.fullname)
{
$details = #{ "LastAccessTime" = $(Get-Item $FolderLeaf -force).LastAccessTime; "Folder Path" = $FolderLeaf}
$results += New-Object PSObject -Property $details
}
$results
}
}
ExportFolders
The FolderInfo variable is sometimes printing out multiple times, but the FolderLeaf variable is printing out once from what I can see. The problem is if I move or remove the results variable from usnder the details that print out the folderInfo, then the Parent directories don't get printed out. Only all the subdirs are shown. Also some directories are empty and don't get printed out, and I want all directories printed out including empty ones.
The updated code seems to print all directories fine, but as I mentioned I am still getting some duplicate $FolderInfo variables.
I think I have to put in a condition or something to check if it has already been processed, but I'm not sure which condition I would use to do that, so that it wouldn't print out multiple times.
In your ExportFolders you Get-ChildItem -Recurse and then loop over all of the subfolders calling Get-FolderItem. Then in Get-FolderItem you provide Robocopy with the /S flag in $params.AddRange(#("/L", "/S", "/NJH", "/BYTES", "/FP", "/NC", "/NFL", "/TS", "/XJ", "/R:0", "/W:0")) The /S flag meaning copy Subdirectories, but not empty ones. So you are recursing again. Likely you just need to remove the /S flag, so that you are doing all of your recursion in ExportFolders.
In response to the edit:
Your $results is inside of the loop. So you will have a n duplicates for the first $subdir then n-1 duplicates for the second and so forth.
ForEach ($SubDir in $ParentDir) {
#skipped code
ForEach ($FolderLeaf in $FolderLeafs.fullname) {
#skipped code
}
$results
}
should be
ForEach ($SubDir in $ParentDir) {
#skipped code
ForEach ($FolderLeaf in $FolderLeafs.fullname) {
#skipped code
}
}
$results
Afternoon All,
I need to run a search across all of our servers.
I have the list of servers in a text document and a list of keywords in another
$Servers = get-content -path 'C:\support\Server Search\Server Test.txt'
$Keywords = get-content -path "C:\Support\Server Search\Keyword Test.txt"
Foreach ($Server in $Servers){
Foreach ($Keyword in $Keywords){
Get-ChildItem "$Server" -Recurse | Where-Object {$_.Name -like "$Keyword"}
$i++
Write-Host "$found: $i - Current $ $_"
New-Object -TypeName PSCustomObject -Property #{
Directory = $_.Directory
Name = $_.Name
Length = $_.Length /1024
CreationTime = $_.CreationTime
LastWriteTime = $_.LastWriteTime
LastAccessTime = $_.LastAccessTime}|
select Directory,Name,Length,CreationTime,LastWriteTime,LastAccessTime |
Export-Csv "C:\support\server search\$Server.csv" -Append -NoTypeInformation
}}
$i = 0
Is there a way to indicate when a Keyword has been located and total keywords found? I feel like I need to change this Line but I cannot fathom what I would actually put, I've tried $Keywords but that just changes keyword everytime the directory changes
$i++
Write-Host "$found: $i - Current $ $_"
I'm assuming your $server is set up something like "\\servername\c$\"
when a Keyword has been located and total keywords found:
$Servers = get-content -path 'C:\support\Server Search\Server Test.txt'
$Keywords = get-content -path "C:\Support\Server Search\Keyword Test.txt"
$num = 0 #Total Keyword files Found
Foreach ($Server in $Servers){
Foreach ($Keyword in $Keywords){
#Keyword found Check
$Found = Get-ChildItem -Path "$Server" -Recurse -Include "$Keyword"
if($Found){
Foreach($File in $Found){
$num++ #increment num of keyword files found by 1
Write-Host "found: $num - $File"
New-Object -TypeName PSCustomObject -Property #{
Directory = $File.Directory
Name = $File.Name
Length = $File.Length /1024
CreationTime = $File.CreationTime
LastWriteTime = $File.LastWriteTime
LastAccessTime = $File.LastAccessTime}|
select Directory,Name,Length,CreationTime,LastWriteTime,LastAccessTime |
Export-Csv "C:\support\server search\$Server.csv" -Append -NoTypeInformation
}
}
}
}
Please let me know if this helps you progress. I can assist further if requested.
I need create this list to allow an other program to properly work. I use this code:
function analyse {
Param(
[parameter(Mandatory=$true)]
[String]$newPath
)
cd $newPath
dir | Foreach-Object {
$data = Get-Content -Path o:\******\public\ParcoursArborescence\Limitless\data.txt
if ($_.PsisContainer -eq $True) {
$testPath = $_.FullName + ";"
$name = $testPath
$testPath = $data -match [regex]::escape($testPath)
$testpath
if($testPath.Length -eq 0) {
$name | Out-File -Append "o:\******\public\ParcoursArborescence\Limitless\data.txt"
if ($_.FullName.Length -gt 248) {
"ecriture"
$result += $_.FullName + "`r"
} else {
"nouvelle analyse"
$_.Fullname
analyse $_.FullName
}
}
} else {
$testPath = $_.Directory.FullName + ";"
$name = $testPath
$testPath = $data -match [regex]::escape($testPath)
if($testPath.Length -eq 0) {
$name | Out-File -Append "o:\******\public\ParcoursArborescence\Limitless\data.txt"
$_.FullName.Length
if ($_.FullName.Length -gt 260) {
"ecriture2"
$result += $_.Directory.Name + "`r"
}
}
}
}
$result | Out-File -Append "o:\******\public\ParcoursArborescence\Limitless\bilanLimitless.txt"
}
But it takes hours and hours... I need to use this in thousands of folders. So, do you have any idea about how could it get faster ?
Maybe I'm oversimplifying things here, but why not list all the files at once, and test their FullName Length (PS 3.0 needed for the -File parameter of Get-ChildItem) ?
$maxLength = 248
Get-ChildItem $newPath -Recurse |
Where-Object { ($_.FullName.Length -gt $maxLength) } |
Select-Object -ExpandProperty DirectoryName -Unique |
Out-File "overlength_paths.txt"
For PS 2.0:
$maxLength = 248
Get-ChildItem $newPath -Recurse -File |
Where-Object { ($_.FullName.Length -gt $maxLength) -and (-not $_.PSisContainer) } |
Select-Object -ExpandProperty DirectoryName -Unique |
Out-File "overlength_paths.txt"
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.
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.
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!!!" } }
}
}
}
}
}
}
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