How to retrieve original, unresolved (unexpanded) registry value? - powershell

In the System Properties > Environment Variables > User variables > PATH contains:
%USERPROFILE%\Downloads\SysinternalsSuite;%USERPROFILE%\bin
The value can be retrieved with:
PS C:\src\t> (Get-ItemProperty -Path HKCU:\Environment).PATH
C:\Users\lit\Downloads\SysinternalsSuite;C:\Users\lit\bin
Is there any way to get the original value without variable expansion? It almost seems like Get-ItemProperty needs a -Raw switch.

PetSerAl, as many times before, has provided an effective solution in a terse comment on the question.
Indeed, PowerShell's Get-ItemProperty / Get-ItemPropertyValue cmdlets currently (PowerShell 7.3.0) lack the ability to retrieve a REG_EXPAND_SZ registry value's raw value, meaning the value as stored in the registry before the embedded environment-variable references (e.g., %USERPROFILE%) are expanded (interpolated).
Direct use of the .NET API is therefore needed:
(Get-Item -Path HKCU:\Environment).GetValue(
'PATH', # the registry-value name
$null, # the default value to return if no such value exists.
'DoNotExpandEnvironmentNames' # the option that suppresses expansion
)
See [Microsoft.Win32.RegistryKey].GetValue().
Note: 'DoNotExpandEnvironmentNames' is automatically converted to [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentVariables by PowerShell; you may use the latter as well.

Related

How two concatenate environment variables names in powershell

Say I have the following environment variables:
a = Poke
b = mon
Pokemon= Feraligatr
I want to be able to concatenate a and b environment variables to get the variable name Pokemon and the get Pokemon value like $($env:ab) or $($env:$($env:a)$($env:b)) (This examples does not work)
Building on the helpful comments:
You're looking for indirection, i.e. the ability to refer to an environment variable indirectly, via another (environment) variable(s) storing the target variable's name.
PowerShell-idiomatic solution:
Use the Env: drive in combination with the Get-Content cmdlet:
# The target environment variable.
$env:Pokemon='bingo!'
# The variables that, in combination, return the *name*
# of the target environment variable.
$env:a = 'Poke'
$env:b = 'mon'
# Use Get-Content and the env: drive to retrieve
# an environment variable by an *indirectly* specified name.
# Note:
# * env:$env:a$env:b is treated like "env:$env:a$env:b",
# i.e. an expandable (interpolating string).
# * For better visual delineation of the variables, use:
# env:${env:a}${env:b}
# * `-ErrorAction Ignore` ignores the case when the target var.
# doesn't exist (quietly returns $null`)
# -> 'bingo!'
Get-Content -ErrorAction Ignore env:$env:a$env:b
# Alternative, with explicit string concatenation.
Get-Content -ErrorAction Ignore ('env:' + $env:a + $env:b)
Note:
To set environment variables indirectly, use the Set-Content cmdlet; e.g.:
$varName = 'FOO'
Set-Content env:$varName BAR # $env:FOO now contains 'BAR'
Applying the same technique to regular shell variables (non-environment variables), requires either use of the variable: drive, or, for more flexibility, the Get-Variable and Set-Variable cmdlets - see this answer.
More more information about expandable (interpolating) string literals such as "env:$env:a$env:b", see the conceptual about_Quoting help topic.
.NET API alternative:
As Max points out, you can also use the static System.Environment.GetEnvironmentVariable .NET method:
[Environment]::GetEnvironmentVariable("${env:a}${env:b}")
For more information about calling .NET API methods from PowerShell, see the conceptual about_Methods help topic.

How to check the existence of registry key in HKEY_CURRENT_CONFIG

How to check the presence of registry key named "Mon12345678" under HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control\VIDEO\ {CD73268F-4662-42EC-80F6-182E03DE7017}\0000 regsitry hive? We can validate till HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control\VIDEO directly and then run random check under "Video" until we get the sub registry key called "Mon12345678"?
I tried the below code snippet
Test-Path -Path "HKCC:\System\CurrentControlSet\Control\VIDEO\ *\Mon12345678" -ErrorAction SilentlyContinue
But the result shows false even though the sub registry hive is found. How to tackle the problem?
The HKEY_CURRENT_CONFIG registry hive is not predefined as a PowerShell drive name HKCC:, so unless you defined such a drive yourself with New-PSDrive, it won't exist.
(Get-PSDrive -psProvider Registry).Name shows you which registry-based drives are defined; by default, it is only HKLM: (HKEY_LOCAL_MACHINE) and HKCU: (HKEY_CURRENT_CONFIG).
Short of defining your own HKCC: drive, you can prefix a registry hive name with registry:: (the provider name) in order to target a hive; e.g.:
# Test the existence of a key known to exist in the HKEY_CURRENT_CONFIG registry hive,
# using provider prefix 'registry::'
PS> Test-Path -Path 'registry::HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control'
True
Therefore, to test the existence of a subkey named Mon12345678 across all subkeys (*) of key HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control\VIDEO, use the following:
$keyPath = 'registry::HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control\VIDEO'
Test-Path -Path "$keyPath\*\Mon12345678"
If, by contrast, you want to test the existence of a value (a property of a registry key) named Mon12345678, you cannot use a single Test-Path call, because Test-Path can only operate on key paths, not paths ending in value names.
See this answer for background information.
$keyPath = 'registry::HKEY_CURRENT_CONFIG\System\CurrentControlSet\Control\VIDEO'
[bool] (Get-ItemProperty -EA ignore "$keyPath\*" 'Mon12345678')
Did you try?
To search part of the registry, use the following syntax:
REG Query HKxx\subkey [/D|/K|/V] /F "search_pattern" /S [/E] [/C]
To search an entire registry hive, just omit the subkey:
REG Query HKxx [/D|/K|/V] /F "search_pattern" /S [/E] [/C]
Source of the solution and full post can be found here

Differentiate Registry Key from Value paths

Is there an equivalent of [System.IO.DirectoryInfo] and [System.IO.FileInfo] for differentiating Registry Keys from Values? I want to evaluate a path and log for the user what the final target of the path is.
So far, this is what I have, and it's kinda ugly.
$path = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\SomeJunkHereToTestFailure'
try {
if ((Get-Item -path:$path -errorAction:stop).GetType().name -eq 'RegistryKey') {
'Registry Key'
}
} catch {
try {
if ((Get-ItemProperty -path:(Split-Path $path -parent) -name:(Split-Path $path -leaf) -errorAction:stop).GetType().name -eq 'PSCustomObject') {
'Registry Value'
}
} catch {
'What is this?'
}
}
Hoping for something more elegant and also consistently correct.
Ok, so after digging around for awhile, canonically speaking, this:
HKEY_LOCAL_MACHINE\SOFTWARE\Path\To\Something
Is always a path to a key.
Look at reg.exe query. Look at how Get-ItemProperty works. Notice in regedit.exe that you can copy key names, but not a "path" to a specific value. Look at how .reg files are written. Look at Registry.GetValue() and Registry.SetValue(). Or Registry.LocalMachine.OpenSubKey().GetValue(). Microsoft clearly thinks that registry paths always point to keys.
If you have software that's using HKEY_LOCAL_MACHINE\SOFTWARE\Path\To\A\Value to refer to a value in the registry, then they're not using standard registry paths that Windows understands.
That's why you run these the way you do:
reg.exe query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v "ProductName"
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name "ProductName"
[Microsoft.Win32.Registry]::LocalMachine.OpenSubKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion').GetValue('ProductName')
[Microsoft.Win32.Registry]::GetValue('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion','ProductName', $false)
That last one is nice because it doesn't error. If it can't find the value, it returns the third argument. It does, however, require the long hive name at the start as far as I can tell.
So, Test-Path -Container can tell you if it's a key. Microsoft.Win32.Registry.GetValue() can tell you if it's a value... with some manipulation.
Original answer (THIS DOES NOT WORK):
Use Test-Path:
$path = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\SomeJunkHereToTestFailure'
# Is it a Key?
Test-Path -Path $path -PathType Container
# Is it a Value?
Test-Path -Path $path -PathType Leaf
As for...
Is there an equivalent of [System.IO.DirectoryInfo] and [System.IO.FileInfo]
Not that I've ever seen or heard of to date.
However, take a look at the below to see if they can fit your need or you may be ableto tweak them to accomplish your goals.
Powershell Registry Cmdlets utilizing the .Net StdRegProv
This Script is a collection of PowerShell cmdlets that can be run in either x86 or x64 processes to handle common registry operations utilizing alternate registry views. This script is compatible with PowerShell versions 2.0 or later.
Download RegistryCmdlets.ps1
https://gallery.technet.microsoft.com/Powershell-Registry-19689888
Or of course is back to writing your own as you are posting here:
See also as you look at your effor to cultivate your own:
Registry Cmdlets: First Steps with CDXML
In this post, I’ll show you how to get started with CDXML. Before that, though, I’d better explain CDXML...
Cmdlet definition XML (CDXML) is a way to create a Windows PowerShell module from a WMI class by using the cmdlets-over-objects technology that was introduced in Windows PowerShell 3.0. As usual, it’s always easier to show you an example:
https://blogs.technet.microsoft.com/heyscriptingguy/2015/02/03/registry-cmdlets-first-steps-with-cdxml/
Registry Cmdlets: Working with the Registry
The bad news is that there aren’t any cmdlets for working with the registry. There’s a Registry provider, which means you can use the Item and ItemProperty cmdlets to manage the local registry—but there aren’t any specific registry cmdlets associated with the provider.
The good news is that we can adopt the approach that many teams at Microsoft have taken and create our own by using cmdlet definition XML (CDXML). A Common Information Model (CIM) class is wrapped in some fairly simple XML and published as a Windows PowerShell module. If you look in the Modules folder on a computer running Windows 8.1, Windows 8, Windows Server 2012 R2, or Windows Server 2012, you will find many files with a CDXML extension:
https://blogs.technet.microsoft.com/heyscriptingguy/2015/02/02/registry-cmdlets-working-with-the-registry
There's a problem with the premise of your question: Ending a registry path - composed of key names - with a value name is not supported - at least neither with the .NET Framework registry types nor with reg.exe nor with PowerShell's registry drive provider.
For instance, to refer to value WindowSize of registry key
HKEY_CURRENT_USER\Control Panel\Colors, you CANNOT use the following path:
# NOT a valid path, because 'WindowSize' is a *value*, not a *key*.
HKEY_CURRENT_USER\Control Panel\Colors\WindowSize
Even if you yourself decide to support such paths in your functions (and there may be utilities that similarly accept that), note that they are ambiguous, because a given registry key can have both a value and a subkey with a given name.
With that limitation in mind, your own code from your question is probably the best you can do (though it can be streamlined a little).
Is there an equivalent of [System.IO.DirectoryInfo] and [System.IO.FileInfo] for differentiating Registry Keys from Values?
The equivalent of [System.IO.DirectoryInfo] (filesystem directory) is [Microsoft.Win32.RegistryKey] (registry key).
By contrast, [System.IO.FileInfo] (filesystem file) has NO counterpart for registry values:
Registry values are represented as properties of registry items (keys) in the PS registry drive provider rather than items in their own right, and such properties are (awkwardly) represented as non-specific [System.Management.Automation.PSCustomObject] instances.
Similarly, registry values have no type representation in the .NET Framework (you only request their data by name).
Fundamentally, in terms of object models, registry values do not correspond to filesystem files; read on for more.
Optional reading: how the registry object model maps onto PowerShell drive-provider concepts in contrast with the filesystem object model
It comes down to this: in the filesystem, a directory's files are child items, whereas in the registry, a key's values are properties of that key.
Only container-type children are represented as child items in both providers (sub-directories in the case of the filesystem provider, sub-keys in case of the registry provider).
In the context of the FileSystem provider, files and directories are both (sub-types of) items.
Files are represented as [System.IO.FileInfo] instances, and directories as [System.IO.DirectoryInfo].
A leaf item is invariably of subtype file, whereas a container (interior) item is invariably of subtype directory (folder).
Therefore, you can use Test-Path -PathType Leaf to test for a file and Test-Path -PathType Container to test for a directory.
In the context of the Registry provider, it is only keys that are items.
Keys are represented as [Microsoft.Win32.RegistryKey] instances.
Since all keys are (potentially) containers, the registry provider has no concept of a leaf item.
Therefore, use of Test-Path -PathType <type> with a registry path is pointless,
because with -PathType Leaf the result is always $False, and with -PathType Container it is always $True.
By contrast, registry values are represented as properties (the equivalent of what the last-modified date is to a filesystem item, for instance).
Use the *-ItemProperty* cmdlets to read and write registry values.

How to change extended windows file attributes via Powershell without using attrib.exe?

This seems to be a quite simple question, yet googling gave me nothing.
Here is the error (PS 5.1, win 10.0.14393 x64):
Set-ItemProperty $myFileInfo -Name Attributes -Value ([System.IO.FileAttributes]::Temporary)
The attribute cannot be set because attributes are not supported. Only the following attributes can be set: Archive, Hidden, Normal, ReadOnly, or System.
attrib.exe seems to support most of System.IO.FileAttributes. Unfortunately is does not seem to work with files referenced using FileSystem PSDrives. That is what I am using extensively.
Making wrapper for SetFileAttributes kernel API call would be the last resort.
Am I missing any other [more simple] ways setting these extended file attributes?
PS. Apart from [System.IO.FileAttributes]::Temporary I am interested in setting [System.IO.FileAttributes]::NotContentIndexed.
You can edit the Attributes property of a [FileInfo] object directly. For example, if you wanted to exclude all files in the C:\Temp folder from being content indexed, you could do this:
Get-ChildItem C:\Temp | ForEach{
$_.Attributes = $_.Attributes + [System.IO.FileAttributes]::NotContentIndexed
}
That would get each file, and then add the [System.IO.FileAttributes]::NotContentIndexed attribute to the existing attributes. You could probably filter the files to make sure that the attribute doesn't already exist before trying to add it, since that may throw errors (I don't know, I didn't try).
Edit: As noted by #grunge this does not work in Windows Server 2012 R2. Instead what you have to do is reference the value__ property, which is the bitwise flag value, and add the bitwise flag for NotContentIndexed. This should work for you on any Windows OS:
Get-ChildItem C:\Temp | ForEach{
$_.Attributes = [System.IO.FileAttributes]($_.Attributes.value__ + 8192)
}

Powershell - Why does $ENV | GET-MEMBER not work?

I come from a C, C++, C#, python background so i'm applying this thought pattern to Powershell which i'm learning from scratch but I'm a little confused so far as at first glance it seems to be inconsistent and does not follow a fixed base class type structure for all objects so that things can be queried in a consistent manner.
The following works fine:
$host | get-member
$env:username
So $env is a valid object but this does not work:
$env | get-member
These also do not work:
$env.gettype()
dir $env
dir $env:
but this type query on $host does so I'm assuming $host is a .net variable but $env is not?
$host.gettype()
I found that env: also works with dir (aka get-childitem) but this colon is yet another type of notation i'm unfamiliar with and things are starting to get very confusing now. This does not seem to be a string format in this case which I have seen some google posts about so what is it? It behaves like a member selection or dictionary key specifier. If it is a member selector or dictionary key then i would expect get-member to work because it is a standard object.
This outputs the variables and values that I wanted but I don't understand why this syntax is used. This is not DOS syntax either so what's going on here?
dir env:
But dir $env seems to equate to dir $env:userprofile???? why?
Therefore $host appears to be a .net object but $env or env: is something else completely different and I've no idea what object type it is in the grand scheme of things and cannot seem to query it's type with by conventional means. Initial thoughts are that it is a list object of sorts because get-childitem works with it but other than that I'm completely lost.
I'm clearly missing something here so can someone steer me in the right direction please?
Get-Help 'about_Providers' -ShowWindow shows that env: is drive in Environment Provider, i.e. one of Windows PowerShell providers.
BUILT-IN PROVIDERS: Windows PowerShell includes a set of built-in
providers that you can use to access the different types of data
stores.
Provider Drive Data store
-------- ----- ----------
Alias Alias: Windows PowerShell aliases
Certificate Cert: x509 certificates for digital signatures
Environment Env: Windows environment variables
FileSystem * File system drives, directories, and files
Function Function: Windows PowerShell functions
Registry HKLM:, HKCU: Windows registry
Variable Variable: Windows PowerShell variables
WSMan WSMan: WS-Management configuration information
* The FileSystem drives vary on each system.
You can also create your own Windows PowerShell providers, and you can
install providers that others develop. To list the providers that are
available in your session, type: get-psprovider.
That's why Get-ChildItem env: works in contrary to dir $env:, dir $env etc.
First thing to note is that $env and $env:username are not related. $env is just a variable and normally it does not exists, because nobody assign anything to it. Using colon in variable name (like $env:username, with exception to some predefined prefixes: global:, script:, local:, private: and variable:) is a special syntax, which allows to access to PowerShell provider item content with variable syntax. It works with any PowerShell provider which implement content cmdlets: ${C:\Windows\System.ini} or $function:prompt. That syntax is equivalent of calling of Get-Content or Set-Content for given PowerShell path.
My 2 cents:
Try get-psdrive and you will get something like:
Name Used (GB) Free (GB) Provider Root
Env Environment
So it seems to be something like a driver in batch.