Powershell's Copy-Item gives inconsistent results on file system and registry - powershell

I am trying to use Powershell's Copy-Item commandlet with the -Recurse and -Force parameters to copy settings from one registry key and overwrite the same settings (if they exist) in another key. If I were using this same command on a file system, then any folders that exist in source and target would be overwritten in the target.
When using this command on the registry, however, if there are keys in the target that exist, the source key is copied as a subkey. Example:
Starting state:
HKCU:Software\OldVendor\Program
HKCU:Software\OldVendor\Program\Setting1
HKCU:Software\OldVendor\Program\Setting2
HKCU:Software\NewVendor\Program
HKCU:Software\NewVendor\Program\Setting1
HKCU:Software\NewVendor\Program\Setting2
Now if I run this command:
Copy-Item -Path "HKCU:Software\OldVendor\Program" -Destination "HKCU:Software\NewVendor" -Recuse -Force
I expect the same structure to be maintained. In fact, the structure under NewVendor looks like this:
HKCU:Software\NewVendor\Program
HKCU:Software\NewVendor\Program\Setting1
HKCU:Software\NewVendor\Program\Setting1\Setting1
HKCU:Software\NewVendor\Program\Setting2
HKCU:Software\NewVendor\Program\Setting2\Setting2
Can anyone tell me how to get Powershell to overwrite existing registry keys, instead of copying to subkeys?

It seems to me you want to copy items and their values, those are properties, not keys. You can list them as follows:
get-itemproperty "HKCU:Software\NewVendor\Program"
or u can use alias and omit quotes:
gp HKCU:Software\NewVendor\Program
Another, better way, which doesn't view powershell specific properties:
Get-Item HKLM:\SOFTWARE\NewVendor\Program | select -ExpandProperty Property
So, if u want to copy all the properties from one key to another:
Get-Item HKLM:\SOFTWARE\Program | select -ExpandProperty Property | % {Copy-ItemProperty -Path HKLM:\SOFTWARE\Program -Destination HKLM:\SOFTWARE\AnotherProgram -Name $_ -Verbose}
You have to use foreach loop, beacuse Copy-ItemProperty cmdlet can copy only one property been specified.

Related

Searching for only folders that contain a certain folder using powershell

I am trying to use powershell to update some programs for my company. I am writing a script to do so (as instructed). When I install the new version of the program on the machines, it also requires me to 'upgrade' existing folders to match the new version of the software.
I need to find all of the folders that contain a certain hidden folder(let the name of said folder be .blah). I am trying to use the get-childitem command, with -path [drives to check] -Recurse -Directory -Force -EA SilentlyContinue. However, I am not sure how to filter correctly to only find folders that contain the .blah folder inside of it.
Help would be very much appreciated.
Combine your Get-ChildItem call with a Where-Object call that tests for a child directory of a given name using Test-Path:
# Note: "." refers to the *current* directory
# Adjust as needed.
Get-ChildItem -LiteralPath . -Recurse -Directory -Force -ErrorAction Ignore |
Where-Object {
Test-Path -ItemType Container -LiteralPath "$($_.FullName)\.blah"
}
The Get-ChildItem call outputs all directories (-Directory) in the entire directory subtree (-Recurse), including hidden ones (-Force), ignoring any errors (such as from lack of permissions, -ErrorAction Ignore).
The Where-Object call calls Test-Path to look for a .blah child directory (-ItemType Container) in the directory at hand ($_).
With a -LiteralPath argument, Test-Path finds the specified path if it exists, irrespective of whether the target file or directory is hidden.
By contrast, with a wildcard-based -Path argument, hidden items are not found, and given that, as of PowerShell 7.2.5, Test-Path has no -Force switch, there is no way to force their inclusion; this gap in functionality is the subject of GitHub issue #6501.
Note: In PowerShell (Core) 7+, you could simplify "$($_.FullName)\.blah" to "$_\.blah", because the [System.IO.DirectoryInfo] and [System.IO.FileInfo] instances output by Get-ChildItem and Get-Item there consistently stringify to their full path (.FullName) property, unlike in WindowsPowerShell, where they situationally stringify by their file/directory name only - see this answer.

Script to Find a registry key with wildcard and remove it

I am trying to clean out old Firefox registry entries that are causing our vulnerability scanner to freak out.
The script I am using is:
New-PSDrive HKU Registry HKEY_USERS
Get-ItemProperty -Path "HKU:\*\Software\Mozilla\Mozilla Firefox*" |
Select-Object -ExpandProperty PSPath |
ForEach-Object {Remove-PsPath -Path $_ -WhatIf}
but it fails. I know my issue is in the last section: ForEach-Object {Remove-PsPath -Path $_ -WhatIf} as I can run the other part of the script and get my expected data returns.
The Keys in the registry I want to remove are located in the HKU\%%%randoms SID%%%\Software\Mozilla path. They are:
HKU\%%%randoms SID%%%\Software\Mozilla\Mozilla Firefox
HKU\%%%randoms SID%%%\Software\Mozilla\Mozilla Firefox ESR
I want the script to remove the entire key and all the subkeys. What am I doing wrong in my script?
Remove-PsPath doesn't exist, as far as I can tell. But it all can be reduced to this:
New-PSDrive HKU Registry HKEY_USERS
Remove-Item "HKU:\*\Software\Mozilla\*Firefox*" -Recurse -WhatIf
And of course, remove -WhatIf once you're sure you want to run it.
Also note that I've added an extra wildcard before Firefox because I noticed on my test machine that registry key was simply called Firefox and not Mozilla Firefox. This'll still target your original registry keys.

Pulling a dir and all its contents from every client on a domain and copy to file share?

I am looking to copy a bunch of dir's "C:\Users\userOne\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates" and all of their contents onto a share with the folder being named the client and the user name.
I am sure a for loop is the best way to go about this but I am hung up on what variables to use to get what I want.
The array will surely contain a list of every workstation on the AD, but I need to specify a bunch of directories per workstation. Maybe a wildcard like this will work? C:\Users*\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates
I've seen tons of useful examples on here but most of them involve copying from one location to many instead of my situation where its from many to one.
This is my first post, thanks in advance for the help!
Yes, you can use ...\*\... in a wildcard-based path to represent all directories at a given level of a directory hierarchy.
In your scenario, you could so something like the following (using a local path on a single machine for simplicity) - be sure to run the command elevated (as administrator) so you're permitted to access other users' home directories:
Get-ChildItem C:\Users\*\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates |
Select-Object #{ name='User'; expression={ ($_.FullName -split '\\')[2] } }, FullName |
ForEach-Object {
Copy-Item -WhatIf -Recurse -Force -LiteralPath $_.FullName "\\some\share\$($_.User)"
}
Get-ChildItem C:\Users\*\... implicitly loops over all user home directories and returns the directory at the specified remaining path for each, if present.
The Select-Object call transforms the System.IO.DirectoryInfo instances emitted by Get-ChildItem into custom objects with .FullName (the full directory path) and .User properties (the username implied by the path), by way of a calculated property.
The ForEach-Object call then uses Copy-Item to copy each directory to a destination directory on the target share named for the user; -Recurse copies the directory and all its contents (the directory's whole subtree), and -Force includes hidden items that would be excluded by default.
The -WhatIf common parameter in the Copy-Item command previews the operation. Remove -WhatIf once you're sure the operation will do what you want.

Multiple csv files to one csv file - Powershell

I have been reading through some of the previous posts about the concept, but all the solutions i find very different from eachother.
I have multiple csv's devided over seperate folders (all with kind of the same path but the subfolders are different).
I now need to import these csv's, change the headers and export it into one single csv.
I have been trying with this but im getting a very weird error: Import-Csv : Cannot open file "C:\Windows\system32\Book1.csv"
Although my path is refering to C:\csv ?
$CSVFolder = 'C:\csv\'; #'
$OutputFile = 'C:\export\test3.csv';
$CSV= #();
Get-ChildItem -Path $CSVFolder -Filter *.csv | ForEach-Object {
$CSV += #(Import-Csv -Path $_)
}
$CSV | Export-Csv -Path $OutputFile -NoTypeInformation -Force;
I was thinking of using a datatable for the headers, but its not so clear to me at this point. Also the issue with the error is not clear for me...
As has been noted:
Import-Csv -Path $_ should be Import-Csv -Path $_.FullName in your code,
(Strictly speaking, it should be Import-Csv -LiteralPath $_.FullName, because strings passed to the -Path parameter are subject to wildcard resolution).
because the [System.IO.FileInfo] instances representing files returned by Get-ChildItem are converted to strings when they are passed to the -Path (or -LiteralPath) parameter as a command-line argument (as opposed to via the pipeline), in which case the the mere file name rather than the full path is used, if your Get-ChildItem command targets a directory in Windows PowerShell (see background information below).
A mere filename such as Book1.csv is interpreted as relative to the current directory (which happened to be C:\Windows\system32 in your case), so Import-Csv looks for file C:\Windows\system32\Book1.csv rather than the intended C:\csv\Book1.csv.
Note that piping Get-ChildItem output to cmdlets is generally not affected by this, because the .PSPath property (which PowerShell adds behind the scenes) containing the full path (including PS provider prefix) binds to the -LiteralPath parameter.
Note that as of PSv5.1.14393.693, however, this mechanism is broken for Import-Csv, due to a bug.
This is a common pitfall that occurs whenever [System.IO.FileInfo] instances are passed to cmdlets that accept file paths via [string](-array)-typed parameters as arguments.
To be safe: Always use .FullName when you pass objects received from Get-ChildItem to another command as a parameter value (as opposed to via the pipeline) to ensure that the full path is passed.
Optional background information:
This behavior is a pitfall, because it is perfectly reasonable to assume that passing a [System.IO.FileInfo] instance as-is to a command that accepts file paths works, given the object-oriented nature of PowerShell - especially, since it does work reliably when using the pipeline rather than a parameter value.
Unfortunately, the built-in cmdlets that accept file paths (-Path, -LiteralPath parameters) do so as [string]s only (there is no parameter set that accepts [System.IO.FileInfo] instances directly), and it is in the course of [System.IO.FileInfo]-to-string conversion that the problem arises.
There also wouldn't be a problem if the [System.IO.FileInfo] instances consistently evaluated to the files' full paths, which is unfortunately not the case in Windows PowerShell (this has since been fixed in PowerShell Core):
Get-ChildItem <directory> outputs [System.IO.FileInfo] instances that evaluate to file names only in a string context.
Get-ChildItem <literalFilePathOrWildCardExpr> outputs [System.IO.FileInfo] instances that evaluate to full paths.
In other words: It is only if Get-ChildItem targets a directory (folder) that the objects it returns evaluate to their file names only in a string context.
Targeting a specific file or using a wildcard expression results in full paths, by contrast; with Get-Item, that's always the case.
You simply need to 'fullname' property, instead of 'name'.
Ex:
PS /Users/adil/Downloads> gi *csv |select name
Name
----
tradesdownload.csv
PS /Users/adil/Downloads> gi *csv |select name, fullname
Name FullName
---- --------
tradesdownload.csv /Users/adil/Downloads/tradesdownload.csv
try this code. This code take all csv file, import them and take only column 1, 2, 3 and change column name to header1, header2, header3, then export all into new csv file
Get-ChildItem "C:\temp2" -Filter "*.csv" |
%{Import-Csv $_.FullName -Header header1, header3,header4} |
Export-Csv "c:\temp\result.csv" -NoTypeInformation
#a short version (for no purist)
gci "C:\temp2" -Filter "*.csv" | %{ipcsv $_.FullName -Header header1, header3,header4} | epcsv "c:\temp\result.csv" -NoType

PowerShell script to find registry key

i need some help, i want to create a powershell script that searches the registry for just the key RebootRequired, no value or data search is needed.
with a IF find create a txt file named RebootRequired.txt in folder C:\Candi\
is that even possible?
been trying out some scripting, but i can barley make the script to find the key if it present within the registry.
You could retrieve all keys with Get-ChildItem -Recurse and then filter on key names with Where-Object.
The Registry provider is a little different from the FileSystem provider, in that the Name property of each item is the entire key path (ie. HKEY_LOCAL_MACHINE\Software\Microsoft instead of just Microsoft). You can use PSChildName to refer to the leaf name:
if(#(Get-ChildItem HKLM: -Recurse |Where-Object {$_.PSChildName -eq 'RebootRequired'}))
{
# Something was returned! Create the file
New-Item C:\Candi\RebootRequired.txt -ItemType File
}
You can suppress error messages from inaccessible keys with the -ErrorAction SilentlyContinue parameter argument with Get-ChildItem