In this project, we set out to automate a tedious task: forwarding emails from Outlook. Using PowerShell and the Outlook COM API, we created a script that not only forwards emails but also ensures formatting and attachments are preserved — while avoiding common pitfalls like invalid recipients or redundant processing. This project came about when emails were sent to the wrong location and we were not able to use eDiscovery or similar tools to locate the emails due to how they were sent and what they were.

Project Goal

Automate the process of forwarding emails from a specific Outlook folder to the intended recipients. Requirements included:

  • Preserving the original sender and full HTML formatting
  • Maintaining all attachments
  • Skipping or cleaning up invalid email addresses
  • Avoiding throttling by pacing email sends
  • Moving processed messages into an "AlreadySent" folder

Technical Approach

The project used PowerShell with the Microsoft.Office.Interop.Outlook COM interface to programmatically access Outlook. Here's a high-level breakdown of what the script does:

  1. Connects to Outlook and selects a specified folder
  2. Iterates through each email in that folder
  3. Forwards the email using the built-in Forward() method
  4. Resolves the To and CC fields, filtering out any recipients without valid SMTP addresses
  5. Preserves HTML formatting using HTMLBody
  6. Sends the email with a 2-second delay between messages to avoid throttling
  7. Moves the original email to an "AlreadySent" folder

Handling Edge Cases

A few tricky aspects required special attention:

  • Invalid recipients: Exchange addresses that couldn’t be resolved were skipped, and the email was still sent to valid ones
  • Preserving HTML: We used HTMLBody instead of Body to retain formatting
  • Output control: Suppressed default PowerShell output to prevent verbose metadata (like raw message bodies) from being printed during script execution

Sample Snippet

# Helper function to resolve valid email addresses
function Get-ValidEmailAddress($recipient) {
    try {
        if ($recipient.AddressEntry.Type -eq "EX") {
            return $recipient.AddressEntry.GetExchangeUser().PrimarySmtpAddress
        } elseif ($recipient.AddressEntry.Type -eq "SMTP") {
            return $recipient.Address
        } else {
            return $null
        }
    } catch {
        Write-Host "Could not resolve address for recipient: $($recipient.AddressEntry.Name)" -ForegroundColor Yellow
        return $null
    }
}

Outcome

The final script runs smoothly, forwards messages as intended, and keeps the Outlook mailbox organized by archiving sent messages. It’s a great example of how scripting and automation can reduce repetitive work while handling real-world edge cases.

Most of the pitfalls we ran into were directly related to problems with mailboxes and how we wanted the original sender to be preserved. We opted for sending from a shared mailbox and letting users know that messages may be coming from an internal source they are not used to seeing. Below is the script if you need it!