2

I've been working on a script to pull logon/logoff history out of the Event logs. The issue is that almost every code example I found uses "Get-EventLog" which does work, but is extremely slow processing due to the event logs found on a server being a lot larger then a typical workstation. So instead I opted for "Get-WinEvent", as it provides very similar results but returns the results within a few seconds. The issue I am having though is manipulating the output of said results into a usable form.Any assistance is GREATLY appreciated!

Update: I was able to slowly work through the issue on my own. See comments for details. Thanks for anyone who was looking into this :)

Original Working Code (Get-EventLog - Slow Output)

function get-logonhistory{
 cls
 $Result = @()
 Write-Host "Gathering Event Logs, this can take awhile..."
 $ELogs = Get-EventLog System -Source Microsoft-Windows-WinLogon
 If ($ELogs)
 { Write-Host "Processing..."
 ForEach ($Log in $ELogs)
 { If ($Log.InstanceId -eq 7001)
   { $ET = "Logon"
   }
   ElseIf ($Log.InstanceId -eq 7002)
   { $ET = "Logoff"
   }
   Else
   { Continue
   }
   $Result += New-Object PSObject -Property @{
    Time = $Log.TimeWritten
    'Event Type' = $ET
    User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])
   }
 }
 $Result | Select Time,"Event Type",User | Sort Time -Descending | Out-GridView
 Write-Host "Done."
 }
 Else
 { Write-Host "There was a problem reading the logs..."
 }
}


get-logonhistory

(Get-WinEvent) - Almost Instant Results

function get-logonhistory{
 cls
 $Result = @()
 Write-Host "Gathering Event Logs, this can take awhile..."
 $ELogs = Get-WinEvent -ea SilentlyContinue ` -ProviderName “Microsoft-Windows-Winlogon”| Where-Object { $_.TimeCreated -le [datetime]::today}
 If ($ELogs)
 { Write-Host "Processing..."
 ForEach ($Log in $ELogs)
 { If ($Log.Id -eq 7001)
   { $ET = "Logon"
   }
   ElseIf ($Log.Id -eq 7002)
   { $ET = "Logoff"
   }
   Else
   { Continue
   }
   $SID = $Log.Properties.Value.Value
   $objSID = New-Object System.Security.Principal.SecurityIdentifier $SID
   $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
   $Result += New-Object PSObject -Property @{
    Time = $Log.TimeCreated
    'Event Type' = $ET
    User = $objUser.Value
   }
 }
 $Result | Select Time,"Event Type",User | Sort Time -Descending | Out-GridView
 Write-Host "Done."
 }
 Else
 { Write-Host "There was a problem reading the logs..."
 }
}


get-logonhistory
HerrtheGeek
  • 51
  • 1
  • 5
  • So... what is the output? What's not working as expected? Do you get any errors? If so, please share the full error messages. Keep in mind that you're the only one who can see your screen :) – Mathias R. Jessen Nov 29 '19 at 17:01
  • Well I actually solved my own issue...it turns out it was the "$Log.ReplacementStings[]. Get-WinEvent and Get-EventLog have a different array of ReplacementStrings. The one I was calling was empty hence why I received scrolling "Cannot add to Null Array" errors. I have updated the code sniplet above to function properly for those who want it. This returns results ALOT faster then the other ways I have seen. – HerrtheGeek Nov 29 '19 at 17:18
  • Well I spoke to soon...still didn't use the right replacement sting to find the actual user that performed the logoff/logon event. But we are getting closer :) – HerrtheGeek Nov 29 '19 at 17:26
  • Update: It looks like Get-WinEvent actually uses ".Properties" as opposed to ".ReplacementStrings". Still trying to figure out the right code to extract the SID from those properties though, and then convert it to a usable name/ID – HerrtheGeek Nov 29 '19 at 17:36
  • Solved! Turns out I had to jump through a few hoops to extract the SID/Account details out of the Get-WinEvent commandlet as opposed to Get-EventLog. The results are still the same. Get-WinEvent returns the data way faster then Get-EventLog. – HerrtheGeek Nov 29 '19 at 17:55

1 Answers1

3

Get-WinEvent and Get-EventLog use different arrays to store the details of an event log. Get-WinEvent users "Properties" and Get-EventLog Users "ReplacementStrings". By converting each to JSON your able to see the exact details of each, and locate the data your looking for. In this case it's the SID of the account that performed the event. Once this is obtain we can translate that SID into a Username and feed it into the results. Grid-View was opted for better usablity so you can filter the results if needed (i.e. wanting events for a specific user). Below is the final code;

function get-logonhistory{
 cls
 $Result = @()
 Write-Host "Gathering Event Logs, this can take awhile..."
 $ELogs = Get-WinEvent -ea SilentlyContinue ` -ProviderName “Microsoft-Windows-Winlogon”| Where-Object { $_.TimeCreated -le [datetime]::today}
 If ($ELogs)
 { Write-Host "Processing..."
 ForEach ($Log in $ELogs)
 { If ($Log.Id -eq 7001)
   { $ET = "Logon"
   }
   ElseIf ($Log.Id -eq 7002)
   { $ET = "Logoff"
   }
   Else
   { Continue
   }
   $SID = $Log.Properties.Value.Value
   $objSID = New-Object System.Security.Principal.SecurityIdentifier $SID
   $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
   $Result += New-Object PSObject -Property @{
    'Date/Time' = $Log.TimeCreated
    'Event Type' = $ET
    User = $objUser.Value
   }
 }
 $Result | Select "Date/Time","Event Type",User | Sort Time -Descending | Out-GridView -Title "System Logon Events"
 Write-Host "Done."
 }
 Else
 { Write-Host "There was a problem reading the logs..."
 }
}


get-logonhistory
HerrtheGeek
  • 51
  • 1
  • 5