in Powershell I want to get a list of Environment Variables with their values AND types (machine or user) from ALL user-profiles.
If I use
gci env:
I'll get all environment variables that are currently set, but I can't see which variable of those is set for the machine and which variable is set for user. And of course I can't see ALL environment variables that are (or have been) set from any users that are working on the current computer (Win10).
Is there a way how such a listing can be coded in Powershell?
Preface:
Note that this answer fundamentally applies to Windows only.
The machine-scope environment variables, as persistently defined in the registry, can be obtained via
[System.Environment]::GetEnvironmentVariables(), which returns an unordered dictionary of environment-variable name-value pairs:[System.Environment]::GetEnvironmentVariables('Machine')For the current user only, the user-scope environment variables, as persistently defined in the registry, can be obtained with
[System.Environment]::GetEnvironmentVariables('User')Any given process sees a composite of these two scopes (for the user in whose context the process is created):
While
[System.Environment]::GetEnvironmentVariables('Process')can be used to retrieve the process-scope variables, in PowerShell it is simplest to useGet-ChildItem Env:, which has the added advantage of returning the variables sorted by name.Similarly, to access a given process-scope variable by name in PowerShell, it simplest to use namespace variable notation, e.g.
$env:OSfor variableOS- see the conceptual about_Environment_Variables help topic.While user-scope environment variable generally override machine-scope ones of the same name, the
PATHenvironment is special: its effective value is the concatenation of the machine- and user-scope values, in that order.Note:
It follows from the above that the user and machine scopes exclusively relate to persisted (persistently predefined) environment variables, which in combination form the runtime process scope, often indirectly, by inheriting the parent process' process-scope environment. At the process level, you cannot reliably tell which persisted scope - if any - a given value came from: it may be a value created or modified by a parent process, or it may be a composite value merging the user- and machine-scope values, as in the case of
PATH. Additionally, there are the so-called volatile (non-persisted, dynamic) environment variables such asUSERPROFILEandAPPDATAthat are created on demand at user login.All methods above report expanded environment-variable values, i.e. any nested environment variable references in persisted-in-the-registry definitions using
REG_EXPAND_SZregistry values were expanded to their values.As of this writing (.NET 8 / PowerShell (Core) 7.4.x), neither the
[System.Environment.NET API nor PowerShell'sEnvironmentprovider support getting or setting unexpanded values.Doing the latter requires working directly with the .NET registry APIs; e.g., the following retrieves the unexpanded definition of the current user's
TEMPenvironment variable:The problematic absence of support for unexpanded environment-variable values in the dedicated APIs is the subject of:
.NET: GitHub issue #1442
PowerShell: GitHub issue #16812
Getting user-scope environment variables across all local user profiles:
The solution below gets the persistently defined user-scope environment variables from all local user profiles.
procexp.exe) utility, as described in this answer.Either way you need to run with elevation, as without it you'll only be able to inspect your own, non-elevated processes.
As js2010 notes, in order to access other users' persistently defined user-scope environment variable definitions, you must explicitly load their profiles into the registry, via the (hidden)
ntuser.datfile in the respective profile directories.Doing so requires elevation (running as administrator).
The following code demonstrates this approach:
It - temporarily - loads other users' local profiles into the
HKEY_USERSregistry hive on demand, into a subkey name for each user's SID in order to extract the target user's user-scope environment-variables.reg.exeload/unload- there may be faster, in-process techniques to achieve the same.It outputs a sorted dictionary, keyed by username, with each entry containing a nested dictionary of environment-variable key-value pairs for that user.
E.g., to query the (unexpanded)
TEMPvariable definition of userjdoe, use the following:Note:
The reported values are unexpanded values; that is, nested environment-variable references are not expanded.
Doing the latter properly would require nontrivial additional effort, as the code doing the expansion would have to take the target user's environment into account; by contrast, using expansion in the caller's environment will lead to false results.
jdoeasking forjsmith's definition ofTEMPand doing expansion of%USERPROFILE%\AppData\Local\Tempin the context ofjdoe, will incorrectly reportC:\Users\jdoe\AppData\Local\Temprather thanC:\Users\jsmith\AppData\Local\Temp