Search code examples
powershellloopstimehashtableduration

Powershell - Time Duration Between Hashtable Objects


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


Solution

  • 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".