0

looking for info on the below piece of code, it actually does what's expected of it. It retrieves the time duration of the 'CreateTimesheets' task for the past 7 days. However it finishes with an error message.

Get-WinEvent -FilterHashtable @{
     'LogName' = 'Microsoft-Windows-TaskScheduler/Operational'
     'ID'      = 200, 201
     'StartTime' = [datetime]::Today.AddDays(-7)
     'EndTime' = [datetime]::Today
 } | Group-Object ActivityID | ForEach-Object {
    if($_.Group.Properties[0].Value -like '*CreateTimesheets*'){
          $start = $_.Group |
              Where-Object { $_.Id -eq 200 } |
              Select-Object -Expand TimeCreated -First 1
           $end   = $_.Group |
              Where-Object { $_.Id -eq 201 } |
              Select-Object -Expand TimeCreated -First 1

           New-Object -Type PSObject -Property @{
            'TaskName'  = $_.Group[0].Properties[0].Value
            'StartTime' = $start
            'Duration'  = ($end - $start).TotalSeconds
    } }
 }

The error message is as follows

Cannot find an overload for "op_Subtraction" and the argument count: "2".
At line:15 char:12
+            New-Object -Type PSObject -Property @{
+            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

i'm quite new to powershell scripting and have found the same error message on other stack overflow questions but the scenarios seem quite different to this, could anyone clear this up for me? thanks

mcgovec9
  • 71
  • 7
  • Looks like either `$end` or `$start` is empty (or at least _not_ a `[datetime]` object) by the time you reach the `New-Object ...` statement. – Mathias R. Jessen Nov 02 '20 at 17:57

2 Answers2

1

You may want to clear out the $start and $end variables just to ensure values aren't being carried over from the previous group. This might reveal which record(s) are causing the error(s). I was not able to duplicate the issue. Depending on amount of entries you're processing it might also be beneficial to filter out the taskname prior to grouping. Finally, it's slightly faster to use the [PSCustomObject] type accelerator instead of New-Object. I've made a couple of adjustments to your code in the first example and then a slightly different version of the same thing following that.

Updated original

Get-WinEvent -FilterHashtable @{
     'LogName'   = 'Microsoft-Windows-TaskScheduler/Operational'
     'ID'        = 200, 201
     'StartTime' = [datetime]::Today.AddDays(-7)
     'EndTime'   = [datetime]::Today
} | Group-Object ActivityID | ForEach-Object {
    if($_.Group.Properties[0].Value -like '*CreateTimesheets*'){

        Remove-Variable start,end -ErrorAction SilentlyContinue
        $start = $_.Group |
            Where-Object { $_.Id -eq 200 } |
            Select-Object -Expand TimeCreated -First 1
        $end   = $_.Group |
            Where-Object { $_.Id -eq 201 } |
            Select-Object -Expand TimeCreated -First 1

        [PSCustomObject]@{
            TaskName  = $_.Group.Properties[0].Value
            StartTime = $start
            Duration  = ($end - $start).TotalSeconds
        }
    }
}

Another possible version

Get-WinEvent -FilterHashtable @{
    'LogName'   = 'Microsoft-Windows-TaskScheduler/Operational'
    'ID'        = 200, 201
    'StartTime' = [datetime]::Today.AddDays(-7)
    'EndTime'   = [datetime]::Today
} | Where-Object Message -match "CreateTimesheets" | Group-Object ActivityID | ForEach-Object {

        Remove-Variable start,end -ErrorAction SilentlyContinue
        $start,$end = $_.Group.where({$_.Id -eq 200 },'split').timecreated

        [PSCustomObject]@{
            TaskName  = $_.Group.Properties[0].Value
            StartTime = $start
            Duration  = ($end - $start).TotalSeconds
        }
}

The adjusted version tested ~10% faster than the original but I didn't have a suitable custom task to filter for. Both provided the same exact records in the end for all tests.

Doug Maurer
  • 8,090
  • 3
  • 12
  • 13
  • thanks for the very informative answer Doug, and yes i had wondered how to filter by the task name prior to the grouping but wasn't sure how to do so! appreciate it. – mcgovec9 Nov 03 '20 at 10:05
1

My Guess is you have a grouped ActivityID without a matching 200 / 201 ID. (Task that didn't finish during the time you ran your script)

If you only want to tasks that have finished and have an $end value - see if modifying line 6 like this does the trick:

} | Group-Object ActivityID | Where-Object {$_.Count -gt 1} | ForEach-Object {
Ricc Babbitt
  • 355
  • 1
  • 7