Why would $env:username and [environment]::username return different users?

Viewed 5094

What is the difference between PowerShell's $env:username and [environment]::username and why would they potentially return different users? (I understand there are other ways to get the current user as well)

Some background:

I have an Azure Pipeline that runs a PowerShell script on a release target. The pipeline agent is configured to run under a specific service account. Part of that PowerShell script uses $env:username to assign permissions. However, permissions are assigned to the local admin account instead. If I change the script to use [environment]::username the correct service account user is given permissions.

2 Answers

$env:USERNAME, while predefined to reflect the current user's username, is a read-write environment variable, just like any other.

Even though it's obviously inadvisable to do so, a statement such as $env:USERNAME = 'foo' changes the value of environment variable USERNAME for the current process as well as its child processes.

This means that if environment variable USERNAME was modified earlier in the same session or in PowerShell's parent process, possibly via an explicitly specified startup environment, it no longer reflects the true username.

While I don't know why the USERNAME environment variable would differ from the real account in the case of Azure Pipelines, an example of - inadvertently - setting the wrong value is PowerShell's Start-Process cmdlet when given the -UseNewEnvironment switch: due to a bug as of PowerShell Core 7.0.0-preview.5 / Windows PowerShell v5.1, $env:USERNAME unexpectedly always reflects SYSTEM, irrespective of the actual user account - see this GitHub issue.

By contrast, [Environment]::UserName uses a different method for obtaining the current user's username, which does not rely on the value of $env:USERNAME and always reflects the true username[1].

In short: Only [Environment]::UserName reliably reflects the current user account's username.


[1] From the linked docs: "On Windows the UserName property wraps a call to the Windows GetUserName function. The domain account credentials for a user are formatted as the user's domain name, the \ character, and user name. Use the UserDomainName property to obtain the user's domain name and the UserName property to obtain the user name.
On Unix platforms the UserName property wraps a call to the getpwuid_r function.

$env: is looking at your environmental variables. The variable that you're looking at- username- happens to be set to the local username by default. But it doesn't have to be.

[Environment] is a call to the System.Environment class in .NET Framework. This class has a property called UserName that contains the username that is logged into the computer.

Similiar names, but very different things. You could get the path statement by $env:Path, for example, or call [Environment]::Version to get the version of .NET Framework.

Note in this example I deliberately messed with the local environmental variable prior to launching PowerShell to give the wrong username.

C:\WINDOWS\system32>echo %username%
mspow

C:\WINDOWS\system32>set username=bob

C:\WINDOWS\system32>echo %username%
bob

C:\WINDOWS\system32>powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\WINDOWS\system32> echo $env:username
bob
PS C:\WINDOWS\system32> [Environment]::Username
mspow
PS C:\WINDOWS\system32> echo $env:ProgramFiles
C:\Program Files
PS C:\WINDOWS\system32> echo $env:path
C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\dotnet\;C:\Users\mspow\AppData\Local\Microsoft\WindowsApps
PS C:\WINDOWS\system32> [Environment]::Version

Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      30319  42000

You can see all of the available environment variables by using

Get-Item -Path Env:

The only way I know of to see all of the Methods and Properties for [Environment] is the link to the MSDN site. Get-Member doesn't work on it.

Related