In this post we'll create a PowerShell advanced function used to send email messages. Instead of using the Send-MailMessage cmdlet or the classes in the System.Net.Mail namespace, we'll use the Exchange Web Services (EWS) Managed API. Using the API to send email is useful because you don't have to relay your messages via smtp – your messages are sent directly through Exchange, and can even be saved in your mailbox sent items folder. We'll also design this function to work with the existing EMS cmdlets so you can pipe in recipient objects and send messages.
The first thing you need to do is download the EWS Managed API. You can download the installer here. Take note of the following requirements before downloading the EWS Managed API:
- Supported OS: Windows 7, Windows Server 2008, Windows Server 2008 R2, or Windows Vista
- Requires Microsoft .NET Framework 3.5
- We'll be using this function from within the Exchange Management Shell, so you'll want to grab the x64 version of the API.
Let's break down the function in a few steps to give you an idea of what is going on.
Creating the Function
We'll start off by creating the function and adding some parameters – we'll call the function Send-EWSMailMessage. We will need to provide parameters for the recipient email address, the subject and the body of the email.
Function Send-EWSMailMessage { [CmdletBinding()] param( [Parameter(Position=0, ValueFromPipelineByPropertyName=$true, Mandatory=$true)] [Object] $PrimarySmtpAddress, [Parameter(Position=1, Mandatory=$true)] [System.String] $Subject, [Parameter(Position=2, Mandatory=$true)] [System.String] $Body )
As you can see, we've defined three parameters; PrimarySmtpAddress, Subject and Body. The PrimarySmtpAddress will provide the address for our email recipient. It will also accept it's value from the pipeline by property name. This means we'll be able to pipe email recipients from the Get-Mailbox and Get-DistributionGroup cmdlets to the Send-EWSEmailMessage function.
The Begin Block
Here we'll import the EWS Managed API assembly and determine the identity of the user running the function. We'll use the email address of the calling user to find an Exchange server using autodiscover and provide the sender address for the message.
begin { Add-Type -Path "C:\bin\Microsoft.Exchange.WebServices.dll" $sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value $user = [ADSI]"LDAP://<SID=$sid>" }
You can see in the code that the path to the assembly is under c:\bin. By default it is located in C:\Program Files\Microsoft\Exchange\Web Services\1.0. Make sure you update your path if needed.
The Process Block
In the process block, we'll handle each object as it comes across the pipeline. We'll create an EWS service object that will locate Exchange using autodiscover using the credentials and identity of the user running the function. Then we'll create a message and send it to the recipient.
process { $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService $service.AutodiscoverUrl($user.Properties.mail) if($PrimarySmtpAddress.GetType().fullname -eq "Microsoft.Exchange.Data.SmtpAddress") { $Recipient = $PrimarySmtpAddress.ToString() } else { $Recipient = $PrimarySmtpAddress } $mail = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage($service) $mail.Subject = $Subject $mail.Body = $Body [Void] $mail.ToRecipients.Add($Recipient) $mail.SendAndSaveCopy() }
You can see here that we're checking the type of the $PrimarySmtpAddress obejct. This is because depending on how the function was run, it could be a simple string or an object piped from Get-Mailbox or Get-DistributionGroup – if it's piped in as an smtp object, we call the ToString method to get the smtp address. Also notice that we can the SendAndSaveCopy method to send the email, this will leave a copy of the message in the sent items folder in the senders Exchange mailbox.
You can download the finished version of the function here.
Testing it out
Now that we've got the code figured out, we can test the function. First, we'll send a simple message to one user:
$subject = "test"
$body = "this is a test email"
Send-EWSMailMessage administrator@contoso.com $subject $body
Email all users in a specific mailbox database:
Get-Mailbox -Database DB1 | Send-EWSMailMessage -subject $subject -body $body
Send an email to a distribution group:
Get-DistributionGroup Sales | Send-EWSMailMessage -subject $subject -body $body
There's a lot that can be done with the EWS Managed API – I'll be posting more about using it with PowerShell in the future.