简体   繁体   中英

PowerShell WMI query fails to return username in logon script

I'm trying to get the username of domain users in a PowerShell logon script. Any number of different users may log into the computers in question.

A local user account (let's call it 'syscheck') is configured on Win7/Win8 domain clients for the purpose of running a PS script (PS 2.0/3.0); the script resides locally and is launched by Task Scheduler on user logon. The script needs to obtain the username of the domain user that is logging in.

I've attempted to do this with WMI:

Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty UserName

but this does not return anything when the script runs.

If I try this:

$env:USERNAME

The username of the 'syscheck' local account is returned.

Is the domain username not yet available when the script is running on logon?

Perhaps there a way to do this with .NET? Other options?

***** UPDATE August 8 *****

I've tested with the solution provided (thanks Alexander!) but still can NOT retrieve the username of the logged-in user. I believe this is because, as mentioned above, this is a logon script launched by Task Scheduler. The principal for the Task that launches the script is a local account. For some reason, all methods of trying to get the domain username fail.

Here is latest attempt:

First, this is how I call the function:

$indx = 0
do {
    $username = GetDomUser
    if (($indx -eq 25) -or ($username.Length -ne 0)) {
        Write-Output $username
        Break
    }
    else {
        Start-Sleep -Seconds 12
    }
    $indx++
}
while ($indx -lt 25)  # 5 minutes is PLENTY of time for boot...

Now, here's the function:

Function GetDomUser {
    $compname = $($env:COMPUTERNAME)
    $pattern = '"MYDOMAIN",Name='
    $antecedent = @(Get-WmiObject -Class Win32_LoggedOnUser -ComputerName $compname | 
        Where-Object { $_.Antecedent -match $pattern } | Select-Object -ExpandProperty Antecedent)
    Return ([regex]::Match([string]$antecedent[0],"$pattern(.*$)").Value).Split('=')[1] -replace '"', ""
}

Of course, this works perfectly from the console once the machine has booted.

Is it possible to refresh whatever store the Win32_LoggedOnUser Class gets its data from?

Other options?

Here are previous methods I've tried - all return the username of the principal of the Task that launches the script (or an empty string, which is what D returns).

$usernameA = $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)
$usernameB = $(whoami)
$usernameC = $($env:USERNAME)
$usernameD = $(Get-WmiObject Win32_ComputerSystem -ComputerName $compname | Select-Object -ExpandProperty UserName)
$usernameE = $([Environment]::UserName)

Here's what you could do to find out what's going on:

$iLOGON32_LOGON_INTERACTIVE = 2

$cLogonSessions = Get-WmiObject -Class "Win32_LogonSession" `
    | Where-Object { $_.LogonType -eq $iLOGON32_LOGON_INTERACTIVE }
if ($cLogonSessions -ne $null) {
    $cInteractiveLogons = @()
    foreach ($oLogonSession in $cLogonSessions) {
        $sWmiQuery = ('ASSOCIATORS OF {{Win32_LogonSession.LogonId="{0}"}} ' `
            + 'WHERE AssocClass=Win32_LoggedOnUser') -f $oLogonSession.LogonId
        $cInteractiveLogons += Get-WMIObject -Query $sWmiQuery `
            | Select-Object -ExpandProperty "Caption"
    }
} else  {
    $ex = New-Object -TypeName System.NullReferenceException(`
        '$cInteractiveLogons is null.')
    throw $ex
}

$cInteractiveLogons | Select-Object -Unique 

When $cInterativeLogons is null exception is thrown, it means that no-one is logged on interactively (yet) in which case you can wait and re-check later.

Note that this code is not reliable because LOGON32_LOGON_INTERACTIVE wasn't limited to local console logons in XP and earlier versions.


As for actual solution, I'd recommend using some kind of explicit notifications. You could for example make use of events. Subscribe for an event and then emit the event from the user's regular logon script.

The problem was not with the WMI code but rather the state of the machine it was being run on. It turns out that when users are VPNed into their machines (almost always thanks to a VPN client's automated reconnect feature), or have some third-party utility installed (eg certain cloud backup services), there are multiple Logons and "the" logged on user is ambiguous.

For now this is working pretty well:

Function GetDomainUser {
    $compname = $($env:COMPUTERNAME)
    $pattern = '"' + $($env:USERDOMAIN) + '"' + ',Name='
    $antecedent = @(Get-WmiObject -Class Win32_LoggedOnUser -ComputerName $compname | 
        Where-Object { $_.Antecedent -match $pattern } | 
        Select-Object -ExpandProperty Antecedent | Select-Object -Unique)
    Return $(([regex]::Match([string]$antecedent,$($pattern + '(".+")')).Value).Split('=')[1] -replace '"','')
}

But I had to write addition code to work around cases when the LoggedOnUser cannot be discovered (multiple logons exist), or when no one is logged in.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM