eventID, find specific
Get-EventLog -Log "Application" -After (Get-Date -Date '7/28/2018') | where {($_.eventID -eq 1000) -or ($_.eventID -eq 1026 -or ($_.eventID -eq 1325))}
exported event log, parse
Trying to parse log files remotely on an overworked AD server can take hours. Sometimes faster results to export the whole log file (or a filtered version) to a file on your local PC.
$events4776FromTable
=
Get-WinEvent
-FilterHashtable
@{
Path="$([environment]::getfolderpath("mydocuments"))\2019-06-28 MyADServer
Security Logs.evtx"
id=4776
}
Timing it:
(Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime # 7 minutes, 21 seconds
Now we can export to CSV for analysis:
$events4776AD11FromTable | Export-Csv EventID4776_2019-06-28a.csv
locked out user, see user locked out
log types, see list of all the different
Get-WinEvent -ListLog *
or perhaps all the different available logs that actually have something potentially useful in them
Get-WinEvent -ListLog * | ? {$_.RecordCount -gt 0} | sort RecordCount -Descending
logon for user failure, see user logon failure - see also 4776 Event ID
OS update fails
$query = @"
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[Provider
[@Name='Microsoft-Windows-WindowsUpdateClient']
and (Level=2) and Task = 1
and (band(Keywords,8200))]]</Select>
</Query>
</QueryList>
"@
$i=0
Foreach($event in
$systemEvents)
{
$systemEvents[$i]
$i++
}
The level=2 specifies failure.
This is a simplified version of what I couldn't get to work here
Security log failures
This finds all audit failures. I cap it at the most recent 2000 so it won’t take forever. In order to get more detail on “Replacement Strings”, concatenate them (separating with “;”). If you don’t, the hash elements won’t show up in the .csv file properly
Get-Eventlog -LogName security -Newest 2000 | where {($_.EntryType -eq 'FailureAudit') } | Select-Object index, TimeGenerated, InstanceID, message, @{L='ReplacementStrings'; E = { $_.ReplacementStrings -join ";"}} | Export-Csv C:\Users\user\Documents\SecurityLogAuditFailures.csv
This doesn’t filter for any EventIDs (“InstanceID”). This might return a whole bunch of EventID 5157 (DNS). To focus on some other EventIDs:
Get-Eventlog -LogName security -Newest 2000 | where {($_.EntryType -eq 'FailureAudit') -and (($_.InstanceID -eq 4625) -or ($_.InstanceID -eq 4656))} | Select-Object index, TimeGenerated, message, @{L='GUID'; E={$_.ReplacementStrings[0]}}, @{L='name'; E={$_.ReplacementStrings[1]}}, @{L='domain'; E={$_.ReplacementStrings[2]}}, @{L='someHexValue'; E={$_.ReplacementStrings[3]}}, @{L='someOtherGUID'; E={$_.ReplacementStrings[4]}} | Export-Csv C:\Users\user\Documents\SecurityLogAuditFailures2.csv
types of logs and how many entries in each
Get-EventLog -List
Get-WinEvent -ListLog * | where {$_.RecordCount -gt 0}
Find all events in last hour
Get-WinEvent -Logname Security -FilterXPath "*[System[EventID=4740 and TimeCreated[timediff(@SystemTime) <= 3600000]] and EventData[Data[@Name='TargetUserName']='someUser']]" | Select-Object TimeCreated,@{Name='User Name';Expression={$_.Properties[0].Value}},@{Name='Source Host';Expression={$_.Properties[1].Value}}
Find all lockout events for a user on a couple DCs:
# Get all security log events for one user from 2 DCs, put the
events into one object lining up all the like properties
$user
=
"someUser"
$ADServers
= ("DC1","DC2")
$objResult
=
@()
foreach
($ADServer
in
$ADServers) {
$eventsRaw
=
Get-WinEvent
-ComputerName
$ADServer
-FilterHashtable
@{LogName='Security';Data=$user}
$i
=
0
$eventsRaw
|
foreach
{
"$ADServer
-
$i
$($_.ID) - $($eventsRaw[$i].RecordID)"
switch
($_.ID)
{
4624
# Kerberos success
{
$eventsParsed
=
$eventsRaw[$i] | Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID,
TimeCreated,@{n='Description';e={'Logon'}},@{n='AccountName';e={$_.Properties[5].value}},@{n='Service Name';e={''}},@{n='Ticket Options';e={''}},@{N='Failure Code';E={''}},@{N='Pre-Authentication Type';E={''}},@{N='IP Address';E={''}},@{N='Port';E={''}}
$objResult
+=
$eventsParsed
}
4627
# Kerberos success
{
$eventsParsed
=
$eventsRaw[$i] |
Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID,
TimeCreated,@{n='Description';e={'Group Membership'}},@{n='AccountName';e={$_.Properties[5].value}},@{n='Service Name';e={''}},@{n='Ticket Options';e={''}},@{N='Failure Code';E={''}},@{N='Pre-Authentication
Type';E={''}},@{N='IP Address';E={''}},@{N='Port';E={''}}
$objResult += $eventsParsed
}
4634
# An account was logged off
{
$eventsParsed
=
$eventsRaw[$i] | Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID,
TimeCreated,@{n='Description';e={'Logoff'}},@{n='AccountName';e={$_.Properties[1].value}},@{n='Service Name';e={''}},@{n='Ticket Options';e={''}},@{N='Failure Code';E={''}},@{N='Pre-Authentication Type';E={''}},@{N='IP Address';E={''}},@{N='Port';E={''}}
$objResult += $eventsParsed
}
4672
# special privileges
{
$eventsParsed = $eventsRaw[$i] | Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID, TimeCreated,@{n='Description';e={'special privileges'}},@{n='AccountName';e={$_.Properties[1].value}},@{n='Service Name';e={''}},@{n='Ticket Options';e={''}},@{N='Failure Code';E={''}},@{N='Pre-Authentication Type';E={''}},@{N='IP Address';E={''}},@{N='Port';E={''}}
$objResult += $eventsParsed
}
4740
# locked out
{
$eventsParsed
= $eventsRaw[$i] |
Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID, TimeCreated,@{n='Description';e={'locked out'}},@{n='AccountName';e={$_.Properties[0].value}},@{n='Service Name';e={''}},@{n='Ticket Options';e={''}},@{N='Failure Code';E={''}},@{N='Pre-Authentication Type';E={'LOCKED!'}},@{N='IP Address';E={''}},@{N='Port';E={''}}
$objResult += $eventsParsed
}
4767
# User Account Management (unlock)
{
$eventsParsed = $eventsRaw[$i] | Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID,
TimeCreated,@{n='Description';e={'Unlocked'}},@{n='AccountName';e={$_.Properties[0].value}},@{n='Service Name';E={''}},@{n='Ticket Options';e={''}},@{N='Failure Code';E={''}},@{N='Pre-Authentication Type';E={'unlock'}},@{N='IP Address';E={''}},@{N='Port';E={''}}
$objResult += $eventsParsed
}
4768
# Kerberos success
{
$eventsParsed = $eventsRaw[$i] | Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID,
TimeCreated,@{n='Description';e={'Kerberos success'}},@{n='AccountName';e={$_.Properties[0].value}},@{n='Service Name';e={$_.Properties[3].value}},@{n='Ticket Options';e={$_.Properties[5].value}},@{N='Failure Code';E={$_.Properties[6].value}},@{N='Pre-Authentication
Type';E={$_.Properties[8].value}},@{N='IP Address';E={$_.Properties[9].value}},@{N='Port';E={$_.Properties[10].value}}
$objResult += $eventsParsed
}
4769
# Kerberos success
{
$eventsParsed = $eventsRaw[$i] | Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID,
TimeCreated,@{n='Description';e={'Kerberos service ticket requested'}},@{n='AccountName';e={$_.Properties[0].value}},@{n='Service Name';e={$_.Properties[3].value}},@{n='Ticket Options';e={$_.Properties[5].value}},@{N='Failure Code';E={$_.Properties[6].value}},@{N='Pre-Authentication
Type';E={$_.Properties[8].value}},@{N='IP Address';E={$_.Properties[9].value}},@{N='Port';E={$_.Properties[10].value}}
$objResult += $eventsParsed
}
4771
# Kerberos failure
{
$eventsParsed = $eventsRaw[$i] | Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID,
TimeCreated,@{n='Description';e={'Kerberos failure'}},@{n='AccountName';e={$_.Properties[0].value}},@{n='Service Name';e={$_.Properties[2].value}},@{n='Ticket Options';e={$_.Properties[3].value}},@{N='Failure Code';E={$_.Properties[4].value}},@{N='Pre-Authentication
Type';E={$_.Properties[5].value}},@{N='IP Address';E={$_.Properties[6].value}},@{N='Port';E={$_.Properties[7].value}}
$objResult += $eventsParsed
}
4772
# Special Logon - I think this happens when a sys admin logs
onto a domain server and his privileges are elevated
{
$eventsParsed = $eventsRaw[$i] | Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={''}}, RecordID, ID,
TimeCreated,@{n='Description';e={'Special Logon'}},@{n='AccountName';e={$_.Properties[1].value}},@{n='Service Name';e={''}},@{n='Ticket Options';e={''}},@{N='Failure Code';e={''}},@{N='Pre-Authentication Type';E={''}},@{N='IP Address';E={''}},@{N='Port';E={''}}
$objResult += $eventsParsed
}
4776
# Credential Verification
{
$eventsParsed = $eventsRaw[$i] | Select-Object
@{n='server';e={$ADServer}},@{n='Source Workstation';e={$_.Properties[2].value}},
RecordID, ID, TimeCreated,@{n='Description';e={'Credential Verification'}},@{n='AccountName';e={$_.Properties[1].value}},@{n='Service Name';e={''}},@{n='Ticket Options';e={''}},@{N='Failure Code';E={$_.Properties[3].value}},@{N='Pre-Authentication
Type';E={''}},@{N='IP Address';E={''}},@{N='Port';E={''}}
$objResult += $eventsParsed
}
default
{
if(!$default)
{
$default=$true
}
"$i eventID $($eventsRaw[$i].ID) cannot be parsed."
}
}
$i++
}
}
$objResult.Count
$objResult | ogv
$objResult | Export-Csv -NoTypeInformation "$([environment]::GetFolderPath("mydocuments"))\$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss'))_$($ADServer)_SecurityEvents.csv"
user logon failure - see also 4776 Event ID
Find a bunch of logon failure events and stuff them into a variable. I choose 1000 below. It takes a bit of time to gather them all together. But not nearly as much time as if you instead chose to try to immediately pipe the results into further refining commands. Trying it that way takes much longer. Especially if you want to filter on a particular element (such as a particular user) of an embedded array like we do right after this command.
$events = Get-EventLog -logname Security -InstanceId 4771 -Newest 1000
Now that we've gathered all the logon failures into a variable, focus on one individual.
The ReplacementStrings
property is an array. We're picking apart various elements
to display.
$events | where {$_.replacementstrings[0] -eq "someUser"} | Select-Object TimeGenerated,@{n='AccountName';e={$_.ReplacementStrings[0]}},@{n='Service Name';e={$_.ReplacementStrings[2]}},@{n='Ticket Options';e={$_.ReplacementStrings[3]}},@{N='Failure Code';E={$_.ReplacementStrings[4]}},@{N='Pre-Authentication Type';E={$_.ReplacementStrings[5]}},@{N='IP Address';E={$_.ReplacementStrings[6]}},@{N='Port';E={$_.ReplacementStrings[7]}},entrytype | ogv
If instead we want to look at all the failures, not just one ID.
So, start with the same statement as above but without that initial where
statement
$eventsParsed = $events | Select-Object TimeGenerated,@{n='AccountName';e={$_.ReplacementStrings[0]}},@{n='Service Name';e={$_.ReplacementStrings[2]}},@{n='Ticket Options';e={$_.ReplacementStrings[3]}},@{N='Failure Code';E={$_.ReplacementStrings[4]}},@{N='Pre-Authentication Type';E={$_.ReplacementStrings[5]}},@{N='IP Address';E={$_.ReplacementStrings[6]}},@{N='Port';E={$_.ReplacementStrings[7]}},entrytype
We're usually interested in how things vary by
- Account name
- Error code (or 'Pre-Authentication Type') - usually a 0 (locked out) or 2 (login failure)
- IP Address
So group by these.
$eventsParsedAndGrouped = $eventsParsed | Group -Property AccountName, "Pre-Authentication Type", "IP Address"
Now that it's grouped, we want summary info on
- how many failed login attempts
- when was the most recent failed attempt
I have yet to figure out how to do both these at once. So, let's start with count
$eventsParsedByGroup = $eventsParsedAndGrouped | Select-Object Count,@{n='AccountName';e={$_.Name.split(",")[0]}},@{n= Pre-Authentication Type;e={$_.Name.split(",")[1]}},@{n='IPAddress';e={if (($_.Name.split(",")[2]).contains(":")) {($_.Name.split(",")[2]).split(":")[3]} else {$_.Name.split(",")[2]}}} | sort AccountName, ErrorCode, IPAddress
Send it to a grid view
$eventsParsedByGroup | ogv
or to a CSV for further processing
$eventsParsedByGroup | Export-Csv "$([environment]::GetFolderPath("mydocuments"))\AD10_LoginError_count_$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).csv"
Now, show the most recent failure for each account/IP/error code combo
$eventsParsedByTime = $eventsParsedAndGrouped | Foreach {$_.Group | Sort TimeGenerated -Descending | Select TimeGenerated,AccountName, "Pre-Authentication Type",@{n='IPAddress';e={($_."IP Address").split(":")[3]}} -First 1} | sort AccountName, ErrorCode, IPAddress
Send it to a grid view
$eventsParsedByTime | ogv
or to a CSV for further processing
$eventsParsedByTime | Export-Csv "$([environment]::GetFolderPath("mydocuments"))\AD10_LoginError_Time_$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).csv"
In my case, I wanted to show count and time - and also stitch these together from two different DCs. Ended up exporting the CSV files and using a full outer join in a database.
user's name in log entries, find
Here I want to find users who are having problems logging in
This works and returns records that include the user name right away. But if I omit
the First 5
clause, it still returns records (often fewer than 5) but takes a lot
longer.
Get-EventLog -Log "Security" -After ((Get-Date).AddMinutes(-5)) -InstanceId 4771 | Select-Object -First 5 TimeGenerated,@{n='AccountName';e={$_.ReplacementStrings[0]}},entrytype,message | ft
Here I try to filter on the user name. Even though this ostensibly looks back
through only 5 minutes' worth of logs, it still takes forever. And it doesn't even return any records. Even though I get hits on this user when I omit the where
and First
clauses
Get-EventLog -Log "Security" -After ((Get-Date).AddMinutes(-5)) -InstanceId 4771 | where {$_.replacementstrings[1] -eq "someUser"} | Select-Object -First 5 TimeGenerated,@{n='AccountName';e={$_.ReplacementStrings[0]}},entrytype,message | ft
And don't even think about trying to put the First 5
clause before the
where
clause to try to speed things up; you won't get any records returned because
once you put in the select
it wrecks the object.
Putting in a Newest
clause up front doesn't speed things up.
Get-EventLog -Log "Security" -newest 500 -After ((Get-Date).AddMinutes(-5)) -InstanceId 4771 | where {$_.replacementstrings[1] -eq "someUser"} | Select-Object TimeGenerated,@{n='AccountName';e={$_.ReplacementStrings[0]}},entrytype,message | ft
Gathering records into a variable first and then applying a where
clause
seems to work a lot faster
$events = Get-EventLog -logname Security -InstanceId 4771 -Newest 5000
$events | where {$_.replacementstrings[0] -eq "someUser"} | Select-Object TimeGenerated,@{n='AccountName';e={$_.ReplacementStrings[0]}},entrytype,message | ft
try this which goes against the live Event Log on the Active Directory server
$events4776
=
Get-WinEvent
-ComputerName AD1 -FilterHashtable
@{logname='security';id=4776}
# 1hr 18min
$events4776
|
Export-Csv
EventID4776_2019-06-28b.csv # 4hr 34min
if I export the event log, download to my PC, much faster
$events4776
=
Get-WinEvent
-FilterHashtable
@{
Path="$([environment]::getfolderpath("mydocuments"))\AD1SecurityLogs.evtx"
id=4776
} # same data: 7 minutes, 21 seconds
now that we have the events, analyze
$events4776Array
=
@()
for
($i =0;
$i
-lt
$events4776.count;
$i++){
$obj
=
New-Object
psobject
$obj
|
Add-Member
span style='color:#D4D4D4'> -MemberType noteProperty -Name TimeCreated -Value
$events4776[$i].TimeCreated
$obj
|
Add-Member
-MemberType noteProperty -Name AuthenticationPackage -Value
$events4776[$i].Properties[0].value
$obj
|
Add-Member
-MemberType noteProperty -Name AccountName -Value
$events4776[$i].Properties[1].value
$obj
|
Add-Member
-MemberType noteProperty -Name SourceWorkstation -Value
$events4776[$i].Properties[2].value
$obj
|
Add-Member
-MemberType noteProperty -Name FailureCode -Value
$events4776[$i].Properties[3].value
$events4776Array
+=
$obj
}
export to grid & file for further examination/analysis
$events4776Array
| ogv
$events4776Array
|
Export-CSV
-Path
"$([environment]::getfolderpath("mydocuments"))\4776events$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).csv"
-NoTypeInformation