#Requires -Modules ActiveDirectory <# .SYNOPSIS Exports a list of users and the last PC they logged into. .DESCRIPTION Queries Domain Controller security event logs for logon events (4624) to determine which computer each user last authenticated from. Resolves IP addresses to computer names for network logons. .PARAMETER OutputPath Path for the output CSV file. Defaults to current directory. .PARAMETER DaysBack Number of days of event logs to search. Default is 7. .PARAMETER IncludeNetworkLogons Include Type 3 (Network) logons. Default is true. .PARAMETER ResolveIPs Attempt to resolve IP addresses to hostnames. Default is true. .EXAMPLE .\Get-UserLastLogonComputer.ps1 .EXAMPLE .\Get-UserLastLogonComputer.ps1 -OutputPath "C:\Reports" -DaysBack 30 .NOTES Must be run on a Domain Controller with appropriate permissions to read Security logs. Logon Type 2 = Interactive (console) Logon Type 3 = Network (file shares, etc.) Logon Type 10 = RemoteInteractive (RDP) Logon Type 11 = CachedInteractive #> [CmdletBinding()] param( [Parameter()] [string]$OutputPath = (Get-Location).Path, [Parameter()] [int]$DaysBack = 7, [Parameter()] [switch]$ExcludeNetworkLogons, [Parameter()] [switch]$SkipIPResolve ) # Build logon types list $LogonTypes = @(2, 10, 11) # Interactive, RDP, Cached if (-not $ExcludeNetworkLogons) { $LogonTypes += 3 # Network } $Timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $CsvFile = Join-Path $OutputPath "UserLastLogonComputer_$Timestamp.csv" Write-Host "`nQuerying security event logs for the last $DaysBack days..." -ForegroundColor Cyan Write-Host "Looking for logon types: $($LogonTypes -join ', ')" -ForegroundColor Cyan Write-Host "" $StartDate = (Get-Date).AddDays(-$DaysBack) # Cache for IP to hostname resolution $IPCache = @{} function Resolve-IPToHostname { param([string]$IP) if ([string]::IsNullOrWhiteSpace($IP) -or $IP -eq '-' -or $IP -eq '::1' -or $IP -eq '127.0.0.1') { return $null } # Check cache first if ($IPCache.ContainsKey($IP)) { return $IPCache[$IP] } try { # Try DNS reverse lookup $hostname = [System.Net.Dns]::GetHostEntry($IP).HostName # Extract just the computer name (before the domain) $computerName = ($hostname -split '\.')[0].ToUpper() $IPCache[$IP] = $computerName return $computerName } catch { # Try querying AD for the IP try { $computer = Get-ADComputer -Filter "IPv4Address -eq '$IP'" -Properties Name -ErrorAction SilentlyContinue if ($computer) { $IPCache[$IP] = $computer.Name return $computer.Name } } catch {} $IPCache[$IP] = $null return $null } } try { Write-Host "Retrieving logon events from Security log..." -ForegroundColor Yellow $FilterHash = @{ LogName = 'Security' ID = 4624 StartTime = $StartDate } $Events = Get-WinEvent -FilterHashtable $FilterHash -ErrorAction Stop Write-Host "Found $($Events.Count) total 4624 events. Filtering..." -ForegroundColor Yellow $UserLogons = @{} $ProcessedCount = 0 $i = 0 foreach ($Event in $Events) { $i++ if ($i % 5000 -eq 0) { Write-Host " Processing event $i of $($Events.Count)..." -ForegroundColor Gray } $xml = [xml]$Event.ToXml() $LogonType = [int]($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' }).'#text' # Skip if not a logon type we want if ($LogonType -notin $LogonTypes) { continue } $Username = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text' $Domain = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetDomainName' }).'#text' $Workstation = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'WorkstationName' }).'#text' $IPAddress = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' }).'#text' $LogonTime = $Event.TimeCreated # Filter out computer accounts and system accounts if ($Username -match '\$$') { continue } if ($Username -in @('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE', 'DWM-1', 'DWM-2', 'DWM-3', 'DWM-4', 'UMFD-0', 'UMFD-1', 'UMFD-2', 'UMFD-3', 'ANONYMOUS LOGON', '-')) { continue } if ($Domain -in @('Window Manager', 'Font Driver Host', 'NT AUTHORITY')) { continue } if ([string]::IsNullOrWhiteSpace($Username)) { continue } # Determine computer name - try WorkstationName first, then resolve IP $Computer = $null if (-not [string]::IsNullOrWhiteSpace($Workstation) -and $Workstation -ne '-') { $Computer = $Workstation.ToUpper() } elseif (-not $SkipIPResolve -and -not [string]::IsNullOrWhiteSpace($IPAddress)) { $Computer = Resolve-IPToHostname -IP $IPAddress } if ([string]::IsNullOrWhiteSpace($Computer)) { # Store IP if we couldn't resolve if (-not [string]::IsNullOrWhiteSpace($IPAddress) -and $IPAddress -ne '-') { $Computer = "[$IPAddress]" } else { $Computer = "Unknown" } } $UserKey = "$Domain\$Username" $ProcessedCount++ # Keep only the most recent logon for each user if (-not $UserLogons.ContainsKey($UserKey) -or $LogonTime -gt $UserLogons[$UserKey].LogonTime) { $UserLogons[$UserKey] = [PSCustomObject]@{ Domain = $Domain Username = $Username Computer = $Computer IPAddress = if ($IPAddress -and $IPAddress -ne '-') { $IPAddress } else { '' } LogonTime = $LogonTime LogonType = switch ($LogonType) { 2 { "Interactive" } 3 { "Network" } 10 { "RDP" } 11 { "Cached" } default { "Type $LogonType" } } } } } Write-Host "Processed $ProcessedCount matching logon events." -ForegroundColor Yellow $Results = $UserLogons.Values | Sort-Object Domain, Username if ($Results.Count -eq 0) { Write-Host "`nNo user logon events found matching criteria." -ForegroundColor Yellow return } # Export to CSV $Results | Select-Object Domain, Username, Computer, IPAddress, LogonTime, LogonType | Export-Csv -Path $CsvFile -NoTypeInformation -Encoding UTF8 Write-Host "`n===== Results =====" -ForegroundColor Green Write-Host "Total users found: $($Results.Count)" Write-Host "Output saved to: $CsvFile" Write-Host "" # Display summary table $Results | Format-Table Domain, Username, Computer, LogonTime, LogonType -AutoSize } catch [System.Exception] { if ($_.Exception.Message -match "No events were found") { Write-Host "`nNo logon events (Event ID 4624) found in the last $DaysBack days." -ForegroundColor Yellow } elseif ($_.Exception.Message -match "Access is denied") { Write-Host "`nError: Access denied. Run PowerShell as Administrator." -ForegroundColor Red } else { Write-Host "`nError: $($_.Exception.Message)" -ForegroundColor Red } }