Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send an email if a PowerShell script gets any errors at all and terminate the script

Something that I've been thinking about recently, and I'd like some input from the good people of Stack Overflow on.

I run a fairly large number of PowerShell scripts on differing schedules (using Task Scheduler). These scripts generally gather data and store it in SQL tables for me. I would say I am running 40-50 scripts or so at the moment, maybe more.

One thing that I have seriously been considering recently is the best way of:

  1. Ensuring that if any errors occur the script exits without further processing.
  2. The script should also send me an email on failure with exception details, showing me how far it got.
  3. The transcript/progress is logged so I can review any errors and try better to handle them in the script.

This is going to be a fair bit of work to do, so I'd like to go in with the right approach from the start.

The only way I can see of achieving something like this would be to do the following:

Set $ErrorActionPreference to "Stop"

This should cause any exceptions to trigger a Try/Catch block.

Run the entire script within a Try/Catch/Finally block.

Finally block is optional of course. Something like this:

$ErrorActionPreference = "Stop"

Try
{
    $Content = gc "C:\Temp\NonExistentFile.txt"
    foreach ($Line in $Content)
    {
        Write-Host $Line
    }
}
Catch
{
    Write-Host $_ -ForegroundColor "Red" -BackgroundColor "Black"
    break
}

Log the "Transcript" Data into HTML and email the content on failure.

I've tried a few ways of logging, and one of the keys things for me is that I want something that is:

  1. Easy to log into by providing the text, and a log file location.
  2. Includes the Date/Time at the start of each line of the log file.

I started out writing text into .txt files, but it was a bit of work to do.

Start-Transcript was OK, but did not include the date/time on each line.

I had some success with a custom Function that would use Start-Transcript, and would write any text I sent into it into Write-Host, with the date/time at the start of each line.

But ultimately I think what I need to do is log into a .html document, and then email the contents of this if an error occurs. Something like this as an example:

Function Send-ErrorEmail
{
    Write-Host "Would Send Email Here" -ForegroundColor "Magenta"
}

Function Write-Html
{
    [CmdletBinding()]
    param
    (
        [Parameter(ValueFromPipeline=$True)]$Text,
        $HTMLTag
    )

    [string]$CurrentDateTime = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss - ")

    switch ($HTMLTag)
    {
        "h1" {$ReturnText =  ("<h1>" + $Text + "</h1>")}
        "h2" {$ReturnText =  ("<h2>" + $Text + "</h2>")}
        default {$ReturnText = ("<p>" + $CurrentDateTime + $Text + "</p>")}
    }

    Write-Host -ForegroundColor "Yellow" ($CurrentDateTime + $Text)
    return $ReturnText
}

$ErrorActionPreference = "Stop"

Try
{
    #Stores the HTML File
    $LogfilePath = "C:\Temp\LogFile.html"

    #Begin HTML
    $HTML = @()
    $HTML += "<html><head></head><body>"
    $HTML += "Begin Script" | Write-Html

    #Loop through Computers
    $Computers = "LocalHost", "DoesNotExist", "127.0.0.1"
    foreach ($Computer in $Computers)
    {
        #Get Services
        $HTML += "Computer: $Computer" | Write-Html -HTMLTag "h1"
        $Services = Get-Service -Computer $Computer
        $HTML += $Services | ConvertTo-Html -Fragment
    }

    #Complete HTML
    $HTML += "Script End" | Write-Html
    $HTML += "</body></html>"
    $HTML | Out-File $LogfilePath
}
Catch
{
    #Add Exception to HTML
    $HTML += $_ | Out-String | Write-Html

    #Complete HTML
    $HTML += "</body></html>"
    $HTML | Out-File $LogfilePath

    #Send EMail
    Send-ErrorEmail

    #Exit Script
    exit
}

Any thoughts? Improvements you think I could make? Obviously the HTML document is ugly without CSS formatting, and the Send Mail function doesn't do anything, but it seems to be the simplest way to do what I want.

I can of course dot source any functions amongst all of my scripts for ease of changing a notification email address or similar.

like image 862
HungryHippos Avatar asked Jan 09 '13 21:01

HungryHippos


People also ask

Which statement is used for handling terminating errors in PowerShell?

The Trap statement can also be used to handle terminating errors in scripts.

How can you cause a terminating error in PowerShell?

The throw keyword causes a terminating error. You can use the throw keyword to stop the processing of a command, function, or script. For example, you can use the throw keyword in the script block of an if statement to respond to a condition or in the catch block of a try - catch - finally statement.

How do you terminate a PowerShell command?

You can interrupt and stop a PowerShell command while it is running by pressing Control-C. A script can be stopped with the command exit. This will also close the PowerShell console.


1 Answers

For global error handling I prefer to use a trap statement.

function ErrorHandler($error) 
{
    ... write out error info
    ... send email message with error info
    ... etc
}

trap { ErrorHandler $_; break }

... rest of script code

And if you want to bail on any error, then yes, set $ErrorActionPreference to Stop.

like image 110
Keith Hill Avatar answered Sep 25 '22 03:09

Keith Hill