Add Get-UserLastLogonComputer.ps1 - exports users and last PC to CSV
This commit is contained in:
142
Get-UserLastLogonComputer.ps1
Normal file
142
Get-UserLastLogonComputer.ps1
Normal file
@@ -0,0 +1,142 @@
|
||||
#Requires -Modules ActiveDirectory
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Exports a list of users and the last PC they logged into.
|
||||
|
||||
.DESCRIPTION
|
||||
Queries Domain Controller security event logs for interactive logon events (4624)
|
||||
to determine which computer each user last authenticated from.
|
||||
|
||||
.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 LogonTypes
|
||||
Array of logon types to include. Default is 2 (Interactive) and 10 (RemoteInteractive/RDP).
|
||||
|
||||
.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 10 = RemoteInteractive (RDP)
|
||||
Logon Type 11 = CachedInteractive
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter()]
|
||||
[string]$OutputPath = (Get-Location).Path,
|
||||
|
||||
[Parameter()]
|
||||
[int]$DaysBack = 7,
|
||||
|
||||
[Parameter()]
|
||||
[int[]]$LogonTypes = @(2, 10, 11)
|
||||
)
|
||||
|
||||
$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)
|
||||
|
||||
# Build XPath filter for Event ID 4624 (Successful Logon)
|
||||
$LogonTypeFilter = ($LogonTypes | ForEach-Object { "Data[@Name='LogonType']=$_" }) -join " or "
|
||||
$XPath = @"
|
||||
*[System[
|
||||
EventID=4624
|
||||
and TimeCreated[@SystemTime >= '$($StartDate.ToUniversalTime().ToString("o"))']
|
||||
]
|
||||
and EventData[
|
||||
($LogonTypeFilter)
|
||||
]
|
||||
]
|
||||
"@
|
||||
|
||||
try {
|
||||
Write-Host "Retrieving logon events from Security log..." -ForegroundColor Yellow
|
||||
|
||||
$Events = Get-WinEvent -LogName Security -FilterXPath $XPath -ErrorAction Stop |
|
||||
Where-Object {
|
||||
$xml = [xml]$_.ToXml()
|
||||
$targetUser = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text'
|
||||
$targetDomain = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetDomainName' }).'#text'
|
||||
# Filter out computer accounts and system accounts
|
||||
$targetUser -notmatch '\$$' -and
|
||||
$targetUser -notin @('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE', 'DWM-1', 'DWM-2', 'UMFD-0', 'UMFD-1') -and
|
||||
$targetDomain -ne 'Window Manager' -and
|
||||
$targetDomain -ne 'Font Driver Host'
|
||||
}
|
||||
|
||||
Write-Host "Found $($Events.Count) logon events. Processing..." -ForegroundColor Yellow
|
||||
|
||||
$UserLogons = @{}
|
||||
|
||||
foreach ($Event in $Events) {
|
||||
$xml = [xml]$Event.ToXml()
|
||||
|
||||
$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'
|
||||
$LogonType = ($xml.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' }).'#text'
|
||||
$LogonTime = $Event.TimeCreated
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Workstation)) {
|
||||
$Workstation = "Unknown"
|
||||
}
|
||||
|
||||
$UserKey = "$Domain\$Username"
|
||||
|
||||
# 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 = $Workstation.ToUpper()
|
||||
LogonTime = $LogonTime
|
||||
LogonType = switch ($LogonType) {
|
||||
2 { "Interactive" }
|
||||
10 { "RDP" }
|
||||
11 { "Cached" }
|
||||
default { $LogonType }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Results = $UserLogons.Values | Sort-Object Domain, Username
|
||||
|
||||
if ($Results.Count -eq 0) {
|
||||
Write-Host "`nNo user logon events found in the specified time period." -ForegroundColor Yellow
|
||||
return
|
||||
}
|
||||
|
||||
# Export to CSV
|
||||
$Results | Select-Object Domain, Username, Computer, 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 -AutoSize
|
||||
|
||||
} catch [System.Diagnostics.Eventing.Reader.EventLogNotFoundException] {
|
||||
Write-Host "`nError: Security event log not accessible. Ensure you're running on a DC with appropriate permissions." -ForegroundColor Red
|
||||
} catch [System.UnauthorizedAccessException] {
|
||||
Write-Host "`nError: Access denied. Run as Administrator with permissions to read Security logs." -ForegroundColor Red
|
||||
} catch {
|
||||
Write-Host "`nError: $($_.Exception.Message)" -ForegroundColor Red
|
||||
}
|
||||
Reference in New Issue
Block a user