Files
management-scripts/Get-UserLogonActivity.ps1
T
2026-07-08 11:27:51 -04:00

844 lines
28 KiB
PowerShell

#Requires -Version 5.1
<#
.SYNOPSIS
Gets user logon, logoff, lock, unlock, and power activity from Windows event logs.
.EXAMPLE
$Start = '2026-07-03T00:00:00Z'
$End = '2026-07-06T00:00:00Z'
.\Get-UserLogonActivity.ps1
.EXAMPLE
.\Get-UserLogonActivity.ps1
#>
$StartVariable = Get-Variable -Name Start -ErrorAction SilentlyContinue
$EndVariable = Get-Variable -Name End -ErrorAction SilentlyContinue
$UseDateRange = ($null -ne $StartVariable -and $null -ne $EndVariable)
If (($null -ne $StartVariable) -xor ($null -ne $EndVariable)) {
Write-Error '$Start and $End must either both be defined or both be omitted. If omitted, the script returns the latest 15 activity events.'
Exit 1
}
Function ConvertTo-ActivityDateTime {
Param(
[Parameter(Mandatory=$true)]
[AllowNull()]
[object]$Value,
[Parameter(Mandatory=$true)]
[string]$ParameterName
)
If ($Value -is [DateTimeOffset]) {
Return $Value
}
If ($Value -is [DateTime]) {
Return [DateTimeOffset]$Value
}
$TrimmedValue = ([string]$Value).Trim()
$Culture = [Globalization.CultureInfo]::InvariantCulture
$ParsedDateTime = [DateTimeOffset]::MinValue
$UtcFormats = [string[]]@(
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-dd'T'HH:mm:ss.FFFFFFF'Z'"
)
$OffsetFormats = [string[]]@(
"yyyy-MM-dd'T'HH:mm:sszzz",
"yyyy-MM-dd'T'HH:mm:ss.FFFFFFFzzz"
)
$LocalFormats = [string[]]@(
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss.FFFFFFF",
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd",
"MM/dd/yyyy HH:mm:ss",
"MM/dd/yyyy"
)
$UtcStyles = [Globalization.DateTimeStyles]::AllowWhiteSpaces -bor [Globalization.DateTimeStyles]::AssumeUniversal -bor [Globalization.DateTimeStyles]::AdjustToUniversal
$LocalStyles = [Globalization.DateTimeStyles]::AllowWhiteSpaces -bor [Globalization.DateTimeStyles]::AssumeLocal
If ([DateTimeOffset]::TryParseExact($TrimmedValue, $UtcFormats, $Culture, $UtcStyles, [ref]$ParsedDateTime)) {
Return $ParsedDateTime
}
If ([DateTimeOffset]::TryParseExact($TrimmedValue, $OffsetFormats, $Culture, [Globalization.DateTimeStyles]::AllowWhiteSpaces, [ref]$ParsedDateTime)) {
Return $ParsedDateTime
}
If ([DateTimeOffset]::TryParseExact($TrimmedValue, $LocalFormats, $Culture, $LocalStyles, [ref]$ParsedDateTime)) {
Return $ParsedDateTime
}
If ([DateTimeOffset]::TryParse($TrimmedValue, $Culture, $LocalStyles, [ref]$ParsedDateTime)) {
Return $ParsedDateTime
}
Throw "Invalid -${ParameterName} datetime '${Value}'. Use ISO 8601 UTC format: yyyy-MM-ddTHH:mm:ssZ. Example: 2026-07-03T00:00:00Z"
}
Function Get-EventDataValue {
Param(
[Parameter(Mandatory=$true)]
[xml]$EventXml,
[Parameter(Mandatory=$true)]
[string]$Name
)
$Data = $EventXml.Event.EventData.Data | Where-Object { $_.Name -eq $Name } | Select-Object -First 1
If ($null -eq $Data) { Return $null }
Return [string]$Data.'#text'
}
Function Get-EventDataValueFromList {
Param(
[Parameter(Mandatory=$true)]
[xml]$EventXml,
[Parameter(Mandatory=$true)]
[string[]]$Names
)
ForEach ($Name in $Names) {
$Value = Get-EventDataValue -EventXml $EventXml -Name $Name
If (-not [string]::IsNullOrWhiteSpace($Value) -and $Value -ne '-') {
Return $Value
}
}
Return $null
}
Function Get-UserDataEventXmlValue {
Param(
[Parameter(Mandatory=$true)]
[xml]$EventXml,
[Parameter(Mandatory=$true)]
[string]$Name
)
$Data = $EventXml.Event.UserData.EventXML.ChildNodes | Where-Object { $_.Name -eq $Name } | Select-Object -First 1
If ($null -eq $Data) { Return $null }
Return [string]$Data.'#text'
}
Function Test-UserLogonActivityUser {
Param(
[AllowNull()]
[string]$UserName
)
If ([string]::IsNullOrWhiteSpace($UserName)) { Return $false }
$LeafUserName = ($UserName -split '\\')[-1]
If ($LeafUserName -eq '-') { Return $false }
If ($LeafUserName.EndsWith('$')) { Return $false }
If ($LeafUserName -in @('ANONYMOUS LOGON', 'LOCAL SERVICE', 'NETWORK SERVICE', 'SYSTEM')) { Return $false }
If ($LeafUserName -match '^(DWM|UMFD)-\d+$') { Return $false }
Return $true
}
Function Format-UserName {
Param(
[AllowNull()]
[string]$DomainName,
[Parameter(Mandatory=$true)]
[string]$UserName
)
If ([string]::IsNullOrWhiteSpace($DomainName) -or $DomainName -eq '-') {
Return $UserName
}
Return "${DomainName}\${UserName}"
}
Function Resolve-UserLogonActivitySid {
Param(
[AllowNull()]
[object]$Sid
)
If ($null -eq $Sid) { Return $null }
$SidString = ([string]$Sid).Trim()
If ([string]::IsNullOrWhiteSpace($SidString) -or $SidString -eq '-') { Return $null }
Try {
Return (New-Object Security.Principal.SecurityIdentifier($SidString)).Translate([Security.Principal.NTAccount]).Value
}
Catch {
$ProfilePath = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\${SidString}" -Name ProfileImagePath -ErrorAction SilentlyContinue).ProfileImagePath
If (-not [string]::IsNullOrWhiteSpace($ProfilePath)) {
Return (Split-Path -Path $ProfilePath -Leaf)
}
Return $SidString
}
}
Function Get-UserLogonActivityKey {
Param(
[AllowNull()]
[object]$Sid,
[AllowNull()]
[string]$User
)
$SidString = ([string]$Sid).Trim()
If (-not [string]::IsNullOrWhiteSpace($SidString) -and $SidString -ne '-') {
Return $SidString.ToUpperInvariant()
}
$UserString = ([string]$User).Trim()
If (-not [string]::IsNullOrWhiteSpace($UserString)) {
Return $UserString.ToUpperInvariant()
}
Return ''
}
Function Get-UserLogonActivitySidFromEvent {
Param(
[Parameter(Mandatory=$true)]
[object]$Event
)
$EventXml = [xml]$Event.ToXml()
$UserSid = Get-EventDataValueFromList -EventXml $EventXml -Names @('UserSid', 'TargetUserSid', 'SubjectUserSid')
If (-not [string]::IsNullOrWhiteSpace($UserSid)) {
Return $UserSid
}
ForEach ($Property in $Event.Properties) {
$PropertyValue = [string]$Property.Value
If ($PropertyValue -match '^S-\d-\d+-.+') {
Return $PropertyValue
}
}
Return [string]$EventXml.Event.System.Security.UserID
}
Function New-UserLogonActivityRecord {
Param(
[Parameter(Mandatory=$true)]
[datetime]$TimeCreated,
[Parameter(Mandatory=$true)]
[string]$User,
[Parameter(Mandatory=$true)]
[ValidateSet('logon', 'logoff', 'unlock', 'lock', 'remote logon', 'session disconnect', 'session reconnect', 'screensaver start', 'screensaver dismiss', 'sleep', 'wake', 'shutdown', 'restart', 'power on')]
[string]$Activity,
[Parameter(Mandatory=$true)]
[string]$Source,
[string]$Details = '',
[string]$UserKey = '',
[int]$SourcePriority = 50,
[switch]$AllowSystemUser
)
If (-not $AllowSystemUser -and -not (Test-UserLogonActivityUser -UserName $User)) { Return $null }
$ResolvedUserKey = $UserKey
If ([string]::IsNullOrWhiteSpace($ResolvedUserKey)) {
$ResolvedUserKey = Get-UserLogonActivityKey -Sid $null -User $User
}
[PSCustomObject]@{
Date = $TimeCreated.ToString('yyyy-MM-dd')
Time = $TimeCreated.ToString('HH:mm:ss')
User = $User
Activity = $Activity
Source = $Source
Details = $Details
UserKey = $ResolvedUserKey
SourcePriority = $SourcePriority
TimeCreated = $TimeCreated
}
}
Function ConvertTo-WinlogonUserActivity {
Param(
[Parameter(Mandatory=$true)]
[object]$Event
)
Switch ($Event.Id) {
7001 { $Activity = 'logon'; $Source = 'Winlogon 7001' }
7002 { $Activity = 'logoff'; $Source = 'Winlogon 7002' }
Default { Return $null }
}
$UserSid = Get-UserLogonActivitySidFromEvent -Event $Event
$User = Resolve-UserLogonActivitySid -Sid $UserSid
If ([string]::IsNullOrWhiteSpace($User)) { Return $null }
New-UserLogonActivityRecord -TimeCreated $Event.TimeCreated -User $User -Activity $Activity -Source $Source -UserKey (Get-UserLogonActivityKey -Sid $UserSid -User $User) -SourcePriority 10
}
Function ConvertTo-SecurityUserActivity {
Param(
[Parameter(Mandatory=$true)]
[object]$Event
)
$EventXml = [xml]$Event.ToXml()
$UserName = Get-EventDataValueFromList -EventXml $EventXml -Names @('TargetUserName', 'SubjectUserName')
$DomainName = Get-EventDataValueFromList -EventXml $EventXml -Names @('TargetDomainName', 'SubjectDomainName')
$UserSid = Get-EventDataValueFromList -EventXml $EventXml -Names @('TargetUserSid', 'SubjectUserSid')
$LogonType = Get-EventDataValue -EventXml $EventXml -Name 'LogonType'
$Activity = $null
$Source = $null
$SourcePriority = 20
$Details = ''
Switch ($Event.Id) {
4624 {
Switch ($LogonType) {
'7' {
$Activity = 'unlock'
$Source = 'Security 4624 type 7'
$SourcePriority = 30
}
'10' {
$Activity = 'remote logon'
$Source = 'Security 4624 type 10'
$SourcePriority = 20
$IpAddress = Get-EventDataValue -EventXml $EventXml -Name 'IpAddress'
$WorkstationName = Get-EventDataValue -EventXml $EventXml -Name 'WorkstationName'
$DetailsParts = @()
If (-not [string]::IsNullOrWhiteSpace($IpAddress) -and $IpAddress -ne '-') { $DetailsParts += "Address=${IpAddress}" }
If (-not [string]::IsNullOrWhiteSpace($WorkstationName) -and $WorkstationName -ne '-') { $DetailsParts += "Workstation=${WorkstationName}" }
$Details = $DetailsParts -join '; '
}
Default {
Return $null
}
}
}
4800 {
$Activity = 'lock'
$Source = 'Security 4800'
}
4801 {
$Activity = 'unlock'
$Source = 'Security 4801'
}
4802 {
$Activity = 'screensaver start'
$Source = 'Security 4802'
}
4803 {
$Activity = 'screensaver dismiss'
$Source = 'Security 4803'
}
Default {
Return $null
}
}
$User = Format-UserName -DomainName $DomainName -UserName $UserName
New-UserLogonActivityRecord -TimeCreated $Event.TimeCreated -User $User -Activity $Activity -Source $Source -Details $Details -UserKey (Get-UserLogonActivityKey -Sid $UserSid -User $User) -SourcePriority $SourcePriority
}
Function ConvertTo-TerminalServicesUserActivity {
Param(
[Parameter(Mandatory=$true)]
[object]$Event
)
$EventXml = [xml]$Event.ToXml()
$User = Get-UserDataEventXmlValue -EventXml $EventXml -Name 'User'
$SessionID = Get-UserDataEventXmlValue -EventXml $EventXml -Name 'SessionID'
$Address = Get-UserDataEventXmlValue -EventXml $EventXml -Name 'Address'
$DetailsParts = @()
If ([string]::IsNullOrWhiteSpace($User)) { Return $null }
If (-not [string]::IsNullOrWhiteSpace($SessionID)) { $DetailsParts += "Session=${SessionID}" }
If (-not [string]::IsNullOrWhiteSpace($Address) -and $Address -ne '-') { $DetailsParts += "Address=${Address}" }
Switch ($Event.Id) {
24 {
$Activity = 'session disconnect'
$Source = 'TermSvc 24'
}
25 {
$Activity = 'session reconnect'
$Source = 'TermSvc 25'
}
Default {
Return $null
}
}
New-UserLogonActivityRecord -TimeCreated $Event.TimeCreated -User $User -Activity $Activity -Source $Source -Details ($DetailsParts -join '; ') -UserKey (Get-UserLogonActivityKey -Sid $null -User $User) -SourcePriority 20
}
Function ConvertTo-PowerUserActivity {
Param(
[Parameter(Mandatory=$true)]
[object]$Event
)
$EventXml = [xml]$Event.ToXml()
$ProviderName = [string]$Event.ProviderName
$User = 'SYSTEM'
$Activity = $null
If ($ProviderName -ieq 'Microsoft-Windows-Kernel-Power' -and $Event.Id -eq 42) {
$Activity = 'sleep'
$Source = 'Kernel-Power 42'
}
ElseIf ($ProviderName -ieq 'Microsoft-Windows-Power-Troubleshooter' -and $Event.Id -eq 1) {
$Activity = 'wake'
$Source = 'Power-Troubleshooter 1'
}
ElseIf ($ProviderName -ieq 'EventLog' -and $Event.Id -eq 6005) {
$Activity = 'power on'
$Source = 'EventLog 6005'
}
ElseIf ($ProviderName -ieq 'EventLog' -and $Event.Id -eq 6006) {
$Activity = 'shutdown'
$Source = 'EventLog 6006'
}
ElseIf ($ProviderName -ieq 'User32' -and $Event.Id -eq 1074) {
$ShutdownType = Get-EventDataValue -EventXml $EventXml -Name 'param5'
$EventUser = Get-EventDataValue -EventXml $EventXml -Name 'param7'
If (-not [string]::IsNullOrWhiteSpace($EventUser) -and $EventUser -ne '-') {
$User = $EventUser
}
If ($ShutdownType -match 'restart') {
$Activity = 'restart'
}
Else {
$Activity = 'shutdown'
}
$Source = 'User32 1074'
}
Else {
Return $null
}
New-UserLogonActivityRecord -TimeCreated $Event.TimeCreated -User $User -Activity $Activity -Source $Source -SourcePriority 10 -AllowSystemUser
}
Function Get-UserLogonActivityWinEvent {
Param(
[Parameter(Mandatory=$true)]
[hashtable]$FilterHashtable,
[int]$MaxEvents = 0,
[Parameter(Mandatory=$true)]
[string]$LogDescription
)
$Parameters = @{
FilterHashtable = $FilterHashtable
ErrorAction = 'Stop'
}
If ($MaxEvents -gt 0) {
$Parameters.MaxEvents = $MaxEvents
}
Try {
Return @(Get-WinEvent @Parameters)
}
Catch {
If ($_.FullyQualifiedErrorId -like '*NoMatchingEventsFound*' -or $_.Exception.Message -like '*No events were found*') {
Return @()
}
Write-Error "Unable to read ${LogDescription}. Run PowerShell as administrator if access is denied. $($_.Exception.Message)"
Return @()
}
}
Function Add-UserLogonActivityRecord {
Param(
[Parameter(Mandatory=$true)]
[object]$ActivityEvents,
[AllowNull()]
[object]$ActivityEvent
)
If ($null -ne $ActivityEvent) {
$ActivityEvents.Add($ActivityEvent) | Out-Null
}
}
Function Get-UniqueUserLogonActivity {
Param(
[AllowNull()]
[object[]]$ActivityEvents
)
$UniqueEvents = New-Object System.Collections.Generic.List[object]
ForEach ($ActivityEvent in (@($ActivityEvents) | Where-Object { $null -ne $_ } | Sort-Object -Property TimeCreated,User,Activity,SourcePriority)) {
$IsDuplicate = $false
$ActivityEventKey = [string]$ActivityEvent.UserKey
If ([string]::IsNullOrWhiteSpace($ActivityEventKey)) {
$ActivityEventKey = Get-UserLogonActivityKey -Sid $null -User $ActivityEvent.User
}
For ($Index = 0; $Index -lt $UniqueEvents.Count; $Index++) {
$UniqueEvent = $UniqueEvents[$Index]
$UniqueEventKey = [string]$UniqueEvent.UserKey
If ([string]::IsNullOrWhiteSpace($UniqueEventKey)) {
$UniqueEventKey = Get-UserLogonActivityKey -Sid $null -User $UniqueEvent.User
}
If (
$UniqueEventKey -eq $ActivityEventKey -and
$UniqueEvent.Activity -eq $ActivityEvent.Activity -and
[Math]::Abs(($UniqueEvent.TimeCreated - $ActivityEvent.TimeCreated).TotalSeconds) -le 5
) {
If ($ActivityEvent.SourcePriority -lt $UniqueEvent.SourcePriority) {
$UniqueEvents[$Index] = $ActivityEvent
}
$IsDuplicate = $true
Break
}
}
If (-not $IsDuplicate) {
$UniqueEvents.Add($ActivityEvent) | Out-Null
}
}
Return $UniqueEvents
}
Function Write-UserLogonActivityOutput {
Param(
[AllowNull()]
[object[]]$ActivityEvents
)
$Rows = @($ActivityEvents | Select-Object Date,Time,User,Activity,Source,Details)
If ($Rows.Count -eq 0) {
Write-Output 'No matching user logon activity events found.'
Return
}
Write-Output (($Rows | Format-Table -AutoSize | Out-String -Width 240).TrimEnd())
}
Function Get-PossibleUseWindow {
Param(
[AllowNull()]
[object[]]$ActivityEvents
)
$OpenWindows = @{}
$UseWindows = New-Object System.Collections.Generic.List[object]
$OpeningActivities = @('logon', 'unlock', 'remote logon', 'session reconnect', 'screensaver dismiss')
$ImpliedClosureDescriptions = @{
'unlock' = 'implied prior lock before'
'session reconnect' = 'implied prior disconnect before'
'screensaver dismiss' = 'implied prior screensaver before'
}
ForEach ($ActivityEvent in (@($ActivityEvents) | Where-Object { $null -ne $_ } | Sort-Object -Property TimeCreated)) {
$User = [string]$ActivityEvent.User
$UserKey = [string]$ActivityEvent.UserKey
$Activity = [string]$ActivityEvent.Activity
$Source = [string]$ActivityEvent.Source
If ([string]::IsNullOrWhiteSpace($UserKey)) {
$UserKey = Get-UserLogonActivityKey -Sid $null -User $User
}
If ($Activity -in @('sleep', 'shutdown', 'restart')) {
ForEach ($OpenUserKey in @($OpenWindows.Keys)) {
$OpenWindow = $OpenWindows[$OpenUserKey]
$UseWindows.Add([PSCustomObject]@{
StartTime = $OpenWindow.StartTime
Start = $OpenWindow.StartTime.ToString('yyyy-MM-dd HH:mm:ss')
End = $ActivityEvent.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
User = $OpenWindow.User
StartedBy = $OpenWindow.StartedBy
EndedBy = "${Activity} / ${Source}"
}) | Out-Null
$OpenWindows.Remove($OpenUserKey)
}
Continue
}
If ([string]::IsNullOrWhiteSpace($User) -or $User -eq 'SYSTEM' -or $User -eq 'NT AUTHORITY\SYSTEM') {
Continue
}
If ($Activity -in $OpeningActivities) {
If ($OpenWindows.ContainsKey($UserKey) -and $ImpliedClosureDescriptions.ContainsKey($Activity)) {
$OpenWindow = $OpenWindows[$UserKey]
$UseWindows.Add([PSCustomObject]@{
StartTime = $OpenWindow.StartTime
Start = $OpenWindow.StartTime.ToString('yyyy-MM-dd HH:mm:ss')
End = "before $($ActivityEvent.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss'))"
User = $OpenWindow.User
StartedBy = $OpenWindow.StartedBy
EndedBy = "$($ImpliedClosureDescriptions[$Activity]) ${Activity} / ${Source}"
}) | Out-Null
$OpenWindows.Remove($UserKey)
}
If (-not $OpenWindows.ContainsKey($UserKey)) {
$OpenWindows[$UserKey] = [PSCustomObject]@{
StartTime = $ActivityEvent.TimeCreated
User = $User
StartedBy = "${Activity} / ${Source}"
}
}
}
ElseIf ($Activity -in @('logoff', 'lock', 'session disconnect')) {
If ($OpenWindows.ContainsKey($UserKey)) {
$OpenWindow = $OpenWindows[$UserKey]
$UseWindows.Add([PSCustomObject]@{
StartTime = $OpenWindow.StartTime
Start = $OpenWindow.StartTime.ToString('yyyy-MM-dd HH:mm:ss')
End = $ActivityEvent.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
User = $OpenWindow.User
StartedBy = $OpenWindow.StartedBy
EndedBy = "${Activity} / ${Source}"
}) | Out-Null
$OpenWindows.Remove($UserKey)
}
}
}
ForEach ($OpenUserKey in @($OpenWindows.Keys)) {
$OpenWindow = $OpenWindows[$OpenUserKey]
$UseWindows.Add([PSCustomObject]@{
StartTime = $OpenWindow.StartTime
Start = $OpenWindow.StartTime.ToString('yyyy-MM-dd HH:mm:ss')
End = 'open through returned events'
User = $OpenWindow.User
StartedBy = $OpenWindow.StartedBy
EndedBy = 'not closed in returned events'
}) | Out-Null
}
Return $UseWindows
}
Function Write-PossibleUseWindowOutput {
Param(
[AllowNull()]
[object[]]$ActivityEvents
)
$UseWindows = @(Get-PossibleUseWindow -ActivityEvents $ActivityEvents | Sort-Object -Property StartTime)
If ($UseWindows.Count -eq 0) {
Write-Output ''
Write-Output 'Possible use windows inferred from returned events: none'
Return
}
$Rows = @($UseWindows | Select-Object Start,End,User,StartedBy,EndedBy)
Write-Output ''
Write-Output 'Possible use windows inferred from returned events:'
Write-Output (($Rows | Format-Table -AutoSize | Out-String -Width 240).TrimEnd())
}
# Winlogon 7001/7002 tracks user session logon/logoff more reliably than Security 4624/4634.
$WinlogonFilter = @{
LogName = 'System'
ProviderName = 'Microsoft-Windows-Winlogon'
Id = @(7001, 7002)
}
$SecurityFilter = @{
LogName = 'Security'
Id = @(4800, 4801, 4802, 4803)
}
# Security 4624 type 7 is unlock authentication evidence and type 10 is RDP evidence.
# Query 4624 separately so it cannot crowd out 4800/4801 workstation lock/unlock events.
$UnlockAuthFilter = @{
LogName = 'Security'
Id = 4624
}
$TerminalServicesFilter = @{
LogName = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'
Id = @(24, 25)
}
$PowerFilters = @(
@{
LogName = 'System'
ProviderName = 'Microsoft-Windows-Kernel-Power'
Id = 42
},
@{
LogName = 'System'
ProviderName = 'Microsoft-Windows-Power-Troubleshooter'
Id = 1
},
@{
LogName = 'System'
ProviderName = 'EventLog'
Id = @(6005, 6006)
},
@{
LogName = 'System'
ProviderName = 'User32'
Id = 1074
}
)
If ($UseDateRange) {
Try {
$StartDateTime = ConvertTo-ActivityDateTime -Value $Start -ParameterName 'Start'
$EndDateTime = ConvertTo-ActivityDateTime -Value $End -ParameterName 'End'
}
Catch {
Write-Error $_.Exception.Message
Exit 1
}
If ($EndDateTime -le $StartDateTime) {
Write-Error "-End must be later than -Start."
Exit 1
}
$WinlogonFilter.StartTime = $StartDateTime.LocalDateTime
$WinlogonFilter.EndTime = $EndDateTime.LocalDateTime
$SecurityFilter.StartTime = $StartDateTime.LocalDateTime
$SecurityFilter.EndTime = $EndDateTime.LocalDateTime
$UnlockAuthFilter.StartTime = $StartDateTime.LocalDateTime
$UnlockAuthFilter.EndTime = $EndDateTime.LocalDateTime
$TerminalServicesFilter.StartTime = $StartDateTime.LocalDateTime
$TerminalServicesFilter.EndTime = $EndDateTime.LocalDateTime
ForEach ($PowerFilter in $PowerFilters) {
$PowerFilter.StartTime = $StartDateTime.LocalDateTime
$PowerFilter.EndTime = $EndDateTime.LocalDateTime
}
$ActivityEvents = New-Object System.Collections.Generic.List[object]
ForEach ($Event in (Get-UserLogonActivityWinEvent -FilterHashtable $WinlogonFilter -LogDescription 'System Winlogon event log')) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-WinlogonUserActivity -Event $Event)
}
ForEach ($Event in (Get-UserLogonActivityWinEvent -FilterHashtable $SecurityFilter -LogDescription 'Security event log')) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-SecurityUserActivity -Event $Event)
}
ForEach ($Event in (Get-UserLogonActivityWinEvent -FilterHashtable $UnlockAuthFilter -LogDescription 'Security unlock-authentication event log')) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-SecurityUserActivity -Event $Event)
}
ForEach ($Event in (Get-UserLogonActivityWinEvent -FilterHashtable $TerminalServicesFilter -LogDescription 'Terminal Services Local Session Manager event log')) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-TerminalServicesUserActivity -Event $Event)
}
ForEach ($PowerFilter in $PowerFilters) {
ForEach ($Event in (Get-UserLogonActivityWinEvent -FilterHashtable $PowerFilter -LogDescription 'System power event log')) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-PowerUserActivity -Event $Event)
}
}
$OutputEvents = @(Get-UniqueUserLogonActivity -ActivityEvents $ActivityEvents | Sort-Object -Property TimeCreated)
Write-UserLogonActivityOutput -ActivityEvents $OutputEvents
Write-PossibleUseWindowOutput -ActivityEvents $OutputEvents
}
Else {
$ActivityLimit = 15
$SearchLimit = 200
$MaxSearchLimit = 10000
$OutputEvents = @()
Do {
$ActivityEvents = New-Object System.Collections.Generic.List[object]
$WinlogonEvents = @(Get-UserLogonActivityWinEvent -FilterHashtable $WinlogonFilter -MaxEvents $SearchLimit -LogDescription 'System Winlogon event log')
$SecurityEvents = @(Get-UserLogonActivityWinEvent -FilterHashtable $SecurityFilter -MaxEvents $SearchLimit -LogDescription 'Security event log')
$UnlockAuthEvents = @(Get-UserLogonActivityWinEvent -FilterHashtable $UnlockAuthFilter -MaxEvents $SearchLimit -LogDescription 'Security unlock-authentication event log')
$TerminalServicesEvents = @(Get-UserLogonActivityWinEvent -FilterHashtable $TerminalServicesFilter -MaxEvents $SearchLimit -LogDescription 'Terminal Services Local Session Manager event log')
$PowerEvents = @()
$PowerFiltersMayHaveMoreEvents = $false
ForEach ($Event in $WinlogonEvents) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-WinlogonUserActivity -Event $Event)
}
ForEach ($Event in $SecurityEvents) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-SecurityUserActivity -Event $Event)
}
ForEach ($Event in $UnlockAuthEvents) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-SecurityUserActivity -Event $Event)
}
ForEach ($Event in $TerminalServicesEvents) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-TerminalServicesUserActivity -Event $Event)
}
ForEach ($PowerFilter in $PowerFilters) {
$CurrentPowerEvents = @(Get-UserLogonActivityWinEvent -FilterHashtable $PowerFilter -MaxEvents $SearchLimit -LogDescription 'System power event log')
$PowerEvents += $CurrentPowerEvents
If ($CurrentPowerEvents.Count -ge $SearchLimit) {
$PowerFiltersMayHaveMoreEvents = $true
}
}
ForEach ($Event in $PowerEvents) {
Add-UserLogonActivityRecord -ActivityEvents $ActivityEvents -ActivityEvent (ConvertTo-PowerUserActivity -Event $Event)
}
$OutputEvents = @(Get-UniqueUserLogonActivity -ActivityEvents $ActivityEvents | Sort-Object -Property TimeCreated -Descending | Select-Object -First $ActivityLimit)
If ($OutputEvents.Count -ge $ActivityLimit -or (($WinlogonEvents.Count -lt $SearchLimit) -and ($SecurityEvents.Count -lt $SearchLimit) -and ($UnlockAuthEvents.Count -lt $SearchLimit) -and ($TerminalServicesEvents.Count -lt $SearchLimit) -and -not $PowerFiltersMayHaveMoreEvents) -or $SearchLimit -ge $MaxSearchLimit) {
Break
}
$SearchLimit = [Math]::Min(($SearchLimit * 2), $MaxSearchLimit)
} While ($true)
Write-UserLogonActivityOutput -ActivityEvents $OutputEvents
Write-PossibleUseWindowOutput -ActivityEvents $OutputEvents
}