Skip to content

Instantly share code, notes, and snippets.

@asheroto
Last active January 25, 2024 21:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asheroto/ed31bb13a0eb59cef22abd247eea89f3 to your computer and use it in GitHub Desktop.
Save asheroto/ed31bb13a0eb59cef22abd247eea89f3 to your computer and use it in GitHub Desktop.
Create e-mails in Outlook using PowerShell. Select account, keep default signature, keep original formatting, produce valid HTML.

Creating E-mails in Outlook using PowerShell

  • Optionally specify the sending account in Outlook (if you have multiple accounts)
  • Keep the default signature associated with the account
  • Retain the original formatting
  • Produce valid HTML-formatted e-mails

After several hours of working with Outlook's COM library through PowerShell, I figured out the trick to get .SendUsingAccount working (select a sending account) as well as keeping Outlook's default signature for the specified account.

Parameters

Parameter Type Description Mandatory
To string The recipient's email address. Yes
Subject string The subject line of the email. Yes
Body string The HTML formatted string that makes up the body of the email. Yes
FromEmail string The email address of the Outlook account to use for sending. If not specified, uses default account. No

Note: The Body parameter expects an HTML formatted string. The FromEmail parameter is optional; if not provided, the default Outlook account is used.

Usage

Direct Parameter Specification

Create-NewOutlookEmail -To "recipient@domain.com" -Subject "test subject" -Body "this is a <strong>test</strong>" -FromEmail "sender@domain.com"

Example using Variables

$To = "recipient@domain.com"
$Subject = "test subject"
$Body = "this is a <strong>test</strong>"
$FromEmail = "sender@domain.com"
Create-NewOutlookEmail -To $To -Subject $Subject -Body $Body -FromEmail $FromEmail

Example Using Hash Table

$params = @{
    To = "recipient@domain.com"
    Subject = "test subject"
    Body = "this is a <strong>test</strong>"
    FromEmail = "sender@domain.com"
}
Create-NewOutlookEmail @params

How it Works

Note

The information below is only provided to explain how the script works. You don't need the information below if you simply want to use the script.

The PowerShell script makes use of the Outlook.Application COM object library. It creates a new mail item, specifies the To, Subject, and then injects the HTML body into the appropriate section of the message body.

How It Retains the Appropriate Account Signature

Many online solutions suggest extracting the HTML of the signature, or prepending it to the HTMLBody, but this results in invalid HTML as shown below...

this is a <strong>test</strong><html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head>....

Instead, the script identifies an empty area where Outlook expects the content, immediately following this tag...

<div class=WordSection1><p class=MsoNormal>

That way we end up with the full message with valid HTML as Outlook expects. 😊

Some may say that the <o:p> tag should be included, but that tag is considered Office namespace which is typically used for content created by Office applications, not by users (at least, that's my understanding).

The trick to use .SendUsingAccount (fixes account selection + signature)

Although using .SendUsingAccount works as is, you'll find that it doesn't actually select the account in Outlook, nor is the default signature for the account invoked by doing so. Some have suggested using .SentOnBehalfOfName as an alternative, but this approach won't switch the selected account or prompt the signature to generate.

To workaround the issue, this script uses a custom function named Invoke-SetProperty. This function uses .InvokeMember to dynamically set properties on the $mail object, which is $outlook.CreateItem(0) where $outlook is the COM object of Outlook.

function Invoke-SetProperty {
    param(
        [__ComObject] $Object,
        [String] $Property,
        $Value
    )
    [Void] $Object.GetType().InvokeMember($Property, "SetProperty", $NULL, $Object, $Value)
}

If you notice, the e-mail is actually displayed before the HTMLBody modifications take place. This allows time for Outlook to generate the default HTML message body as well as insert the default signature for the associated account.

Message Body is Not Setting Custom HTML in Variable, Reverts to Default Template

This is a weird issue. When setting HTML defined in a variable, for some reason Outlook reverts the HTML body back to the default template.

This works:

$mail.HTMLBody = "this is a <strong>test</strong"

But when I tried this:

$Message = "this is a <strong>test</strong"
$mail.HTMLBody = $Message

It would revert the message to the default template, which in my case is this:

<HTML>
<HEAD>
<META NAME="Generator" CONTENT="MS Exchange Server version
14.02.5004.000">
<TITLE></TITLE>
</HEAD>
<BODY>
<!-- Converted from text/plain format -->
 
</BODY>
</HTML>

This issue, possibly due to a PowerShell bug or string misinterpretation, is resolved by specifying the $Body variable in braced notation as ${Body}.

function Create-NewOutlookEmail {
param (
[Parameter(Mandatory = $true)]
[string]$To,
[Parameter(Mandatory = $true)]
[string]$Subject,
[Parameter(Mandatory = $true)]
[string]$Body, # Expecting HTML formatted string
[string]$FromEmail # Email address of the Outlook account to use
)
function Invoke-SetProperty {
param(
[__ComObject] $Object,
[String] $Property,
$Value
)
[Void] $Object.GetType().InvokeMember($Property, "SetProperty", $NULL, $Object, $Value)
}
try {
# Create an Outlook Application instance
$outlook = New-Object -ComObject Outlook.Application
$mail = $outlook.CreateItem(0)
$account = $outlook.Session.Accounts | Where-Object { $_.SmtpAddress -eq $FromEmail }
if ($account) {
# Use Invoke-SetProperty to set the SendUsingAccount
Write-Output "Using account: $FromEmail"
Invoke-SetProperty -Object $mail -Property "SendUsingAccount" -Value $account
} else {
Write-Output "Account not found: $FromEmail"
}
# Set the properties of the Mail Item
$mail.To = $To
$mail.Subject = $Subject
# Display the email
$mail.Display()
# Store original HTML body (uncomment for debugging)
#$originalBody = $mail.HTMLBody
# Insert the message body after the existing content
$mail.HTMLBody = $mail.HTMLBody -replace '(<div class=WordSection1><p class=MsoNormal>)', "`$1${Body}"
} catch {
Write-Output "Error: Unable to create email. Ensure that Outlook is installed and properly configured."
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment