Error Trapping Get-Winevent

I have a script for getting error logs from remote servers. I am onlu looking for event evel 5,6 Critical & Error. I am also filtering out certain event IDs This seems to cause an error of “NoMatchingEventsFound,Microsoft.PowerShell.Commands.GetWinEventCommand” Ihave tried several different try\catch and nothing seems to trap it.

I even tried to change the order, i.e., instead of assignig to and object variable, having it out put straight to a file

This is a subset of what I am using

[Pre]
[String] $Servers = get-content ‘D:\TEMP\ServerNamesFile.txt’

QUERY COMPUTER SYSTEM EVENT LOG

[string]$Events = (“5051”, “1111”, “1067”)

$StartTime = (Get-Date).Adddays(-30)
foreach($Server in $Servers)
{
try
{

 Get-Winevent -computername $Server -FilterHashtable @{LogName="System"; Level=5,6; startTime=$StartTime}|
 where-object {$_.ID -ne "1111" -or $_.ID -ne "1067" - $_.Id -ne "5051"}|  
 Select-Object @{ e={$_.LogName}; l='LogName' }, @{ e={$_.LevelDisplayName}; l='Level' }, Message, @{ e={$_.ID}; l='EventID' },  @{ e={$_.LogName}; l='Log' }, @{ e={$_.MachineName}; 
 l='Server' },@{ e={$_.TimeCreated}; l='Created' }| 
 export-csv "D:\TEMP\ErrorLogFile.csv" -Delimiter ";" -NoTypeInformation | Out-GridView
 

}

catch [System.Exception]
{
if ($.Exception -match “No events were found that match the specified selection criteria”)
{
Write-Host “No events found”;
}
Else
{
Write-Host $
.Exception
}
}
Finally
{
}
}

[/Pre]

In order to perform a trap, you have to set the ErrorAction to Stop:

Get-WinEvent -ErrorAction Stop ...

Most commands don’t produce terminating exceptions, they produce non-terminating errors, which cannot be caught. To change that behavior, use the -ErrorAction Stop parameter on the command. In most cases, that will turn the error into a catch-able terminating exception. See The Big Book of PowerShell Error Handling (which is free) for explanations.

I had place that in the code at before the | before the export line, and it didnt work, I am guessing that is the wrong place. Where would you select it be placed?

You are running the cmdLet Get-WinEvent, so it is producing the error. You have to tell that command to stop what it’s doing when an error occurs and use Try\Catch to capture the exception. Everything you are doing after the initial pipe is either filtering data produced by Get-WinEvent or running other commands\cmdlets.

Get-Winevent -computername $Server -FilterHashtable @{LogName=“System”; Level=5,6; startTime=$StartTime} -ErrorAction Stop |

For instance, if you thought there was a possibility that exporting to a CSV could cause an error, your code would look something like this:

try {
 
    $events = Get-Winevent -computername $Server -FilterHashtable @{LogName="System"; Level=5,6; startTime=$StartTime} -ErrorAction Stop|
    where-object {$_.ID -ne "1111" -or $_.ID -ne "1067" -or $_.Id -ne "5051"}|  
    Select-Object @{ e={$_.LogName}; l='LogName' },
                  @{ e={$_.LevelDisplayName}; l='Level' },
                  Message,
                  @{ e={$_.ID}; l='EventID' },
                  @{ e={$_.LogName}; l='Log' },
                  @{ e={$_.MachineName}; l='Server' },
                  @{ e={$_.TimeCreated}; l='Created' } 
     
    try{
        export-csv -InputObject $events -Path "D:\TEMP\ErrorLogFile.csv" -Delimiter ";" -NoTypeInformation -ErrorAction Stop
    }
    catch {
        "Error occured exporting csv. {0}" -f $_.Exception.Message
    }    
 
} 
catch {
    "Error occured gathering events. {0}" -f $_.Exception.Message
}

Also, another note is you have Out-Gridview after Export-CSV. Commands like Format-Table, Format-List, Out-Gridview (and others) are like the final stop. There is no data passed after that point:

Get-Process | Export-CSV C:\temp\test.csv | Out-GridView

Nothing underlined is going to get data. These “Gotchas” are documented and explained in the The Big Book of Powershell Gotchas located under Resources > Free EBooks

Thank you guys! I so need a class on this, so many little “Gotchas” and syntax things to keep track of. The Reference Books are great, but sometime your just need a voice to put it in perspective.
I have the error trapping working now.

Can I add a question? In the Get-Winevent, when I select the items to view, I am not finding the correct “heading” for Source. Is there one?

Most likely you would need to access the XML data in the event.

http://blogs.technet.com/b/ashleymcglone/archive/2013/08/28/powershell-get-winevent-xml-madness-getting-details-from-event-logs.aspx?pi57261=2

Basically, you convert the event .ToXML() and extract event details.

Here is the code with the suggesteed changes, It still isnt working.
The Error message I am reciving is:

Error occured gathering events. Cannot bind argument to parameter ‘InputObject’ because it is null.

I have tried with

$LastWriteTime = (Get-Date).Adddays(-30) and the datetime in the code both to make sure I had errors to report.

I also want to determine Subject of an email based on if there were or were not events. I am pretty sure I understand that part, I just wanted to let you know where I was heading.


[String] $Servers = get-content 'D:\TEMP\ServerNamesFile.txt'

$ExportPath = "D:\TEMP\OFSErrorReports\ErrorLogFile.csv"

# QUERY COMPUTER SYSTEM EVENT LOG

#$StartTime = (Get-Date).Adddays(-30)

#Find the DateTimeStamp of when the Exported LogReport was last written
#This will then look for all of the Events that took place prior to that datetimestamp
$LastWriteTime = [datetime] (Get-ItemProperty -Path $ExportPath).LastWriteTime 

#Write the value to the display
write-host $LastWriteTime

foreach($Server in $Servers)
{
try  
    {
  
     $Events = Get-Winevent -computername $Server -FilterHashtable @{LogName="System", "Application";  startTime=$LastWriteTime} -ErrorAction Stop |  #Gathering Events
     where-object { $_.ID -ne "1067" - $_.Id -ne "5051"}|  
     Select-Object @{ e={$_.LogName}; l='LogName' }, @{ e={$_.LevelDisplayName}; l='Level' }, Message, @{ e={$_.ID}; l='EventID' },  @{ e={$_.LogName}; l='Log' }, @{ e={$_.MachineName}; 
     l='Server' },@{ e={$_.TimeCreated}; l='Created' } 
     
     Write-Host $Events
     
    try  #Try Export CSV
        {
            export-csv -InputObject $Events -Path $ExportPath -Delimiter ";" -NoTypeInformation -ErrorAction Stop  #Exporting csv
        
        Catch 
            { 
                If ($_.Exception.Message = "Cannot bind argument to parameter 'InputObject' because it is null") 
                    {
                         "Error occured exporting csv. {0}" -f $_.Exception.Message
                    }
            }
            
         }#End Export CSV
 
 catch 
    {
         "Error occured gathering events. {0}" -f $_.Exception.Message
 
    }
               
    } #End Try winEvent 
 Finally
    {
 
    }
}  #End ForEach Loop
[/Pre]

Your problem is the first line:

[String] $Servers = get-content ‘D:\TEMP\ServerNamesFile.txt’

Get-Content returns each line as an array and -ComputerName is expecting a string array. Remove the [string] from the line and try it. The events line is generating the error, not the CSV code, so your if is in the wrong catch block. Basically, the error is stating you are passing something to Get-WinEvent as parameter and it’s null, no data. I’m guessing it’s what was aforementioned.

Thank you Rob! helped alot.

I have made some more changes and realized somewhere along the way I made a mistake in the export section. I have tried the 2 different lines of code below. They both sort of work. but they arent appending on each server, they are overwiting.

[Pre]

QUERY COMPUTER SYSTEM EVENT LOG

#Start Get Events Process
#__________________________________________________________
$Servers = get-content ‘D:\ServerNamesFile.txt’

$ExportPath = “D:\ErrorLogFile.csv”

#Find the DateTimeStamp of when the Exported LogReport was last written
#This will then look for all of the Events that took place prior to that datetimestamp
#______________________________________________________________________________
$StartTime = (Get-Date).Adddays(-10)
$LastWriteTime = [datetime] (Get-ItemProperty -Path $ExportPath).LastWriteTime

#Write the value to the display
write-host $LastWriteTime
write-host $Servers

#Field Names

foreach($Server in $Servers)
{
try
{
$Events = Get-Winevent -computername $Server -FilterHashtable @{LogName=“System”, “Application”; Level= 1,2; startTime=$StartTime} -ErrorAction Stop | #Gathering Events
where-object { $.ID -ne “1067” -or $.Id -ne “5051”}|
Select-Object @{ e={$.MachineName};l=‘Server’ },@{ e={$.LogName}; l=‘LogName’ },@{ e={$.ID}; l=‘EventID’ }, @{ e={$.LevelDisplayName}; l=‘Level’ },@{ e={$.ProviderName}; l=‘Source’ }, @{ e={$.message}; l=‘Message’ },@{ e={$_.TimeCreated}; l=‘Created’ }

     try  #Try Export CSV
     {
        
         if($Message -eq "No Records") 
        { 
            #$Message | ConvertTo-csv -NoTypeInformation | Select-Object | Out-File - Append -FilePath $ExportPath
           $Message | export-csv  -Path $ExportPath  -NoTypeInformation   -ErrorAction Stop   #Exporting csv
         } 
        else
        { 
            #$Events | ConvertTo-csv -NoTypeInformation | Select-Object | Out-File - Append -FilePath $ExportPath
           $Events | export-csv  -Path $ExportPath   -NoTypeInformation  -ErrorAction Stop  #Exporting csv  
        }

     }#End Export CSV     
     catch 
     { 
         If ($_.Exception.Message = "Cannot bind argument to parameter 'InputObject' because it is null") 
         {
            "Error occured exporting csv. {0}" -f $_.Exception.Message
         }
          
     }#End Export CSV Catch
 
 } #End Try CSV

catch #Start WinEvent Catch
{
If ($.Exception -match “No events were found that match the specified selection criteria”)
{
$message = “No Records”
}
else
{
“Error occured gathering WinEvents. {0}” -f $
.Exception.Message
$message = “Records”
}

 }   #End WinEvent Catch

Finally
{
Continue
}

} #End ForEach Loop

$Message = “”

Exit

Let’s think about this a bit more “Powershelly”. If you run Get-Service and a service isn’t running, do you get an error or is that information returned in the object so that you query against it? The object is just returned with a Status of “Stopped”. If no records are returned, I don’t want to look through the logs or output to figure out what server didn’t return records, it’s easier to just filter the results (but I still have it if I need it).

$servers = "Server1", "Server2", "Server3"
#Create an empty array for all return objects
$eventsFromAllServers = @()
#Assign the foreach loop results to the empty array
$eventsFromAllServers = foreach ($server in $servers) {
    #***  This is just an example, the if line is your try and the elseif is the Catch ***
    if ($server -like "*2") {
        #Emulate your first try\catch, you have data...
        #Note that we are not assigning a variable, we are return our results
        #to the for loop.  Select is creating a object for you
        Get-WinEvent -LogName Application -MaxEvents 2 |
        Select @{l="Server";e={$server}}, 
               @{l="Status";e={"Successful"}},
               @{l="StatusDetails";e={"Got logs"}},
               LogName,
               @{l="EventID";e={$_.ID}}
    }
    elseif ($server -notlike "*2") {
        #Emulate no records
        #Since you can't Select from nothing, we create a custom object with the
        #same properties as your select and assign manual values
        $properties = @{Server=$Server;
                        Status= "NoRecords";
                        StatusDetail="The error you received (e.g. $_.Exception.Message)"
                        LogName=$null;
                        EventID=$null}
        #Since since there is no Select, we create a new PSObject and assign the properties
        #and this is returned to our for loop variable
        New-Object -TypeName PSObject -Property $properties
    }
    
}

#Now our variable contains all returned objects for all servers
$eventsFromAllServers

Output:

StatusDetail : The error you received (e.g. .Exception.Message)
Status       : NoRecords
Server       : Server1
LogName      : 
EventID      : 

Server        : Server2
Status        : Successful
StatusDetails : Got logs
LogName       : Application
EventID       : 1

Server        : Server2
Status        : Successful
StatusDetails : Got logs
LogName       : Application
EventID       : 1

StatusDetail : The error you received (e.g. .Exception.Message)
Status       : NoRecords
Server       : Server3
LogName      : 
EventID      : 

Now all of your data for all servers is in one place and we can run queries against the whole resultset:

PS C:\Windows\System32\WindowsPowerShell\v1.0> $eventsFromAllServers | Where{$_.Status -eq "NoRecords"} | Select Server, StatusDetail | Format-Table -AutoSize

Server  StatusDetail                                    
------  ------------                                    
Server1 The error you received (e.g. .Exception.Message)
Server3 The error you received (e.g. .Exception.Message)

So, if you can run queries against this now, do you really need a CSV to open it in Excel? You could export it to a CSV or XML for historical reasons, but you can run queries against records from all servers to find anything you want. In your for loop, I would not make CSV’s. Gather all of your data and then it’s as simple $eventsFromAllServer | Export-CSV C:\ServerEvents.csv -NoTypeInformation. As another side note, in your for loop I would have a Test-Connection to validate the server is online before running WMI or Event queries against it:

$eventsFromAllServers = foreach ($server in $servers) {
    #This Test-Connection command returns a boolean value, True or False
    if (Test-Connection -ComputerName $server -Count 3 -Quiet) {
        #code for getting events
    }
    else {
        #custom object with status offline, NoPing...whatever is your fancy
    }
}

I like that. I will have to keep that concept for future use, in the mean time, I have to tweek what I have for “politcal” reasons.

I got this working except 2 items.

  1. Export-csv when $events = no Records. When I do this, it exports to the csv file “Length” on 1st line and “16” on 2nd line which is the property of the contents “No Error Records” but when i have actaul log records it exports them correctly.

[Pre]

#Start Get Events Process
#______________________________________________________________________________

$Servers = get-content ‘D:\TEMP\ServerNamesFile.txt’

$ExportPath = “D:\TEMP\ErrorLogFile.csv”

$EmailPath = “D:\TEMP\EmailRecipients.csv”

#Find the DateTimeStamp of when the Exported LogReport was last written
#This will then look for all of the Events that took place prior to that datetimestamp
#__________________________________________________________
$LastWriteTime = [datetime] (Get-ItemProperty -Path $ExportPath).LastWriteTime

#Make sure csv is not read only
sp $ExportPath IsReadOnly $false

If(Test-Path $ExportPath )
{
Remove-Item $ExportPath
}

#Gather the events from all of the servers
#__________________________________________________________
$Events = foreach($Server in $Servers)
{
try
{
Get-Winevent -computername $Server -FilterHashtable @{LogName=“System”, “Application”, “Security”; Level=5,6; startTime=$LastWriteTime} -ErrorAction Stop | #Gathering Events
where-object {$.ID -ne “1111” -or $.ID -ne “1067” -or $.Id -ne “5051”}|
Select-Object @{ e={$
.MachineName};l=‘Server’ },@{ e={$.LogName}; l=‘LogName’ },@{ e={$.ID}; l=‘EventID’ },@{ e={$.LevelDisplayName}; l=‘Level’ },@{ e={$.ProviderName}; l=‘Source’ }, @{ e={$.message}; l=‘Message’ },@{ e={$.TimeCreated}; l=‘Created’ }

 } #End Try CSV

catch #Start WinEvent Catch
{
If ($.Exception -match “No events were found that match the specified selection criteria”)
{
Continue
}
else
{
“Error occured gathering WinEvents. {0}” -f $
.Exception.Message
Continue
}

 }   #End WinEvent Catch

} #End ForEach Loop

#Start Export CSV, This takes the contents of the $Events and
#Converts them to a csv file (The file is overwritten every time
#__________________________________________________________
write-host "Before Export: " $Events

try #Try Export CSV
{
$RecordCount = ($Events | Measure-Object).Count

if($RecordCount -gt  1) 
{ 
   $Events | export-csv  -Path $ExportPath  -NoTypeInformation  -ErrorAction Stop  #Exporting csv  

}
else
{
$Events = “No Error Records”
$Events | export-csv -Path $ExportPath -NoTypeInformation -ErrorAction Stop #Exporting csv

}

write-host "After Export: " $Events
}#End Export CSV

catch
{
“Error occured exporting csv. {0}” -f $_.Exception.Message
Continue
}

##Send the Email out
#__________________________________________________________
$RecordCount = (Import-csv $ExportPath).count
Write-Host $RecordCount

if ($RecordCount -gt 0)
{

Populate Variables

#concatenate all of the Error Log variables (events)together
[String]$Body = “The Error Files are Located in the Attachements.”
[string]$Subject = "Event Log Results for " + (Get-Date).Adddays(-1)
[string]$Path = “D:\TEMP\ErrorLogFile.csv”
}
Else
{
[string]$Body = “No Errors to Report”
[string]$Subject = "Event Log Results for " + (Get-Date).Adddays(-1) + “: No Errors”
[string]$Path = “None”
}

#update Email information as required

Send the email with All Attachments in Folder

Try #Send Email
{
#call the function to send the email, passing in the subject and body text…(more params can be added, if you want)
$RES = SendEmail $Subject $Body $Path $EmailPath
}
Catch #Start Send Email Catch
{
“Error occured Send-EmailMessage. {0}” -f $_.Exception.Message
Continue
}

Exit

[/Pre]

You are assigning output to a variable:

$output = @()
$output = for($i=1;$i -le 3;$i++){
    Get-Process | Select Name -First 2
    "How are you"
}

$output

Output:

Name                                                                                                                                                                                                                   
----                                                                                                                                                                                                                   
agent                                                                                                                                                                                                                  
armsvc                                                                                                                                                                                                                 
How are you
agent                                                                                                                                                                                                                  
armsvc                                                                                                                                                                                                                 
How are you
agent                                                                                                                                                                                                                  
armsvc                                                                                                                                                                                                                 
How are you

You can fix it by only appending data from your events query to your object, but the method I posted earlier is much better for reporting and analysis. You can identify servers that are and ARE NOT having issues and only select data that you want to be reported for the output. However, I digress…

$output = @()
for($i=1;$i -le 3;$i++){
    $output += Get-Process | Select Name -First 2
    "How are you"
}

$output

Output:

How are you
How are you
How are you

Name                                                                                                                                                                                                                   
----                                                                                                                                                                                                                   
agent                                                                                                                                                                                                                  
armsvc                                                                                                                                                                                                                 
agent                                                                                                                                                                                                                  
armsvc                                                                                                                                                                                                                 
agent                                                                                                                                                                                                                  
armsvc 

Lastly, I’m not sure how you are sending the email, but there is a built-in cmdlet Send-MailMessage that you can use to send these notifications. If the SMTP relay you are using requires credentials, then you would need to provide them using the -Credential switch. However, if you are getting emails without credentials then most likely the culprit is something in your code not sending the email like trying to attach a file that doesn’t exist.

Thanks Rob! I really appreciated the assistance. It has been running now for 3 hours appears to be working very well. (Knock on Wood)

I ended up using this as a solution:

[Pre]
$Message = @(“No Error Records”)
$Events = $Message |Select-Object @{Name = ‘Results’;Expression={$_}}
$Events | export-csv -Path $ExportPath -NoTypeInformation -ErrorAction Stop
[/Pre]

And for email I used

function SendEmail($subject, $body, $Path, $EmailPath)
  {
    #Set Variables that were not Passed as Parameters
    $smtpServer = "smtpserver"
    $from = ("emailaddress") 
    $Credential = "cred"
    
    #Load email Addresses from File
    $Recipients = Get-Content $EmailPath
    
    If ($Path = "None")
    {
    send-mailmessage -smtpserver  $smtpServer -to $Recipients -from $from  -subject $subject -body $body     
   }
    Else
    {
    send-mailmessage -smtpserver  $smtpServer -to $Recipients -from $from  -subject $subject -body $body -Attachment $Path    
    }
      
 }

[/Pre]