powershellforeachpsobjectget-eventlog

Get-EventLog loop for psobjects, strange results


I've to collect certain events for sharing it by mail; I'm missing something because the script, take a lot of time for only 2 servers and, the result is absurd!

Server1 events count:

    $Etype = @( "Warning", "Error" )
    $Source = "Cisco Systems Inc. ICM"
    $StartDate = ((Get-Date).AddDays(-2))
    $EndDate = Get-Date
    (Get-EventLog -LogName Application -After $StartDate -Before $EndDate -EntryType $Etype -Source $Source).Count
}
$rEVT
2576

Server2 events count:

$rEVT2 = Invoke-Command -ComputerName $Srv2 -scriptblock {
    $Etype = @( "Warning", "Error" )
    $Source = "Cisco Systems Inc. ICM"
    $StartDate = ((Get-Date).AddDays(-2)) 
    $EndDate = Get-Date
    (Get-EventLog -LogName Application -After $StartDate -Before $EndDate -EntryType $Etype -Source $Source).Count
}
$rEVT2
3853

My script:

$HostsTable = @()
foreach ( $Srv in $MyList ) {
$rEVT_ALL = Invoke-Command -ComputerName $Srv -scriptblock {
    $Etype = @( "Warning", "Error" )
    $Source = "Cisco Systems Inc. ICM"
    $StartDate = ((Get-Date).AddDays(-2)) 
    $EndDate = Get-Date
    Get-EventLog -LogName Application -After $StartDate -Before $EndDate -EntryType $Etype -Source $Source | Select-Object -Property TimeGenerated, EntryType, Category, Message
    }| ForEach-Object { 
        [PSCustomObject]@{
            HN = $Srv
            TimeCreated = [DateTime]$_.TimeGenerated
            Category = $_.Category
            Level = $_.EntryType
            Message = $_.Message
        }
        $HostsTable +=  $rEVT_ALL
    }
}

Mesaure command:

(Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime


Days              : 0
Hours             : 0
Minutes           : 18    <--------
Seconds           : 35
Milliseconds      : 898
Ticks             : 11158982764
TotalDays         : 0,0129154893101852
TotalHours        : 0,309971743444444
TotalMinutes      : 18,5983046066667
TotalSeconds      : 1115,8982764
TotalMilliseconds : 1115898,2764

Result:

$HostsTable.count
10011624      <------ :D

$HostsTable | select -First 1


HN          : SRV1    <--------
TimeCreated : 18/06/2024 19:10:56
Category    : Message Delivery
Level       : Error
Message     : Client: clgr, state transfer operation aborted.



$HostsTable | select -Last 1


HN          : SRV1    <--------
TimeCreated : 16/06/2024 19:55:43
Category    : Call Router
Level       : Error
Message     : PG has reported that peripheral: CC_VRU_1 (ID: 5001) is not operational.

After, I've to export-excel, but at the moment something goes wrong...

Please, help me! Roberto


Solution

  • You're currently re-adding all the results from the previous iteration of the foreach loop to $HostsTable for every new event returned by the query against the next server.

    Move this assignment statement $HostsTable += $rEVT_ALL outside the pipeline:

    foreach ($srv in $MyList) {
        $rEVT_ALL = Invoke-Command -ComputerName $Srv -scriptblock {
            $Etype = @( "Warning", "Error" )
            $Source = "Cisco Systems Inc. ICM"
            $StartDate = ((Get-Date).AddDays(-2)) 
            $EndDate = Get-Date
            Get-EventLog -LogName Application -After $StartDate -Before $EndDate -EntryType $Etype -Source $Source | Select-Object -Property TimeGenerated, EntryType, Category, Message
        } | ForEach-Object { 
            [PSCustomObject]@{
                HN          = $Srv
                TimeCreated = [DateTime]$_.TimeGenerated
                Category    = $_.Category
                Level       = $_.EntryType
                Message     = $_.Message
            }
        }
        # wait to update `$hostsTable` until after the query returns
        $HostsTable += $rEVT_ALL
    }
    

    This will ensure you save a reference to each event only once, and will improve execution time and memory footprint significantly.

    You could also simplify your code further by passing the server list directly to Invoke-Command and then use Select-Object for the property selection/renaming, like so:

    $HostsTable = Invoke-Command -ComputerName $MyList -scriptblock {
        $Etype = @( "Warning", "Error" )
        $Source = "Cisco Systems Inc. ICM"
        $StartDate = ((Get-Date).AddDays(-2)) 
        $EndDate = Get-Date
        Get-EventLog -LogName Application -After $StartDate -Before $EndDate -EntryType $Etype -Source $Source | Select-Object -Property TimeGenerated, EntryType, Category, Message
    } | Select-Object -Property @{Name='HN';Expression={$_.PSComputerName}},TimeCreated,Category,@{Name='Level';Expression={$_.EntryType}},Message
    

    This will also make it faster overall, since the $HostsTable array doesn't have to be resized multiple times, and you won't be blocking when a remote server is slow to respond.


    One last trick to make the code a little more readable - splat your arguments, in turn reducing the amount of horizontal scrolling required when looking at the code:

    $winEventQueryArgs = @{
      LogName = 'Application'
      Entrytype = @( 
        'Warning'
        'Error' 
      )
      Source = "Cisco Systems Inc. ICM"
      After = (Get-Date).AddDays(-2)
      Before = Get-Date
    }
    
    Get-EventLog @winEventQueryArgs | Select-Object -Property TimeGenerated, EntryType, Category, Message