2

I am trying to run a PowerShell script to find all computers a user is logged into on my domain. I haven't been able to get anything to work. I found the following script which will run without errors, but never produces output. Any thoughts or suggestions?

Add-PSSnapin Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue
$ErrorActionPreference = "SilentlyContinue"

# Retrieve Username to search for, error checks to make sure the username
# is not blank and that it exists in Active Directory

Function Get-Username {
    $Global:Username = Read-Host "Enter username you want to search for"
    if ($Username -eq $null) {
        Write-Host "Username cannot be blank, please re-enter username!"
        Get-Username
    }
    $UserCheck = Get-QADUser -SamAccountName $Username
    if ($UserCheck -eq $null) {
        Write-Host "Invalid username, please verify this is the logon id for the account"
        Get-Username
    }
}

get-username

$computers = Get-QADComputer | where {$_.accountisdisabled -eq $false} -searchroot '\\MyDomainName\computers'
foreach ($comp in $computers) {
    $Computer = $comp.Name
    $ping = new-object System.Net.NetworkInformation.Ping
    $Reply = $null
    $Reply = $ping.send($Computer)
    if($Reply.status -like 'Success') {
        #Get explorer.exe processes
        $proc = gwmi win32_process -computer $Computer -Filter "Name = 'explorer.exe'"
        #Search collection of processes for username
            ForEach ($p in $proc) {
                $temp = ($p.GetOwner()).User
                if ($temp -eq $Username) {
                write-host "$Username is logged on $Computer"
                }       
            }
        }
    }
Brian Weaver
  • 41
  • 1
  • 2
  • 4
  • 3
    Good luck running this when you have thousands of computers.. The usual way to do this is to read the security logs, use logon+logoff scripts to write to a file (or attribute in AD) or using the functionality/logging in a user environment solution (for those who have one). – Frode F. Feb 07 '16 at 15:37
  • 1
    It could be done with background jobs, but I agree that there are better ways of approaching the problem. – Ansgar Wiechers Feb 07 '16 at 17:11
  • But back to the point: What do you mean it doesn't produce output? If you run this as a script (schduled task or whatever), you won't get any data since you're only using `Write-Host`, but it should write the text to you console. Small tip: move `$ping = new-object ....` outside the foreach-loop so you don't create thousand objects when you can reuse the first one. – Frode F. Feb 07 '16 at 20:25
  • Possible duplicate of [Powershell script to see currently logged in users (domain and machine) + status (active, idle, away)](http://stackoverflow.com/questions/23219718/powershell-script-to-see-currently-logged-in-users-domain-and-machine-status) – user4317867 Feb 07 '16 at 21:07
  • 1
    goole and read more about `$ErrorActionPreference` you will know why there is no output. Change `$ErrorActionPreference=continue` if you want to see the output – Bum Feb 08 '16 at 01:43

4 Answers4

3

We have to login to the AD server and query the Event ID 4624, search the user logged on history from all event list. It display only the IP address of source computer. There we can use the command nslookup to find out the host name. To do do this process it required a well written batch file or power shell script to quickly findout the HOSTNAME.

Manas Dash
  • 53
  • 8
1

If you don't want to be dependent upon a proprietary Quest.ActiveRoles.ADManagement there is a native PowerShell solution available on PSGallery called Get-ActiveUser.

We can feed our list of computers to that and find any place the user is logged in to.

NOTE: As others have mentioned this will take a very long time to query all the computers on your network. Approximately 15 minutes per 100 PCs. Suggestions to check logs will be faster.

# List-Computers-ByUser.ps1
#
[CmdletBinding()]
param (
    # Search for user
    [Parameter(Mandatory=$false,
    ValueFromPipelineByPropertyName=$true,
    HelpMessage="If you don't pass a name you will be prompted",
    Position=0)]
    [String]
    $UserName,

    # Choose method, WMI, CIM or Query
    [Parameter(Mandatory=$false,
    ValueFromPipelineByPropertyName=$true,
    HelpMessage="Default set to WMI",
    Position=1)]
    [ValidateSet('WMI','CIM','Query')]
    [String]
    $Method = "Query"
)

# Retrieve Username to search for, error checks to make sure the username
# is not blank and that it exists in Active Directory
Function Get-Username([String]$UserName) {
    if ([string]::IsNullOrEmpty($UserName)) {
        $UserName = Read-Host "Enter username you want to search for"
    }

    $UserCheck = Get-ADUser -Identity $Username
    if ($null -eq $UserCheck) {
        Write-Debug "Invalid username, please verify this is the logon id for the account"
        $UserName = Get-Username
    }

    return $UserName
}

$Script:UserName = Get-Username $UserName

Write-Host "Checking for PS Module Get-ActiveUser ..."
if (-not (Get-InstalledModule Get-ActiveUser -ErrorAction silentlycontinue)) {
    Install-Module -Name Get-ActiveUser 
}

[String]$Global:output=@()

Write-Host "Searching for username across all computers in domain. This will take a long time ..."
$adComputers =  Get-ADComputer  -Filter 'enabled -eq "true"' | Select-Object -ExpandProperty Name
Measure-Command {
    $output = $adComputers | ForEach-Object -Parallel {
        $users = Get-ActiveUser -ComputerName $_ -Method $using:Method
        ForEach($activeUser in $users) {
            if($activeUser.UserName -eq $UserName) {
                $output+=$activeUser
                Write-Output $activeUser
            }
        }
    } 
}

# Show results, all computers that user is logged in to
$output
HackSlash
  • 4,944
  • 2
  • 18
  • 44
  • It spits out this error: ForEach-Object : Parameter set cannot be resolved using the specified named parameters. + $output = $adComputers | ForEach-Object -Parallel { + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : MetadataError: (:) [ForEach-Object], ParameterBindingException + FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Commands.ForEachObjectCommand – parsecpython Jan 17 '23 at 17:57
  • What version of PowerShell are you using? – HackSlash Jan 18 '23 at 18:30
1

Tested this on Windows 2016 and worked very nice for me in powershell:

$computerName = "."
$wmi = [WMI] ""
get-wmiobject Win32_UserProfile -computername $computerName | foreach-object {
  $userAccount = [WMI] ("\\$computerName\root\cimv2:Win32_SID.SID='{0}'" -f $_.SID)
  $userName = "{0}\{1}" -f $userAccount.ReferencedDomainName,$userAccount.AccountName
  new-object PSObject -property @{
    "Name" = $userName
    "LastUseTime" = $wmi.ConvertToDatetime($_.LastUseTime)
    "Loaded" = $_.Loaded
  }
} | sort-object LastUseTime -descending
grepit
  • 21,260
  • 6
  • 105
  • 81
1

Expanding on Manas' answer because it was exactly what I needed but doesn't show how to do it - and for my own future reference because it will come up again (:

  1. Login to your AD domain controller.
  2. Open powershell as an administrator so you have access to security logs.
  3. If you know the username name of who you're looking for, run the following command. Note the use of newest to trim the results and make it faster to run, increase as needed.
Get-EventLog -LogName Security -InstanceId 4624 -Newest 9000 | 
    Where {$_.Message -Like "*<username here>*"} | 
    Select -First 1 -ExpandProperty Message

If there's a match, the log's message will have a line containing the Source Network Address which will be an ip address. Copy this.

  1. With the IP address you just copied, run nslookup <ip address> to see the computer name the user logged into.

I have various use cases where I need to know what computer someone uses while we're all working remotely and this is the most straight forward solution, just thought it'd be nice to have it spelled out somewhere. In our case, people have remote access to a single computer so I don't need to produce a comprehensive list of all logon events.

sanigirl
  • 433
  • 3
  • 19