Till now I never used PowerShell, but now I have to. (Don’t worry I know how to program in other languages.)
What I have to do:
There are 2 eventlogs:
“EventID equals 3317 and Event Level equals Error" and "EventID equals 3317 and Event Level equals Information"
If there is an event with the level “Error” without an event with the level “Information” afterwards, then the output has to be “Critical”.
If there is an event with the level “Information” after this event with the level “Error” the output has to be “Ok”.
(If there is no event with that ID everything is "Ok".)
Now the problem is that I don’t know how to compare both date values I get from those lines...
get-eventlog -log application -EntryType error –newest 1 | where {$_.eventID -eq 3317}
get-eventlog -log application -EntryType information –newest 1 | where {$_.eventID -eq 3317}
Other solutions are appreciated as well. ;)
Greetings,
Cédric
EDIT (The code):
#Error:
$e_error = (get-eventlog -log application -EntryType error | where {$_.eventID -eq 3317} | Select -First 1).TimeGenerated
write-host "Error: $e_error"
#Information:
$e_info = (get-eventlog -log application -EntryType information | where {$_.eventID -eq 3317} | Select -First 1).TimeGenerated
write-host "Information: $e_info"
if (($e_error) -and ($e_info)) { #If $e_error (Error) & $e_info (Information) are not empty (events exists)
$timediff = (new-timespan –start $e_error -end $e_info).TotalSeconds #Difference
if ($timediff -gt 0) { #If $e_info (Information) newer than $e_error (Error)
$res = "Ok"
} else { #If one of them or both are empty
$res = "Critical"
}
} else {
if (($e_error) -and (!($e_info))) { #If $e_error (Error) exists but not $e_info (Information)
$res = "Critical"
} else { #If non of both or only Information exists
$res = "Ok"
}
}
write-host $res
The TimeWritten or TimeGenerated values will give you dates and times to compare...
get-eventlog -log application -EntryType error –newest 1 | where {$_.eventID -eq 3317} | Select TimeGenerated, TimeWritten
You can also do this to just pull that attribute
(get-eventlog -log application -EntryType error –newest 1 | where {$_.eventID -eq 3317}).TimeGenerated
The other recommendation I'd make is to filter the events prior to selecting the last error. Otherwise you may miss an entry if another error occurs in quick succession. So the basis of your query should look like this:
get-eventlog -log application -EntryType error | where {$_.eventID -eq 3317} | | Select -First 1
Related
I have a powershell command value for a process field that I am trying to match in my splunk search but I am getting
"Error in 'SearchParser': Subsearches are only valid as arguments to commands. Error at position '3271' of search query 'litsearch (index=notable ((sourcetype="WMI:LocalPr...{snipped} {errorcontext = andLine"=[[Decoded Ba}'.":
`macro` rule_name="rule-name" process="[[Decoded Base64]] -LogName Microsoft-Windows-Diagnostics-Performance/Operational
| Where-Object {$_.Id -eq 100 -and $_.TimeCreated -ge ((Get-Date) - (New-TimeSpan -Day 90))}
| Format-list ;<!LF!>$ImpersonatedUser.ImpersonationContext.Undo()<!LF!>"
I need a solution for grabbing every instance that this value occurs
How can I get the total number of Event ID error 4625 and for each and every Windows Server in my AD domain using the below Powershell script?
$DCServers = Get-ADDomainController -filter * | select -ExpandProperty hostname
$events = #()
$totalCt = 0
$servers = #()
Foreach ($Server in $DCServers)
{
Write-Host "Calling Get-WinEvent for $Server"
$serverEvents = Get-WinEvent -ComputerName $Server -FilterHashtable #{ logname = 'Security'; id = 4625 } -EA 0
if (!$?)
{
Write-Host "Get-WinEVent failure for $Server"
continue
}
if ($null -ne $serverEvents)
{
$totalCt += $serverEvents.Count
$servers += [PsCustomObject] #{ $server = $serverEvents.Count }
Write-Host $server $serverEvents.Count
}
$serverEvents | ForEach-Object {
$events += [PsCustomObject] #{
Date = $_.TimeCreated
"Event Id" = $_.Id
"User Name" = $_.Properties[6].Value + "\" + $_.Properties[5].Value ## fixed
"IPAddress" = $_.Properties[21].Value
"FailureReason" = (($_.message -split "\n") | Select-String -Pattern "Failure Reason:\s+(.+)").matches[0].groups[1].value
"Status Code" = $_.message -split '\s{4}' | Select-String -Pattern "Status"
"Logon Type" = $_.Properties[10].Value
"DC Logged On" = $_.Properties[13].value ## this is "workstation that processed the request", not the DC Logged On
}
}
}
$HTML = '<h1>Head</h1>'
$GetDate = Get-Date
$Report = 'C:\clu\temp-4625.html'
#convert the array of events to HTML
$Events |
Select-Object Date, "Event Id", "User Name", "FailureReason", "Status Code", "DC Logged On", "Logon Type" |
Convertto-html -head $HTML -PreContent "<H2>Accounts that Failed to Log On</H2>", "<H2>$GetDate </H2>" -PostContent "<p></p>Total 4625 records: $totalCt <p></p>" |
Out-File $Report -append
Write-Host "Total 4625 records: $totalCt"
Write-Host "4625 records per server:"
$servers | ft -auto
Write-Host "4625 records grouped by user"
$events | group "User Name" | sort Count
The goal is to see which servers have Event 4625 and group it by the content to see which IP or AD account failed logins where possible?
Before digging into how to extract the workstation IP address and how to group the events by specific properties, let me suggest rewriting your existing code slightly, given your goal.
Doing $event = New-Object psobject |Select listOfPropertyNames and then assigning the values to each property separately is going to be slow - something I imagine you might want to avoid if you have many servers to query.
Since PowerShell 3.0, we can instantiate a new custom object in one go with the following syntax:
$newObject = [pscustomobject]#{
PropertyName = "Value"
}
So, refactoring your existing code, we end up with something like:
$events += [pscustomobject]#{
Date = $_.TimeCreated
"Event Id" = $_.Id
"User Name" = $_.Properties[5].Value + "\" + $_.Properties[6].Value
"FailureReason" = (($_.Message -split "\n") | Select-String -Pattern "Failure Reason:\s+(.+)").Matches[0].Groups[1].Value
"Status Code" = $_.Message -split '\s{4}' | Select-String -Pattern "Status"
"Logon Type" = $_logontype[ [int] $_.Properties[10].Value ]
"DC Logged On" = $_.Properties[13].Value
}
Another change we might wanna make is change the data type of $events - when you instantiate an empty array with #(), PowerShell will allow you to add new items to the array with +=, but there's a catch - arrays are of a fixed size, so if you keep adding new items via +=, PowerShell will have to stop and resize the array by creating a larger underlying array and then copying the existing array items into the new, larger array. This obviously takes some time and might incur unnecessary memory acquisition as well.
To work around this, use a list instead - lists are designed with dynamic sizing in mind, so will perform better even when you add a 1000s of items to it:
$events = New-Object 'System.Collections.Generic.List[psobject]'
Now, back to the question - how to group all the events by IP address - first of all, we need to extract the workstation IP address in order to me able to group on it later, so let's add an extra property to the custom object we created:
$events += [pscustomobject]#{
# ...
IPAddress = $_.Properties[21].Value
}
Now that the IPAddress is present, grouping the objects based on it is as simple as:
$events | Group-Object IPAddress
Note that you can also get these fields from the xml of the event:
$a = Get-WinEvent -Max 1 #{logname='Security'; id=4625}
$xml = [xml]$a.ToXml()
$xml.event.EventData.data
Name #text
---- -----
SubjectUserSid S-1-5-18
SubjectUserName COMP$
SubjectDomainName DOM
SubjectLogonId 0x3e7
TargetUserSid S-1-0-0
TargetUserName admin
TargetDomainName COMP
Status 0xc000006d
FailureReason %%2313
SubStatus 0xc000006a
LogonType 7
LogonProcessName User32
AuthenticationPackageName Negotiate
WorkstationName COMP
TransmittedServices -
LmPackageName -
KeyLength 0
ProcessId 0xa60
ProcessName C:\Windows\System32\svchost.exe
IpAddress 127.0.0.1
IpPort 0
Compare with .Properties
$a.properties
Value
-----
S-1-5-18
COMP$
DOM
999
S-1-0-0
admin
COMP
-1073741715
%%2313
-1073741718
7
User32
Negotiate
COMP
-
-
0
2656
C:\Windows\System32\svchost.exe
127.0.0.1
0
I am trying to create code in Powershell that will track the user login/logout times with the id codes of 7001 (login), and 42 (computer goes to sleep), and then export it as a csv.
My current problem is that sometimes the user will login/logout throughout the day, but I just want the earliest login and latest logout so I can track the total hours.
My current code works, but it gets every login/logout events of the day, seen below:
$startDate = (get-date).AddDays(-1)
$FileName = "Y:\Powershell_ " + $startDate.ToString('MMddyy') + ".csv"
$log_time = get-WinEvent -FilterHashtable #{logname='system';id='7001', '42'}
$log_time| Select Id, MachineName, Message, TimeCreated | export-csv $FileName
Thank you in advance
You have to filter by the day you working on first.
Since you can catch the output of a for each loop with a parameter, this is a way you can achieve your goal:
$startDate = (get-date).AddDays(-1).GetDateTimeFormats()[0]
$FileName = "somepath.csv"
$log_time = get-WinEvent -FilterHashtable #{logname='system';id='7001', '42'}
$daily_events = foreach($event in $log_time)
{
if ($event.timecreated.GetDateTimeFormats()[0] -like "*$startDate*"){$event}
}
$daily_events | Select Id, MachineName, Message, TimeCreated | export-csv -Path $Filename
You can add this to track about the first and last while execution:
#The sort is by default from newer to older
$first_event = $daily_events[-1].TimeCreated #last element of the array
$last_event = $daily_events[0].TimeCreated #first element of the array
Write-Host "First event was $first_event in and the last event was in $last_event"
You can use Get-Eventlog to make sure only events from the current day are retrieved. From there:
$Results =[System.Collections.Generic.List[PSObject]]::new()
$Date = (get-date).Date
$FirstLogin = Get-EventLog -LogName System -InstanceId 7001 -After $Date -Before $Date.AddDays(1) | Select -Last 1
$LastLogout = Get-EventLog -LogName System -InstanceId 42 -After $Date -Before $Date.AddDays() -Newest 1
$Results.Add($FirstLogin)
$Results.Add($LastLogout)
$Results | Select Id, MachineName, Message, TimeCreated | export-csv -Path $Filename
I have a large script which looks at certain event logs. Part of it is the following command:
Get-EventLog -ComputerName $computer -InstanceId 4625 -LogName Security -After $date -ErrorAction Stop | Select TimeWritten,#{n='Reason for Failure';e={$_.ReplacementStrings[8]}}
I receive the following output:
TimeWritten Reason for Failure
----------- ------------------
08/05/2018 10:55:06 %%2313
08/05/2018 09:19:24 %%2313
08/05/2018 07:49:22 %%2304
08/05/2018 07:49:22 %%2304
Is it possible to change the output in the reason for failure column to some other message. I know of the -replace operator but I am struggling on how to incorporate this?
This should get you headed in the right direction:
$failures = #{'%%2313' = 'Unknown User Name or Bad Password';
'%%2304' = 'An Error occured during Logon'
}
Get-EventLog -ComputerName $computer -InstanceId 4625 -LogName Security -After $date -ErrorAction Stop | Select TimeWritten,#{n='Reason for Failure';e={$failures[$_.Message]}}
Change $_.Message to be whichever field has the error code.
I'm trying to get unexpected shutdown times of Windows Sever 2008 machines via Get-EventLog in Powershell. I can get close by searching for events with an EventID of 6008 and selecting only message, but I need to parse within the field to grab the time it occurred (not the time the event fired).
I've tried to use replacementstrings[x] but I can't find how to specify the field to use (messages) and can't get a result.
get-eventlog -LogName System -ComputerName svr-name | Where-Object {$_.EventID -eq 6008 -AND $_.timegenerated -gt (get-date).adddays(-30)}| select message
Produces this:
Message
-------
The previous system shutdown at 3:35:32 AM on 7/29/2014 was unexpected.
The previous system shutdown at 3:40:06 PM on 7/10/2014 was unexpected.`
Retrieving all events from a remote host and filtering them on the local machine ususally doesn't perform too well, because that way you transmit tons of unrelated events over the network, just to throw them away. Get-EventLog has options for filtering messages by Event ID or before/after a given timestamp on the source, so better use those for pre-selecting the messages you're actually interested in. The timestamp of the crash can be extracted from the Message field with a regular expression and parsed into a DateTime value via ParseExact():
$log = 'System'
$server = 'svr-name'
$id = [uint64]"0x80000000" + 6008
$date = (Get-Date).AddDays(-30)
$fmt = 'h:mm:ss tt on M\/d\/yyyy'
$culture = [Globalization.CultureInfo]::InvariantCulture
Get-EventLog -LogName $log -ComputerName $server -InstanceId $id -After $date | ? {
$_.Message -match 'at (\d+:\d+:\d+ [ap]m on \d+/\d+/\d+) was unexpected'
} | select MachineName, TimeGenerated,
#{n='Crashtime';e={[DateTime]::ParseExact($matches[1], $fmt, $culture)}}
The pipeline produces a list of objects with the properties MachineName, TimeGenerated and Crashtime (the last one being a calculated property). If you collect the output of the pipeline in a variable (e.g. $evt) you can access the Crashtime property of the third object like this:
$evt = .\script.ps1
$evt[2].Crashtime
Using regex, you can pull it out as such.
$Messages = (get-eventlog -LogName System -ComputerName svr-name | Where-Object {$_.EventID -eq 6008 -AND $_.timegenerated -gt (get-date).adddays(-30) }| select message)
$Messages | ForEach-Object {
$Matched = $_.Message -match "([0-9]{1,2}:.*[0-9]{4})"
if ($Matched) {
Write-Output "System rebooted at $($Matches[1])"
}
}
There might be a better way, but I do not know what :)
Example Output from my System
System rebooted at 4:34:30 PM on 4/20/2014
System rebooted at 1:48:38 PM on 1/21/2014
System rebooted at 1:37:12 PM on 1/21/2014
System rebooted at 1:22:01 PM on 1/21/2014
System rebooted at 4:41:21 PM on 11/22/2013
More easy
get-eventlog system | where-object {$_.EventID -eq "6008"} | fl