Cannot Get-ItemProperty in Group Policy registry - powershell

I need to locate a specific GPO to manually delete it from our machines, due to the pandemic they are at home and outside domain, so I thought about doing it remotely via PS with Intune.
I'm trying to create an script that looks for the DisplayName of the GPO and the deletes it, but it seems like the properties are protected or some other issue, because i cannot find any Property beyond the Group Policy registry.
If i try to do:
Get-ChildItem -Path 'Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Group Policy\History\'
I get something like:
Name Property
---- --------
{3537}
{42B5}
{4CFB}
It does not matter how deep I go beyond that point, it does not show me any Property. I just started with PS and I don't know if there's anything I'm doing wrong, with others registries i got no issue.
¿Any thoughts? :(
At the end I want to have something like:
$path = "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Group Policy\History\"
$match = "GPO_1234"
Get-ChildItem -Path $path -recurse |
ForEach { Get-ItemProperty $_.PSPath } |
Where-Object { $_.DisplayName -match $match } | del
But if it cannot match with anything if the Properties cannot be iterated.
Thanks in advance

It's a bit hard without knowing what's in "Group Policy\History" exactly (I have a single DWORD there and that's it, no subkeys at all), but assuming "DisplayName" is the name of a property somewhere and "GPO_1234" is the value of that property, then something like this should work:
$RegPath = "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Group Policy\History\"
$Pattern = "GPO_1234"
Get-ChildItem -Recurse $RegPath |
ForEach-Object { Get-ItemProperty $_.PsPath } |
Where-Object {
$_.psobject.Properties.Name -eq 'DisplayName' -and
$_.psobject.Properties.Value -eq $Pattern
}
If you get the matches you want just throw a final | Remove-Item -Force at the end, and if you have any questions about what's going on just ask!
As a sidenote, you should avoid using aliases like Foreach and del and instead use the real nammes (ie. Foreach-Object and Remove-Item). It will make your scripts easier to read and follow in the long run.
Especially important with Foreach since it exists with that exact spelling but a completely different syntax as well (foreach ($Item in $Collection) {}).

Related

Is there a way to display the latest file of multiple paths with information in a table format?

I check every day, whether a CSV-File has been exported to a specific folder (path). At the moment there are 14 different paths with 14 different files to check. The files are being stored in the folder and are not deleted. So i have to differ between a lot of files with "lastwritetime". I would like a code to display the results in table format. I would be happy with something like this:
Name LastWriteTime Length
ExportCSV1 21.09.2022 00:50 185
ExportCSV2 21.09.2022 00:51 155
My code looks like this:
$Paths = #('Path1', 'Path2', 'Path3', 'Path4', 'Path5', 'Path6', 'Path7', 'Path8', 'Path9', 'Path10', 'Path11', 'Path12', 'Path13', 'Path13')
foreach ($Path in $Paths){
Get-ChildItem $path | Where-Object {$_.LastWriteTime}|
select -last 1
Write-host $Path
}
pause
This way i want to make sure, that the files are being sent each day.
I get the results that i want, but it is not easy to look at the results individually.
I am new to powershell and would very much appreciate your help. Thank you in advance.
Continuing from my comments, here is how you could do this:
$Paths = #('Path1', 'Path2', 'Path3', 'Path4', 'Path5', 'Path6', 'Path7', 'Path8', 'Path9', 'Path10', 'Path11', 'Path12', 'Path13', 'Path13')
$Paths | ForEach-Object {
Get-ChildItem $_ | Where-Object {$_.LastWriteTime} | Select-Object -Last 1
} | Format-Table -Property Name, LastWriteTime, Length
If you want to keep using foreach() instead, you have to wrap it in a scriptblock {…} to be able to chain everything to Format-Table:
. {
foreach ($Path in $Paths){
Get-ChildItem $path | Where-Object {$_.LastWriteTime} | Select-Object -Last 1
}
} | Format-Table -Property Name, LastWriteTime, Length
Here the . operator is used to run the scriptblock immediately, without creating a new scope. If you want to create a new scope (e. g. to define temporary variables that exist only within the scriptblock), you could use the call operator & instead.

Powershell, registry and wildcards, oh my

Given...
HKLM\Software\
KeyName
Property_1
Property_2
Property_[0-1]
Key*Name
Property_1
Property_2
Property_[0-1]
Key#Name
Property_1
Property_2
Property_[0-1]
I can use
Get-Item -path:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name"
which will return KeyName, Key*Name and Key#Name, while
Get-Item -literalPath:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name"
will return just Key*Name. So far, so good. I can use -path or -literalPath as needed to either search for a key with wildcards or not. But properties pose a problem.
Get-ItemProperty -path:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\KeyName" -name:"Prop_[0-9]"
works as expected and returns Prop_1 & Prop_2 from the KeyName key. And
Get-ItemProperty -literalPath:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\KeyName" -name:"Prop_[0-9]"
works as expected and returns just Prop_[0-9] from the same key. But it all fails apart when you need to use a wildcard to find properties, in a path that includes a wildcard character as a literal in the key path. So...
Get-ItemProperty -path:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name" -name:"Prop_[0-9]"
returns Prop_1 & Prop_2 from all three keys. Not the desired behavior at all.
I had hoped to be able to filter on PSPath using -`literalPath' but this
Get-ItemProperty -literalPath:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name" -name:"Prop_[0-9]" | where {$_.PSPath -match [RegEx]::Escape("Key*Name")}
does not return the correct properties. It seems that a -literalPath means a literal name also. So I tried filtering on PSPath and Name like so
Get-ItemProperty -literalPath:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name" -name:"Prop_[0-9]" | where {(($_.PSPath -match [RegEx]::Escape("Key*Name")) -and ($_.Name -match "Prop_[0-9]"))}
But that doesn't work because once you actually get real properties, they are no longer a .NET type, they have been shat into a PSCustomObject.
And that is starting to get so complicated I wonder if there is a better way to proceed. I should note that the ultimate goal here is to get both a literal path and a list of literal property names, so that I can move, copy or delete the properties. So, given a path of Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name and a name of Prop_[0-9] I will eventually want to, for example, delete
HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name\Prop_1
&
HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name\Prop_2
but not
HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name\Prop_[0-9]
EDIT: Based on the answer from #Tomalak I have simplified a bit, to simply get back a list of property names. That looks like this
$keyPath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name"
$propExpr = "Prop_[0-9]"
((Get-Item -literalPath:$keyPath | Get-ItemProperty).PSObject.Properties | Where-Object Name -Match $propExpr | ForEach-Object {$_.Name})
This will get a registry key by literal path and filter its properties by regex match
$keyPath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name"
$propExpr = "Prop_[0-9]"
Get-Item -literalPath $keyPath -PipelineVariable key | Get-ItemProperty | ForEach-Object {
$_.PSObject.Properties | Where-Object Name -Match $propExpr | ForEach-Object {
[pscustomobject]#{
key = $key.Name
prop = $_.Name
value = $_.Value
}
}
}
Instead of the $key.Name you can of course return the actual $key if that's more convenient for your task.

Powershell Get-ChildItem, filtered on date with Owner and export to txt or csv

I am trying to export a list of documents modified files after a set date, including its owners from a recursive scan using Get-ChildItem.
For some reason I cannot get it to port out to a file/csv:
$Location2 = "\\fs01\DATAIT"
$loc2 ="melb"
cd $Location2
Get-ChildItem -Recurse | Where-Object { $_.lastwritetime -gt [datetime]"2017/05/01" } | foreach { Write-Host $_.Name "," $_.lastwritetime "," ((get-ACL).owner) } > c:\output\filelisting-$loc2.txt
Could any of the PowerShell gurus on here shed some light please?
The problem with your code is that you are using Write-Host which explicitly sends output to the console (which you then can't redirect elsewhere). The quick fix is as follows:
Get-ChildItem -Recurse | Where-Object { $_.lastwritetime -gt [datetime]"2017/05/01" } | foreach { "$($_.Name),$($_.lastwritetime),$((get-ACL).owner)" } > filelisting-$loc2.txt
This outputs a string to the standard output (the equivalent of using Write-Output). I've made it a single string which includes the variables that you wanted to access by using the subexpression operator $() within a double quoted string. This operator is necessary to access the properties of objects or execute other cmdlets/complex code (basically anything more than a simple $variable) within such a string.
You could improve the code further by creating an object result, which would then allow you to leverage other cmdlets in the pipeline like Export-CSV. I suggest this:
Get-ChildItem -Recurse | Where-Object { $_.lastwritetime -gt [datetime]"2017/05/01" } | ForEach-Object {
$Properties = [Ordered]#{
Name = $_.Name
LastWriteTime = $_.LastWriteTime
Owner = (Get-ACL).Owner
}
New-Object -TypeName PSObject -Property $Properties
} | Export-CSV $Loc2.csv
This creates a hashtable #{} of the properties you wanted and then uses that hashtable to create a PowerShell Object with New-Object. This Object is then returned to standard output, which goes into the pipeline so when the ForEach-Object loop concludes all the objects are sent in to Export-CSV which then outputs them correctly as a CSV (as it takes object input).
As an aside, here is an interesting read from the creator of PowerShell on why Write-Host is considered harmful.
[Ordered] requires PowerShell 3 or above. If you're using PowerShell 2, remove it. It just keeps the order of the properties within the object in the order they were defined.

can't seem to match two values in registry

I am trying to get all the NICs on my system and then using that information to insert registry values of *TCPChecksumOffloadIPv4 etc. However, I am failing this task miserably!
I can get all the GUID's and want to match that to what is in this registry path: HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\*
I get all the GUID's by this:
$GuidSet = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\*" | select -ExpandProperty pschildname
Output:
{1FE01120-3866-437F-81FF-556B08999AA4}
{2533855F-2A59-485D-87A0-167E5DA39E45}
{2A6471FB-C1D6-47D2-A665-9F276D142D7C}
{306D2DED-18B5-45D8-858E-BB3F49E3BD6A}
{30EF50B2-E4B3-400D-9614-B590E37DE4D8}
{4A208C06-0D99-4DE4-9B2F-86285AEF864E}
{B7883140-E15B-4409-BA1B-96E37A45425C}
{D129DDA8-C64B-46A1-B99A-EA74FC4FAF81}
{D5C9183B-E542-4010-866F-4443AD55F28C}
This is where I am stuck now...how can I use this information to match what is in the registry path of "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\*" ?
I tried the below but I get access denied - I think this is because of the "Properties" registry key - how can I ignore that registry key?
$path1 = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\*" |?{$_.NetCfgInstanceId -match $guidset} | select -ExpandProperty pspath
Once that is done though then do I construct a foreach loop on each entry and then add in the registry keys I need?
ANSWER:
you know what...when your in a muddle and you have lots of scripts...take a break, open a new window and start from scrath! That's what I did and in 10min I figured it out...!
$aGUID_SET = #(Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\*" | select -ExpandProperty pschildname)
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\*" -exclude "Properties" |
Where-Object {$aGUID_SET.Contains($_.NetCfgInstanceId)} |
ForEach-Object {
""
$_.DriverDesc
$_.NetCfgInstanceId
}
You are on the right track.
The Get-ItemProperty cmdlet will only get the properties of the items specified, not including any sub-items.
Since the registry values you are looking for are not actually properties of the registry key HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318} but instead are properties of subkeys of that key, the first thing we need to do is list the subkeys: $path = "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}"
We can then use Get-ChildItem $path to list the subkeys.
After formatting the paths properly (add Registry:: to the front), you can then input that to Get-ItemProperty. I would filter with something like: Where-Object {$guidset -contains $_.NetcfgInstanceID} | Select-Object -ExpandProperty PSPath.
Finally, you should have an array of paths to keys that matched $guidset, which
Set-ItemProperty can take.
EDIT: The error you are receiving is because permissions on those "Properties" subkeys is restricted. I would tack an -ErrorAction SilentlyContinue to Get-ChildItem because it is not a terminating error and does not actually affect the results.
You can do it like this
ForEach ($item in $(Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\*" |?{$_.NetCfgInstanceId -match $guidset} | select -ExpandProperty pspath)) {
Try {
Write-Host $item
} Catch {
Write-Host "error..."
}
}

Why does Test-Path not work on HKEY_LOCAL_MACHINE, but only HKLM:

I have found a behaviour in Powershell that looks very inconsistent and confusing to me, it is mainly about different notations when accessing registry keys, and what kind of syntax is expected (as an argument) or delivered (as a return value) by various commandlets, especially loke this:
HKEY_LOCAL_MACHINE...
vs.
HKLM:\
Let me show you an example:
$baseDir = "HKLM:\System\CurrentControlSet\Enum\SCSI"
$Results = Get-ChildItem $baseDir -Recurse -ErrorAction SilentlyContinue
foreach ($item in $Results)
{
$Subkey = $item.Name
$keyExists = Test-Path "$Subkey" -PathType Container -ErrorAction SilentlyContinue
if ($keyExists -eq $False)
{
New-Item $Subkey
}
}
So what happens is the following:
$Subkey = $item.Name
returns HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\SCSI\SomePath
and
$keyExists = Test-Path "$Subkey" -PathType Container -ErrorAction SilentlyContinue
fails to work with that syntax, i.e. returns "$false" even though the path exists.
I have, as a workaround, entered the followng line of code between these two lines, which fixes the problem:
$Subkey = $Subkey -replace "HKEY_LOCAL_MACHINE", "HKLM:"
That works - it alters the string to:
HKLM:\System\CurrentControlSet\Enum\SCSI\SomePath
so Test-Path can work with that syntax, but it is not very elegant.
What am I actually missing? Why does powershell not return the Result-names from Get-ChildItem in a way that is suitable for further processing in powershell? Why not always use the same syntax style?
To me, this is a design flaw in Powershell, or is there any other way to fix that?
(Note: this is only a stripped-down example to show the basic problem, I know that it doesn't make sense to search for child items and check it's existence...)
HKLM: is a valid PSDrive while HKEY_LOCAL_MACHINE is not.
PS C:\> Get-PSProvider Registry | select -Expand Drives
Name Used (GB) Free (GB) Provider Root CurrentLocation
---- --------- --------- -------- ---- ---------------
HKLM Registry HKEY_LOCAL_MACHINE
HKCU Registry HKEY_CURRENT_USER
Use Test-Path on the items' PSPath property instead of their Name property.