I am trying to create a time duration between objects in a hashtable. So, i am pulling the eventlogs to show when the machine was locked and unlocked:
$Unlocks = Get-WinEvent -FilterHashTable @{LogName = 'Security'; ID = '4801' }
$Locks = Get-WinEvent -FilterHashTable @{LogName = 'Security'; ID = '4800' }
$AllEvents = $Unlocks + $Locks
$OrderedEvents = $AllEvents | Sort-Object TimeCreated
This gives me this output:
TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
24/07/2020 07:41:06 4800 Information The workstation was locked.…
24/07/2020 07:42:27 4801 Information The workstation was unlocked.…
24/07/2020 07:58:56 4800 Information The workstation was locked.…
24/07/2020 07:59:13 4801 Information The workstation was unlocked.…
24/07/2020 09:36:01 4800 Information The workstation was locked.…
24/07/2020 09:41:50 4801 Information The workstation was unlocked.…
24/07/2020 10:30:42 4800 Information The workstation was locked.…
24/07/2020 10:31:25 4801 Information The workstation was unlocked.…
24/07/2020 12:22:51 4800 Information The workstation was locked.…
24/07/2020 12:35:55 4801 Information The workstation was unlocked.…
24/07/2020 13:16:07 4800 Information The workstation was locked.…
24/07/2020 13:27:28 4801 Information The workstation was unlocked.…
So i would like to add the duration between the events.
For example,
TimeCreated Id LevelDisplayName Message Duration
----------- -- ---------------- ------- --------
24/07/2020 07:41:06 4800 Information The workstation was locked.…
24/07/2020 07:42:27 4801 Information The workstation was unlocked.… 1min 21sec
24/07/2020 07:58:56 4800 Information The workstation was locked.… 16min 29sec
Obviously the first entry would not have a time. The duration is looking at the previous entry. The format of the time isnt important as i will convert it later.
When i search for ways to resolve this, the results dont quite do what i need, but i am sure this is probably me not using the right words.
I tried this, but it didnt provide what i wanted, nor did i properly understand it:
$OrderedEvents | ForEach-Object { $_.TimeCreated = [datetime]$_.TimeCreated; $_ } |
Group-Object Id |
ForEach-Object { $_.Group | Sort-Object TimeCreated }
That just provided me the two latest entries.
Any help or pointers would be greatly appreciated.
Many Thanks POC
There are a couple of ways of addressing this problem. The primary piece your missing is a variable to hold the previous entry.
$last = $null
$OrderedEvents | ForEach-Object {
$duration = [timespan]0
$timeStamp = [datetime]$_.TimeCreated
if ($null -ne $last) {
$duration = $last - $timeStamp
}
$_ | Add-Member -MemberType NoteProperty -Name Duration -Value $duration -PassThru
$last = $timeStamp
} | Select-Object -Property Duration, TimeCreated, Id, LevelDisplayName, Message
I added a $last
variable to hold the timestamp from the previous entry. I also used Add-Member to add a new Duration
property to the existing object. Duration
will be a [Timespan]
object which will likely look something like 00:05:00.0046918
when written to the console. If you want more human-readable values, you could use PowerShellHumanizer by changing $_ | Add-Member -MemberType NoteProperty -Name Duration -Value $duration -PassThru
to $_ | Add-Member -MemberType NoteProperty -Name Duration -Value $duration.Humanize() -PassThru
which would change Duration to a string like "5 minutes, 4 milliseconds".