Sending mail with PowerShell

[June 27, 2014] Update: I’ve made several updates, including multiple recipients and sending attachments.

If you’ve created a PowerShell script that runs as a scheduled task, you may want to have it send you an e-mail with a log file or other notification about what the script did or didn’t do (i.e. error notification). PowerShell version 2 provides the Send-MailMessage cmdlet to do this, but in some cases the older System.Net.Mail .NET namespace is a more desirable approach (System.Net.Mail also works in PowerShell version 1).

The Send-MailMessage Method

Here is an example of the Send-MailMessage cmdlet:

$emailSmtpServer = "mail.somewhere.com"
$emailFrom = "John Smith <john@somewhere.com>"
$emailTo = "jane@somewhere.com"
$emailSubject = "Testing e-mail"
$emailBody = @"
Here is a message
From your friendly neighborhood IT guy
"@
Send-MailMessage -To $emailTo -From $emailFrom -Subject $emailSubject -Body $emailBody -SmtpServer $emailSmtpServer

Some notes:

  • On line 1, $emailSmtpServer is the mail server that you will be using to send the message.
  • On line 3, $emailFrom contains a friendly name (i.e. “John Smith”) followed by John’s e-mail addressed, enclosed in <>.

You can use HTML formatting in the $emailBody if you specify the -BodyAsHTML flag in the Send-MailMessage command, like this:

$emailBody = @"
<p>Here is a message that is <strong>HTML formatted</strong>.</p>
<p>From your friendly neighborhood IT guy</p>
"@

Send-MailMessage -To $emailTo -From $emailFrom -Subject $emailSubject -Body $emailBody -BodyAsHTML -SmtpServer $emailSmtpServer

That message will come out looking something like this:

Here is a message that is HTML formatted.

From your friendly neighborhood IT guy

If you need to send an attachment, change your Send-MailMessage command to something like this:

$attachment = "C:\myfile.txt"

#Or do multiple attachments like this:
$attachment = "C:\myfile1.txt","C:\myfile2.txt"

Send-MailMessage -To $emailTo -From $emailFrom -Subject $emailSubject -Body $emailBody -BodyAsHTML -Attachments $attachment -SmtpServer $emailSmtpServer

The System.Net.Mail Method

The Send-MailMessage method works well, if you have an SMTP server that does not require authentication and works on the standard SMTP port (port 25). If you need to send the message through an e-mail system such as GMail or Exchange Online which requires authentication and TLS on port 587, then using the System.Net.Mail .NET namespace is a good way to go.

$emailSmtpServer = "mail.somewhere.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "username"
$emailSmtpPass = "password"

$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = "John Smith <john@somewhere.com>"
$emailMessage.To.Add( "jane@somewhere.com" )
$emailMessage.Subject = "Testing e-mail"
$emailMessage.IsBodyHtml = $true
$emailMessage.Body = @"
<p>Here is a message that is <strong>HTML formatted</strong>.</p>
<p>From your friendly neighborhood IT guy</p>
"@

$SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer , $emailSmtpServerPort )
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential( $emailSmtpUser , $emailSmtpPass );

$SMTPClient.Send( $emailMessage )

Essentially, what we are doing is building the mail message $emailMessage with its various components, building the $SMTPClient with the server and credential information, and then using that to send the mail message. This obviously takes some more code, but it is more flexible than the Send-MailMessage method.

To send attachments using this method, add this before the $SMTPClient.Send line:

$attachment = "C:\myfile.txt"
$emailMessage.Attachments.Add( $attachment )

For multiple attachments, just repeat those two lines of code, with the new file name.

Multiple Recipients

If you need to your message to multiple recipients, for the Send-MailMessage method:

$emailTo = "jane@somewhere.com","jim@somewhere.com"

For the System.Net.Mail method:

$emailMessage.To.Add( "jane@somewhere.com" )
$emailMessage.To.Add( "jim@somewhere.com" )

Here-Strings

On a side note, I’ve used a Here-String for the $emailBody variable. When sending a non-HTML formatted message, this allows you to easily insert line breaks without having to use `n notation. Even when sending an HTML formatted message, it makes it easier to read in your code.

26 Comments


  1. Thanks for the TLS example. There were a couple of bugs though:

    1. The following needs to be inserted at line 14:

    $emailMessage.IsBodyHtml = “True”
    $emailMessage.Body = $emailBody

    2. Line 19 should read:

    $SMTPClient.Send($emailMessage)

    Reply

  2. I apologize for the oversight. You are absolutely correct. I have made the corrections in the TLS example.

    I had originally used the code in a more complex script and consequently missed a piece when I posted it here.

    As far as the SMTPClient.Send portion, I notice that you suggest removing the spaces from before and within the parentheses. The only space that actually NEEDS to be removed is the one between “Send” and the opening parenthesis.

    I tend to use spaces within the parentheses to make the code a little more legible (especially in the case of nested parentheses), but they are certainly not necessary.

    Reply

  3. how about multiple recipient?
    i tried
    $emailTo = @(“nick.ry.khor@carlsberg.asia”, “nick_khor@hotmail.com”)
    it doesn’t work.

    Reply

    1. Hey Nick,

      Just tried it here and that format worked for me. From your script, are you able to send mail to each of these accounts when you have them in as single recipients (i.e. try once for the first account and then try again for the second account)? I’m wondering if there’s something else going on with mail delivery.

      Reply

      1. Correction – it works in my first example, the unauthenticated method using Send-MailMessage. I will check on the proper syntax to add multiple recipients for System.Net.Mail.MailMessage.

        Reply

      2. I’ve updated the post to show the proper way to do multiple recipients when using System.Net.Mail.

        Reply

    1. I apologize for the delay in answering your question. I’ve made some updates to this post, including how to send attachments.

      Reply

  4. I get the following error

    Exception calling “Send” with “1” argument(s): “Failure sending mail.”
    At C:\Scripts\Send-Email02.ps1:21 char:17
    + $SMTPClient.Send <<<< ($emailMessage )
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Reply

    1. Hey James,

      Can you provide a little more information about your code? If you don’t want to paste parts of your code publicly here, feel free use my contact form and I’ll see if I can find anything.

      Also, what version of Windows and PowerShell are you testing this on?

      Reply

  5. Thank you for the quick response. It works on my Windows 8.1 machine. But the script is for a general users that is running a Windows 7 PC and we have a Exchange 2010 environment.

    $emailSmtpServer = “mail.xyz.com”
    $emailSmtpServerPort = “587”
    $emailSmtpUser = “xyz\jdoe”
    $emailSmtpPass = “password”

    $emailMessage = New-Object System.Net.Mail.MailMessage
    $emailMessage.From = “jdoe@xyz.org”
    $emailMessage.To.Add( “kdoe@xyz.org” )
    $emailMessage.Subject = “Testing e-mail”
    $emailMessage.IsBodyHtml = “True”
    $emailMessage.Body = $emailBody
    $emailMessage.Body = @”
    Here is a message that is HTML formatted.
    From your friendly neighborhood IT guy
    “@

    $SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer , $emailSmtpServerPort )
    $SMTPClient.EnableSsl = $true
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential( $emailSmtpUser , $emailSmtpPass );

    $SMTPClient.Send($emailMessage )

    Reply

    1. Hey James,

      I took your code, plugged in my server info (an on-prem 2007 Exchange system, in this case) and credentials and the message sent without any issues. I made a few tweaks to my environment to try to reproduce the error (i.e. setting the Exchange Receive Connector to not accept my IP address, using bad credentials, etc.) and I couldn’t get that exact message.

      A few things to try:
      1) The line “$emailMessage.Body = $emailBody” is unnecessary, but I verified that it’s not causing the issue.
      2) Make sure that all of your quotes are standard “non-curly” quotes – especially when copying from web pages, they can get a little wonky. In my experience, even if they look like they pasted fine into PowerShell, there might still be an issue, so put them into Notepad first and check them out.
      3) Check to ensure that you can communicate to your Exchange server on port 587 from your client IP address. In particular, the Exchange Receive Connectors need to accept authenticated messages from your IP and any firewalls in between you and the mail server also need to allow the communication. The best way I can think to test this is to configure a mail client on your computer to point to the server/port that you’re trying to use in the script. Also, ensure that you’re pointing to the right Exchange server (if there are multiple in your environment) – my standard Exchange build, for instance, has the hub transport role on a different set of servers than the mailbox role. I can’t guarantee that any of this is the culprit – as the error messages for these issues should be more descriptive, but it’s worth checking. Been there.

      I’m curious about your problem. Please let me know if you track it down or come across more information that I could help with.

      Reply

    2. Another thought just occurred to me – some antivirus programs can be configured to block SMTP connections, so that’s something else to check. Basically, make sure that you can communicate with the mail server from your client system before doing more code troubleshooting. System.Net.Mail.MailMessage will through similar errors whenever it can’t communicate with the mail server or if it receives an error from the mail server (can’t relay, unauthenticated, etc.), though it normally regurgitates that error message to the screen and doesn’t leave you hanging with a vague error.

      Reply

  6. I needed the script but using port 25 with auth so I just changed the port and removed the SSL line and worked a treat much appreciated great solution.

    Reply

  7. The information on enabling SSL helped me a lot. Thanks. I still have one issue needs to work out and hope you can help me out here. We are using Office 365 to host mailbox, and zscaler for proxy. The script works great when it is not behind proxy, but fail with time out when it runs behind a proxy.
    Thanks
    -Byron

    Reply

    1. Byron – sorry for not seeing this. I haven’t had to run this from behind a proxy to this point. Have you found any further information? I’ll have to see if I can get a test system behind a proxy to give it a shot. I assume that the proxy is allowing port 587 outbound to Office 365 (dumb question, most likely, but start with the obvious)? From the system running PowerShell, are you able to telnet to port 587 on outlook.office365.com?

      Reply

  8. Hi Phil,
    My requirement is I will get to list email ids in flat file, I need to send a mail to all the users in flat file in one go, can you please help me if there is any way to achieve this.
    Thanks
    Gopal

    Reply

    1. Do you want to send an e-mail to each one individually? Or do you want one e-mail with all recipients on the same e-mail? Both can be accomplished, just different methods.

      Reply

  9. Hi Phil, I tried this code more than 25 times with tweaks each time, and never got it to work:

    $emailSmtpServer = “206.46.232.100”
    $emailSmtpServerPort = “465”
    $emailSmtpUser = “joepica”
    $emailSmtpPass = “mypassword”

    $emailMessage = New-Object System.Net.Mail.MailMessage
    $emailMessage.From = “gg@verizon.net”
    $emailMessage.To.Add(“olivia@metonto.com”)
    $emailMessage.Subject = “Testing e-mail”
    $emailMessage.IsBodyHtml = $true
    $emailMessage.Body = @”
    Here is a message that is HTML formatted.
    From your friendly neighborhood IT guy
    “@

    $SMTPClient = New-Object System.Net.Mail.SmtpClient($emailSmtpServer, $emailSmtpServerPort)
    $SMTPClient.EnableSsl = $true
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailSmtpUser, $emailSmtpPass)
    $SMTPClient.Send($emailMessage)

    The error I get is this:

    PS C:\users\admin> .\email6.ps1
    Exception calling “Send” with “1” argument(s): “The operation has timed out.”
    At C:\users\admin\email6.ps1:19 char:1
    + $SMTPClient.Send($emailMessage)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : SmtpException

    Can you help? I’m really frustrated. I copied the params right out of Thunderbird which I have been using for years. I also turned off my virus protection. Thanks Very Greatly in advance!! Stephen F.

    Reply

    1. First thing I’d recommend is a basic connectivity test with telnet. Try this from a command line:

      telnet 206.46.232.100 465

      If that doesn’t time out, then you’re connectivity is fine. It’ll go to a blank screen if the connection is successful. Chances are, if you’re using Thunderbird on the same system to connect to this server, the telnet connection should be successful (but just checking).

      Note that the telnet client needs to be enabled on Windows 7 and above (go to Control Panel > Programs and Features > Turn Windows Features on or off).

      How about the SSL setting? I’m assuming that the server requires it (I hope so!), but try disabling it.

      Reply

    2. Change port 465 to 587. 587 – starttls, 465 – tls. This script uses starttls port (587) or smtp (25), and not tls (465).

      Reply

  10. Thank you so much Phil, after years this post keeps helping people 🙂 this is the best post I found for sending emails with powershell, specially talking abouth the TLS method. Works perfectly! Elizabeth

    Reply

Leave a Reply