Home > Net >  Powershell - Find computers with a specific username logged in
Powershell - Find computers with a specific username logged in

Time:02-13

I've been trying to figure out a powershell script that doing a search of all computers with a specific username logged in.

so far I've found a way looking via all computers the 'explorer.exe' process and owner

Also I found this script:

Function Get-Username {
    Clear-Host
    $Global:Username = Read-Host "Enter Username"
    if ($Username -eq $null){
        Write-Host "Username cannot be blank, please enter a Username"
        Get-Username
    }
    $UserCheck = Get-ADUser $Username
    if ($UserCheck -eq $null){
        Write-Host "Invalid Username, please verify this is the logon id for the account"
        Get-Username
    }
}
Get-Username

Function Get-Computername {
    Clear-Host
    $Global:Prefix = Read-Host "Enter Computername"
    Clear-Host
}
Get-Computername

$computers = Get-ADComputer -Filter {Enabled -eq 'true' -and SamAccountName -like $Prefix}
$CompCount = $Computers.Count
Write-Host "Searching for $Username on $Prefix on $CompCount Computers`n"

foreach ($comp in $computers){
    $Computer = $comp.Name
    $Reply = $null
    $Reply = test-connection $Computer -count 1 -quiet
    if($Reply -eq 'True'){
        if($Computer -eq $env:COMPUTERNAME){
            $proc = gwmi win32_process -ErrorAction SilentlyContinue -computer $Computer -Filter "Name = 'explorer.exe'"
        }
        else{
            $proc = gwmi win32_process -ErrorAction SilentlyContinue -Credential $Credential -computer $Computer -Filter "Name = 'explorer.exe'"
        }           

            $progress           
            ForEach ($p in $proc) {             
                $temp = ($p.GetOwner()).User
                Write-Progress -activity "Working..." -status "Status: $progress of $CompCount Computers checked" -PercentComplete (($progress/$Computers.Count)*100)
                if ($temp -eq $Username){
                write-host "$Username is logged on $Computer"
                }
            }
        }   
    }

This is not my script, only trying to use it.

this script can take a very long long time, trying to run on about 300 machines, it can take for 20 minutes run.

now im not sure, which part is taking most of the time, im thinking either the gwmi win32 process, that checking on each machine if the process 'explorer.exe' is on or the part test-connection that trying to ping each machine first, and then check for the 'explorer.exe' process.

for your knowledge, is there any faster way with powershell script to do this kind of checkup? to search for a specific username on which machines logged on?

Thanks in advance for any help!

CodePudding user response:

this is a slightly different approach. [grin] it presumes that you may want to check on more than one user name, so it grabs ALL the users on ALL the systems. once you have that, you can query for the one you want ... and then query for the next without having to wait for the system list to be scanned again.

the code ...

127.0.0.1
BetterNotBeThere
$env:COMPUTERNAME
"@ -split [System.Environment]::NewLine
#endregion >>> fake reading in a list of computer names


$GCIMI_Params = @{
    ClassName = 'CIM_ComputerSystem'
    ComputerName = $ComputerList
    ErrorAction = 'SilentlyContinue'
    # this will hold the errors from the non-repsonding systems - if you want that info
    ErrorVariable =  'NonResponderList'
    }
$LoggedInUserList = Get-CimInstance @GCIMI_Params |
    ForEach-Object {
        [PSCustomObject]@{
            # the ".Split()" gets rid of the domain/workgroup/system part of the UserName
            UserName = $_.UserName.Split('\')[-1]
            ComputerName = $_.DNSHostName
            # this shows the name used by the G-CimI call - it can be different
            QueryComputerName = $_.PSComputerName
            }
        }

#get your target user name
$TargetUserName = $env:USERNAME

#get the system that name is logged into
$LoggedInUserList.Where({$_.UserName -eq $TargetUserName})
 

output on my system today ...

UserName    ComputerName QueryComputerName
--------    ------------ -----------------
MyUserName  MySysName    MySysName            
MyUserName  MySysName    LocalHost        
MyUserName  MySysName    127.0.0.1

what the code does ...

  • builds a list of systems to check
    when you are ready to do this with real data, just remove the entire #region/#endregion block & replace it with your Get-ADComputer call.
  • builds a splat of the parameters for the Get-CimInstance call
  • sends the G-CimI call to the target systems
  • generates a [PSCustomObject] containing the desired properties
  • sends that PSCO out to a results collection
  • grabs a user name
  • filters the results collection
  • shows the system[s] the user name is logged into - if there are any

note that i only have one system, so the 3 responses are all from that same box. also note that the computer can be referred to by DNS name, IP address, or LocalHost.

CodePudding user response:

Continuing from my comment. . .

Here's an quick and dirty approach you can take:

Function Get-Username {
    Clear-Host
    $Global:Username = Read-Host "Enter Username"
    if ($Username -eq $null){
        Write-Host "Username cannot be blank, please enter a Username"
        Get-Username
    }
    $UserCheck = Get-ADUser $Username
    if ($UserCheck -eq $null){
        Write-Host "Invalid Username, please verify this is the logon id for the account"
        Get-Username
    }
}
Get-Username
Function Get-Computername {
    Clear-Host
    $Global:Prefix = Read-Host "Enter Computername"
    Clear-Host
}
Get-Computername

$computers = Get-ADComputer -Filter {Enabled -eq $true -and SamAccountName -like $Prefix}

$check = Get-CimInstance -Query "SELECT Name,UserName from Win32_ComputerSystem WHERE UserName LIKE '%$userName%'" `
                -ComputerName $Computers.Name`
                -ErrorAction SilentlyContinue
if ($check) { 
    # $check.UserName/Name may return an array same name users are found. 
    Write-Output -InputObject ($check.UserName   " is logged into: "   $check.Name)
}
else {
    Write-Output -InputObject "Unable to find $userName."
}

Seeing as you're not doing nothing with the systems that you're unable to ping, you can silence the output and only returning the results for the ones that are online. Best solution for you scenario and question, is to take advantage of the parallelization that Get-CIMInstance allows you to do when passing an array of computers to -ComputerName.

  • Write-Progress tends to be terribly slow and if you're looking for a fast solution, this will definitely slow you down.
  • Get-WMIObject is a deprecated cmdlet that has been superseded by Get-CIMInstance which offers the same functionality, with a safer remoting procedure.
  • Using -Query, we can use WQL to search at the time of query speeding up the process some more. The only downside to some admins is that it follows its own syntax.

One thing to note, if you can ping a machine, it doesn't mean you can connect to it.

  • Related