Query Security Log Using Powershell
I’ve just completed a script that will parse the Windows Security Event log for Event ID’s of type 4624 (user logons). Once the events have been retrieved the script then creates and outputs a custom object populated with the following properties:
- Account Name
- DateTime
- Type ( Interactive,Network,Unlock)
The script is composed of 2 functions:
- Find-Matches
- Query-SecurityLog
Query-SecurityLog is the main function and is responsible for creating and outputing the custom object. Find-Matches is a helper function used to parse the event log message property for the following pattern: “Account Name: SomeUserName”. Prior to incorporating the Find-Matches function I had used the -Match operator but then realized that pattern was referenced twice in the message and that the -Match operator returns only the first matching expression. So, if you need to find more than one occurrence of a pattern in raw text, you have to switch over to the RegEx object underlying the -Match operator and use it directly.
This left me with 2 possible option that could be used to discover one or more matching values:
- Select-String (as used in Find-Matches)
- [regex]
The Find-Matches function was something I already had in my repository of scripts so I went that route, however after reading Chapter 13: Text and Regular Expression in the Powershell.com ebook I realized this could of been done with less code by using [regex].
$Pattern = [regex]"(?i)\baccount name:\s+\w+\b" $Content = $Pattern.matches($i.message) | select -prop value | %{$_.value}
The example below demonstrates how Select-String is used in the Find-Matches function to output all matching patterns:

I then used an if\else statemtment block to determine how many matches were returned and then set the $account parameter accordingly.
if($content.Count -eq 2) { $account = $content[1]} else {$account = $content }
I was also suprised to see just how many non-user account logon events were recorded and decided to remove any events that referenced the accounts below:
- System
- IUSER
- LOCAL
- NETWORK
- $ENV:Computername
As well as filter the events based on the following Logon Types and assign the $logontype variable accordingly:
- Interactive (2)
- Network (3)
- Computer Unlocked (7)
This was done using the following code:
$notmatch = "System|IUSR|LOCAL|NETWORK" if($account -notmatch $notmatch) { if($i.Message | Select-String -Pattern "Logon Type:\s+[2]") { $logontype = "Interactive" } if($i.Message | Select-String -Pattern "Logon Type:\s+[3]") { $logontype = "Network" } if($i.Message | Select-String -Pattern "Logon Type:\s+[7]") { $logontype = "Computer Unlocked" } }
It was then just a matter of creating and populating the custom object. There are 2 ways to populate an object once created. One way is to create the object and then use the Add-Member cmdlet to populate it and the other is to use the -Property property along with splatting ( @{} ).
- $Obj | Add-Member -Noteproperty NameOfProperty ValueOfProperty
- -Property @{ NameOfProperty = “Value” }
I choose to go with the Splatting option as it requires less code.
$obj = New-Object PSObject -Property @{ User = $user Date = $Date LogonType = $LogonType }
The end result was the following code.
Function Find-Matches { Param($Pattern) Process { $_ | Select-String -pattern $Pattern -AllMatches | select -ExpandProperty matches | select -ExpandProperty value } } Function Query-SecurityLog { Param( [int]$MaxEvents, [array]$global:Users = @(), [string]$Comp = $env:computername, $notmatch = "System|IUSR|LOCAL|NETWORK" ) if($MaxEvents) { $events = Get-WinEvent -LogName security -MaxEvents $MaxEvents | Where-Object{$_.id -eq "4624"} } Else { $events = Get-WinEvent -LogName security | Where-Object{$_.id -eq "4624"}} Foreach($i in $events) { $content = $i.message| Find-Matches -Pattern "account name:\s+\w+" if($content.Count -eq 2) { $account = $content[1]} else {$account = $content } $account = (($account -split ":")[1]) -replace "\s+","" if($account -notmatch $notmatch) { if($i.Message | Select-String -Pattern "Logon Type:\s+[2]") { $logontype = "Interactive" } if($i.Message | Select-String -Pattern "Logon Type:\s+[3]") { $logontype = "Network" } if($i.Message | Select-String -Pattern "Logon Type:\s+[7]") { $logontype = "Computer Unlocked" } $user = $account $Date = $i.TimeCreated $obj = New-Object PSObject -Property @{ User = $user Date = $Date LogonType = $LogonType } $Global:Users += $Obj } } write-output $Global:Users | Select User,Date,LogonType | Sort Date -Descending | Format-Table -Auto } Query-SecurityLog -MaxEvents 1000
The output of Query-SecurityLog is:
