Error Trapping Get-Winevent

This topic contains 13 replies, has 3 voices, and was last updated by Profile photo of Susan Miller Susan Miller 2 years, 1 month ago.

  • Author
    Posts
  • #24290
    Profile photo of Susan Miller
    Susan Miller
    Participant

    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]

  • #24291
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

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

    Get-WinEvent -ErrorAction Stop ...
    
  • #24292
    Profile photo of Don Jones
    Don Jones
    Keymaster

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

  • #24295
    Profile photo of Susan Miller
    Susan Miller
    Participant

    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?

  • #24300
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    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}[b] -ErrorAction Stop [/b]|

    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 [u]| Out-GridView
    [/u]

    Nothing underlined is going to get data. These "Gotchas" are documented and explained in the [i]The Big Book of Powershell Gotchas[/i] located under Resources > Free EBooks

  • #24302
    Profile photo of Susan Miller
    Susan Miller
    Participant

    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?

  • #24303
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    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.

  • #24305
    Profile photo of Susan Miller
    Susan Miller
    Participant

    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]
  • #24309
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    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.

  • #24413
    Profile photo of Susan Miller
    Susan Miller
    Participant

    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

  • #24416
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    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
        }
    }
    
  • #24455
    Profile photo of Susan Miller
    Susan Miller
    Participant

    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]

  • #24458
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    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.

  • #24465
    Profile photo of Susan Miller
    Susan Miller
    Participant

    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]

You must be logged in to reply to this topic.