Error retrieving registry value with Powershell - powershell

I'm attempting to read a value from a registry entry with Powershell. This is fairly simple, however, one particular registry key is giving me trouble.
If I run the following, I can't get the value of the (default) of "$setting".
C:\Program Files\PowerGUI> $setting = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping\Autorun.inf"
C:\Program Files\PowerGUI> $setting
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\
CurrentVersion\IniFileMapping\Autorun.inf
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\
CurrentVersion\IniFileMapping
PSChildName : Autorun.inf
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
(default) : #SYS:DoesNotExist
Normally, I would do $setting.Attribute, or $setting.(default). However, that results in the following error:
C:\Program Files\PowerGUI> $setting.(default)
The term 'default' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
At :line:1 char:17
+ $setting.(default <<<< )
How do I get the value of the "(default)" attribute?
Thanks in advance.

EDIT Had to look through and old script to figure this out.
The trick is that you need to look inside the underlying PSObject to get the values. In particular look at the properties bag
$a = get-itemproperty -path "HKLM:\Some\Path"
$default = $a.psobject.Properties | ?{ $_.Name -eq "(default)" }
You can also just use an indexer instead of doing the filter trick
$default = $a.psobject.Properties["(default)"].Value;

Another version:
(Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping\Autorun.inf').'(default)'

Use Get-Item to get an object representing the registry key:
PS > $regKey = Get-Item HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping\Autorun.inf
This gives you an instance of RegistryKey. RegistryKey has a method named GetValue; if the argument to GetValue is the empty string (''), then it will return the (default) value:
PS > $regKey.GetValue('')
Why is this better than Get-ItemProperty? It extends more naturally to Get-ChildItem. Get-ChildItem will give you a list of RegistryKey objects. In my particular case, I wanted to list the install paths of the versions of Python installed on my machine:
PS C:\> get-childitem HKLM:\SOFTWARE\Wow6432Node\Python\PythonCore\*\InstallPath `
>> | foreach-object { $_.GetValue('') }
C:\Python26\ArcGIS10.0\
C:\Python\27\

Related

How to change value of property in powershell

How do you access the value of a property in powershell and change its value
PS C:\Windows\system32> $tamp = get-item "HKLM:\SOFTWARE\Microsoft\AppServiceProtocols\ms-phone-api"
PS C:\Windows\system32> $tamp
Hive: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AppServiceProtocols
Name Property
---- --------
ms-phone-api AppServiceName : com.microsoft.phone.api
PackageFamilyName : Microsoft.YourPhone_8wekyb3d8bbwe
PS C:\Windows\system32>
now that we have an item named ms-phone-api i would like to access the properties and change the values for them the registry path used here is just for the sake of explanation, any registry key can be used
I tried to access the values by using the following
$tamp.<property-name> = <value>
but this does not work and outputs this
PS C:\Windows\system32> $tamp.AppServiceName = com.google
com.google : The term 'com.google' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
At line:1 char:25
+ $tamp.AppServiceName = com.google
+ ~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (com.google:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
PS C:\Windows\system32>
Using New-ItemProperty is the easiest way:
# get the RegPath
$RegPath = "HKLM:\SOFTWARE\Microsoft\AppServiceProtocols\ms-phone-api"
# set the Name we want to update
$Name = "ms-phone-api"
# set Value name we want to update
$Value = "AppServiceName"
New-ItemProperty -Path $RegPath -Name $Name -Value $Value -PropertyType String
You can read more details here: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-itemproperty?view=powershell-7.2
You can do
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\AppServiceProtocols\ms-phone-api' -Name 'AppServiceName' -Value 'com.google'
(you can also append -Type String)
The Set-ItemProperty cmdlet creates or changes the value of a property of an item.

Filter PowerShell command Output

I want to change a mapped drive remote path. but I'm unable to filter the remote path property. Is there any way I can filter all the mapped drives remote path only so, later on, I can run a foreach loop to change the values? Thanks.
Get-Item -Path HKCU:\Network | Where-Object -FilterScript {'RemotePath'}
Hive: HKEY_CURRENT_USER\Network
Name Property
---- --------
Z RemotePath : \\IAPC\Users\IA\Documents\10
UserName :
ProviderName : Microsoft Windows Network
ProviderType : 131072
ConnectionType : 1
ConnectFlags : 0
DeferFlags : 4
UseOptions : {68, 101, 102, 67...}
PS HKCU:\Network>
Only interested in name of the network drive with RemotePath (under Property column)
The following outputs [pscustomobject] instances representing the values of those registry subkeys of HKCU:\Network whose RemotePath value is non-empty:
Get-ItemProperty -Path HKCU:\Network\* | Where-Object RemotePath
To get just the drive-name-remote-path pairs:
Get-ItemProperty -Path HKCU:\Network\* | Where-Object RemotePath |
Select-Object PSChildName, RemotePath
Note: The PSChildName property contains the drive letter of each mapping (it is the name of the subkey whose values are being returned).
To loop over all mappings of interest and update them by replacing the server-name component:
$oldServer = '\\IAPC\'
$newServer = '\\localhost\'
# CAVEAT: This instantly updates your drive mappings.
# You can add -WhatIf to the Set-ItemProperty call,
# to *preview* the operation, but it will only show the
# target registry key and value, not the new data.
Get-ItemProperty -Path HKCU:\Network\* | Where-Object RemotePath | ForEach-Object {
$driveLetter, $remotePath = $_.PSChildName, $_.RemotePath
Set-ItemProperty -LiteralPath HKCU:\Network\$driveLetter RemotePath ($remotePath -replace [regex]::Escape($oldServer), $newServer)
}
As for what you tried:
Where-Object -FilterScript {'RemotePath'} is a no-op, because any input meets the criterion 'RemotePath', which, as a non-empty string literal is invariably $true when interpreted as a Boolean. To access a property on the current input object, you need to use automatic $_ variable: { $_.RemotePath }
It is only with the simplified syntax, shown above, which doesn't use a script block ({ ... }) that the string argument given is implicitly interpreted as the name of the property to access on the input object at hand.
Get-Item -Path HKCU:\Network only targets the root key of all network mappings itself, not the subkeys that define the actual mappings.
In your case you're also interested in the RemotePath value of each subkey, which Get-ItemProperty provides for all subkeys, targeted with a wildcard pattern, HKCU:\Network\*
Get-ItemProperty, when not given a property name, returns all properties - which in the case at hand are registry values - as a "property bag", in the form of a [pscustomobject] instance, with the name of the containing key reported in the .PSChildName property.
Unfortunately, working with PowerShell's registry provider is often not as straightforward as one would like.
#imtiaz Hey i am not sure if you are trying the same but here is how i have achieved recently.
$oldServer = "\\abc.local"
$newServer = "\abc.com"
$paths = REG QUERY HKCU\Network | where{$_ -ne ""}
foreach ($item in $paths)
{
$oldPath = REG QUERY $item /f RemotePath /t REG_SZ | Out-String
$oldPath1 = $oldPath.Split()[-12]
$updatedPath = $oldPath1 -replace $oldServer,$newServer
reg add $item /v RemotePath /t REG_SZ /d $updatedPath /f /reg:64
}
I tried before Get-ItemProperty and set-itemproperty was facing some issues. This might help you. I know this's not a "professional way" but this worked for me.

Invoke-Command on remote session returns local values

Question
Should the script block of Invoke-Command, when run with a PSSession, always run on the remote computer?
Context
I ran the below powershell against a list of servers:
Clear-Host
$cred = get-credential 'myDomain\myUsername'
$psSessions = New-PSSession -ComputerName #(1..10 | %{'myServer{0:00}' -f $_}) -Credential $cred
Invoke-Command -Session $psSessions -ScriptBlock {
Get-Item -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters'
} | Sort-Object PSComputerName
# $psSessions | Remove-PSSession
This returned:
Hive: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos
Name Property PSComputerName
---- -------- --------------
Parameters MaxPacketSize : 1 myServer01
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer02
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer03
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer04
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer05
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer06
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer07
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer08
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer09
MaxTokenSize : 65535
Parameters MaxPacketSize : 1 myServer10
MaxTokenSize : 65535
All looks good; onlyl I'd not expected to see these values / I was running this as a quick sense check before setting the values on these servers to ensure I didn't overwrite anything.
I had a quick look at one of the servers using regedit; and found that MaxTokenSize and MaxPacketSize did not exist.
I then amended the command to use Get-ItemProperty instead of Get-Item:
Invoke-Command -Session $psSessions -ScriptBlock {
Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters' -Name 'MaxTokenSize'
} | Sort-Object PSComputerName
This time I got 10 errors:
Property MaxTokenSize does not exist at path HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters.
+ CategoryInfo : InvalidArgument: (MaxTokenSize:String) [Get-ItemProperty], PSArgumentException
+ FullyQualifiedErrorId : System.Management.Automation.PSArgumentException,Microsoft.PowerShell.Commands.GetItemPropertyCommand
+ PSComputerName : myServer01
# ... (and the same for the other 9 servers, with only PSComputerName changing)
Regarding where the values that were returned came from... they're from my local machine. Amending my local registry entries and rerunning the original command showed all "servers" as having the new value.
I'm guessing this is a bug; but because I've not played with PSSessions much so far wanted to check here in case it's an issue with my understanding / usage of these commands, or if there are known gotchas to watch out for when using PSSessions.
tl;dr:
The root cause is a bug in the formatting instructions for registry keys (as of Windows PowerShell 5.1.18362.125 and PowerShell Core 7.0.0-preview.2) leading to the unexpected mix of remote and local information - see GitHub issue #10341.
As an aside: Similarly, PowerShell's ETS (extended type system) can introduce problems too, as shown in Mathias' answer here.
The best workaround is to simply use Get-ItemProperty (without a -Name argument) instead of Get-Item.
Mathias R. Jessen has provided the crucial pointer in a comment on the question, and js2010's answer provides a limited workaround and a pointer to the root cause, but it's worth providing more background information:
PowerShell comes with formatting instructions for type Microsoft.Win32.RegistryKey, as output by Get-Item with a registry path.
These formatting instructions define a calculated column named Property for the default (tabular) view, which helpfully shows a summary of the output registry key's values, which involves accessing the registry again, using Get-ItemProperty as shown in js2010's answer.
However, that behind-the-scenes Get-ItemProperty call always accesses the local registry - even when the keys were retrieved from a different machine, via PowerShell remoting, so you'll end up with a spurious mix of remote and local information.
Note that, technically, when Get-Item is run remotely, what you receive locally is an approximation of the original Microsoft.Win32.RegistryKey object, due to the serialization and deserialization involved in remoting. This approximation is a custom object with static copies of the original object's property values, and its (simulated) type name is Deserialized.Microsoft.Win32.RegistryKey - note the prefix.
PowerShell applies formatting instructions based on the full type name of output objects, but in the absence of a specific instructions or a given Deserialized.<originalTypeName> type, PowerShell applies the instructions for <originalTypeName>, which is what causes the problems here.
A - cumbersome, but edition-agnostic[1] - way to see the problematic formatting instruction is to run the following command:
(Get-FormatData Microsoft.Win32.RegistryKey -PowerShellVersion $PSVersionTable.PSVersion).FormatViewDefinition.Control | % {
$colNames = $_.Headers.Label
$colValues = $_.Rows.Columns.DisplayEntry.Value
foreach ($i in 0..($colNames.Count-1)) {
[pscustomobject] #{
ColumnName = $colNames[$i]
ColumnValue = $colValues[$i]
}
}
} | Format-Table -Wrap
This yields the column names and definitions for the table view:
ColumnName ColumnValue
---------- -----------
Name PSChildName
Property
$result = (Get-ItemProperty -LiteralPath $_.PSPath |
Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider |
Format-List | Out-String | Sort).Trim()
$result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
if($result.Length -eq 5000) { $result += "..." }
$result
The workaround suggested in js2010's answer - piping to Format-Table * or Format-List * is effective in the sense that it prevents the inapplicable local information from being displayed: by specifying properties explicitly (even by wildcard pattern *), only those properties are displayed on output - not also the flawed calculated column.
However, while the true Property property of the output objects provides access to the value names in the registry key at hand, it doesn't provide the actual data, the way that the calculated Property column does.
By contrast, using Get-ItemProperty without a -Name argument in lieu of Get-Item as a workaround returns both value names and data (correctly even when remoting) and even does so without restrictions (whereas Get-Item limits output to 5000 chars.)
The output format will be slightly different, but all the information is there.
[1] That is, the command works also in PowerShell Core, where the built-in formatting instructions are no longer maintained as external *.format.ps1xl files and are instead compiled into the executable.
Pipe it to fl * or ft * so it doesn't use the format file to display the registry keys. The format file runs get-itemproperty locally to try to display the properties.
From the bottom of $PSHOME\Registry.format.ps1xml for type Microsoft.Win32.RegistryKey:
<ScriptBlock>
$result = (Get-ItemProperty -LiteralPath $_.PSPath |
Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider |
Format-List | Out-String | Sort).Trim()
$result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
if($result.Length -eq 5000) { $result += "..." }
$result
</ScriptBlock>

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.

finding registry parent folder to add registry

I am stuck at how to actually find the parent folder to add a new registry key using powershell. The reason is because the device shows as a different value per computer. And the registry that I am trying to add will set the default over to the "internal mic"'s parents key.
Here is the code
$pathToIntMic = Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Capture | Where-Object { .Name -eq "internal mic"}
#$intMicParent = (Get-item $pathToIntMic).parent.Fullname
#write-host $intMicParent
The last two line are commented out but serve just to get the parent folder key name just under \Audio\Capture.
Thanks for any help!
Use the property PSParentPath:
$key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Capture'
$pathToIntMic = Get-ItemProperty "HKLM:\$key" | ? { $_.Name -eq "internal mic" }
Write-Host $pathToIntMic.PSParentPath
You can enumerate the properties and methods of an object by piping it into the Get-Member cmdlet:
$pathToIntMic | Get-Member