How to Send Attachments with Outlook in PowerShell

Sending emails with attachments using PowerShell is a common requirement. However, I've had a hard time finding an up-to-date way of doing this, so I thought I would share what I found.

Built-In Cmdlets

So the first thing I do when I need to do something on PowerShell is to check the PowerShell documentation. I used Get-Help *email* to find any commands related to emails. I also checked Get-Command.

The cmdlet I found that was most relevant was Send-MailMessage. Send-MailMessage is a built in cmdlet that comes standard with PowerShell, so I thought I had found my solution. After checking the help file for the cmdlet however, I found it was listed as obsolete. The help file also notes that there is no immediate replacement in PowerShell for this command.

COM & Outlook

Since there was no built in method to do this, I decided to use COM to control Outlook. This would allow me to automate the creation and sending of emails in PowerShell scripts. Using COM does have some disadvantages. COM access can be unstable and not reliable. For example, I found when filtering and deleting emails from folders filled with 1000s of emails, the script had to run a few times before all matching emails were deleted. This is something to keep in mind if you go down this route.

In my own experience, I also found documentation online for using COM & Outlook with PowerShell to be lacking. While COM itself is well documented by Microsoft, I found all their examples to be in VBScript.

There was some helpful documentation however. Over at Microsoft's Devblog, Outlook Automation with PowerShell was a very helpful post. It used contact groups and other Outlook features I did not want to use, however. I wanted to be able to choose To's CC's etc from a PowerShell script, not from a contact group created in Outlook. Regardless, this post does contain some very useful code that I found nowhere else:

try
{
    $outlook = [Runtime.Interopservices.Marshal]GetActiveObject('Outlook.Application')
    $outlookWasAlreadyRunning = $true
}
catch
{
    try
    {
        $Outlook = New-Object -comobject Outlook.Application
        $outlookWasAlreadyRunning = $false
    }
    catch
    {
        write-host "You must exit Outlook first."
        exit
    }
}

This allows PowerShell to take control of Outlook if it is already open, and at the end of the script, if you place the following:

# Close outlook if it wasn't opened before running this script
if ($outlookWasAlreadyRunning -eq $false)
{
    Get-Process "*outlook*" | Stop-Process force
}

This will leave the Outlook window open when the script is finished, which made debugging much faster, as I did not have to open and close Outlook all the time. This is also useful in a production script, because the script can be left to run automatically and no matter the status of Outlook, it will be able to run and leave it in the state it was before running.

Sending an Email with Attachments

This is the code to send an email from Outlook with an attachment. I think this code is pretty self-explanatory. The hard part was finding the information to put it all together.

#Sent the namespace to the MAPI
$namespace = $Outlook.GetNameSpace("MAPI")

#Create the Email Message
$MailMsg = $Outlook.CreateItem(0)
$MailMsg.GetInspector.Activate()

#Add CCs
$CCs = $MailMsg.Recipients.Add("emailyouwanttocc@goeshere.ca")
$CCs.Type = [Microsoft.Office.Interop.Outlook.OlMailRecipientType]olCC
$CCs2 = $MailMsg.Recipients.Add("anotheremailyouwanttocc@goeshere.ca")
$CCs2.Type = [Microsoft.Office.Interop.Outlook.OlMailRecipientType]olCC

#Add Recipients
$MailMsg.Recipients.Add("Themainrecipient@goeshere.ca") | Out-Null

#Add the Subject Line, Body, and Attachments
$MailMsg.Subject = "Enter your Subject Line Here"
$MailMsg.HTMLBody = "<style>p{font-size:100%;}</style><p>You can enter</p><p>A message formatted using HTML here.</p>"
$MailMsg.Attachments.Add("FullPath\ToYourFile\GoesHere") | Out-Null

#Defers the delivery date to today's date at 1600, useful if you do not want emails to send right away.
$DeferredDate = Get-Date -Hour 16 -Format ("MM/dd/yyyy hh:mm:ss tt")

#Set the deffered delivery datetime and send the email.
$MailMsg.DeferredDeliveryTime = $DeferredDate
$MailMsg.Send()

If you want to BCC recipients, just change:

$CCs.Type = [Microsoft.Office.Interop.Outlook.OlMailRecipientType]olCC

to:

$CCs.Type = [Microsoft.Office.Interop.Outlook.OlMailRecipientType]olBCC

Conclusion

I've used this code as a base to write all sorts of scripts that send emails with attachments, it is perfect for invoice generation and reports. I am happy to answer any questions about this topic in the comments below, or feel free to contact me.