AutomateMyJob
Back to BlogPowerShell

Send Outlook Emails with PowerShell: Complete Automation Guide

Chris Anderson12 min read

Send Outlook Emails with PowerShell: Complete Automation Guide

Email is the lifeblood of business communication, but sending the same types of emails over and over is a productivity killer. Weekly reports, status updates, reminder notifications—all of these can be automated with PowerShell and Outlook.

Today, we're building a complete email automation toolkit that lets you send emails programmatically, attach files, use templates, and even send to distribution lists—all from the command line or scheduled tasks.

What You'll Learn

  • How to send emails through Outlook using PowerShell COM objects
  • Creating reusable email templates with variable substitution
  • Attaching files and formatting HTML emails
  • Building a bulk email sender for reports and notifications
  • Scheduling automated email delivery

Prerequisites

  • Windows 10/11 with Microsoft Outlook installed
  • PowerShell 5.1 or higher
  • Outlook must be configured with at least one email account
  • Outlook should be running (or will be started by the script)

The Manual Pain

How many times have you done this?

  1. Open Outlook, click New Email
  2. Type the same recipient addresses you always use
  3. Copy the same subject line format
  4. Paste content, maybe update a date or number
  5. Attach the same type of file
  6. Click Send
  7. Repeat for the next recipient

It's not that each email takes long—it's that they add up. A weekly report to five stakeholders is 260 emails a year. Let's automate that.

The Automated Solution

We'll build scripts that can:

  1. Send simple emails with one command
  2. Use templates with placeholder substitution
  3. Attach files automatically
  4. Send HTML-formatted emails
  5. Process bulk sends from a CSV file

Step 1: Understanding Outlook COM Objects

PowerShell can control Outlook through COM (Component Object Model) automation. Here's the basic pattern:

powershell
1# Create Outlook application object
2$outlook = New-Object -ComObject Outlook.Application
3
4# Create a new mail item
5$mail = $outlook.CreateItem(0)  # 0 = Mail item
6
7# Set properties
8$mail.To = "recipient@example.com"
9$mail.Subject = "Test Subject"
10$mail.Body = "This is the email body"
11
12# Send the email
13$mail.Send()
14
15# Clean up
16[System.Runtime.InteropServices.Marshal]::ReleaseComObject($outlook) | Out-Null

That's it—the foundation for all Outlook automation. Now let's build on it.

Step 2: Creating a Reusable Send Function

Let's wrap the basics into a proper function with error handling:

powershell
1function Send-OutlookEmail {
2    [CmdletBinding()]
3    param (
4        [Parameter(Mandatory = $true)]
5        [string[]]$To,
6        
7        [Parameter(Mandatory = $true)]
8        [string]$Subject,
9        
10        [Parameter(Mandatory = $true)]
11        [string]$Body,
12        
13        [Parameter(Mandatory = $false)]
14        [string[]]$Cc,
15        
16        [Parameter(Mandatory = $false)]
17        [string[]]$Bcc,
18        
19        [Parameter(Mandatory = $false)]
20        [string[]]$Attachments,
21        
22        [Parameter(Mandatory = $false)]
23        [switch]$IsHTML,
24        
25        [Parameter(Mandatory = $false)]
26        [ValidateSet("Low", "Normal", "High")]
27        [string]$Priority = "Normal"
28    )
29    
30    try {
31        # Create Outlook application
32        $outlook = New-Object -ComObject Outlook.Application
33        $mail = $outlook.CreateItem(0)
34        
35        # Set recipients
36        $mail.To = $To -join ";"
37        
38        if ($Cc) {
39            $mail.Cc = $Cc -join ";"
40        }
41        
42        if ($Bcc) {
43            $mail.Bcc = $Bcc -join ";"
44        }
45        
46        # Set subject and body
47        $mail.Subject = $Subject
48        
49        if ($IsHTML) {
50            $mail.HTMLBody = $Body
51        }
52        else {
53            $mail.Body = $Body
54        }
55        
56        # Set priority
57        $mail.Importance = switch ($Priority) {
58            "Low" { 0 }
59            "Normal" { 1 }
60            "High" { 2 }
61        }
62        
63        # Add attachments
64        foreach ($attachment in $Attachments) {
65            if (Test-Path $attachment) {
66                $mail.Attachments.Add($attachment) | Out-Null
67            }
68            else {
69                Write-Warning "Attachment not found: $attachment"
70            }
71        }
72        
73        # Send the email
74        $mail.Send()
75        
76        Write-Host "Email sent successfully to: $($To -join ', ')" -ForegroundColor Green
77        return $true
78    }
79    catch {
80        Write-Error "Failed to send email: $_"
81        return $false
82    }
83    finally {
84        # Clean up COM objects
85        if ($mail) {
86            [System.Runtime.InteropServices.Marshal]::ReleaseComObject($mail) | Out-Null
87        }
88        if ($outlook) {
89            [System.Runtime.InteropServices.Marshal]::ReleaseComObject($outlook) | Out-Null
90        }
91        [System.GC]::Collect()
92        [System.GC]::WaitForPendingFinalizers()
93    }
94}

Now you can send emails with a single command:

powershell
1# Simple email
2Send-OutlookEmail -To "colleague@company.com" -Subject "Quick Update" -Body "The report is ready!"
3
4# With attachment
5Send-OutlookEmail -To "boss@company.com" -Subject "Weekly Report" `
6    -Body "Please find the weekly report attached." `
7    -Attachments "C:\Reports\WeeklyReport.xlsx"
8
9# HTML email with high priority
10Send-OutlookEmail -To "team@company.com" -Subject "URGENT: System Update" `
11    -Body "<h1>System Maintenance</h1><p>Systems will be down tonight.</p>" `
12    -IsHTML -Priority "High"

Step 3: Email Templates with Variables

For recurring emails, templates save time and ensure consistency:

powershell
1function Send-TemplatedEmail {
2    [CmdletBinding()]
3    param (
4        [Parameter(Mandatory = $true)]
5        [string[]]$To,
6        
7        [Parameter(Mandatory = $true)]
8        [string]$TemplateFile,
9        
10        [Parameter(Mandatory = $false)]
11        [hashtable]$Variables = @{},
12        
13        [Parameter(Mandatory = $false)]
14        [string[]]$Attachments
15    )
16    
17    # Load template
18    if (-not (Test-Path $TemplateFile)) {
19        throw "Template file not found: $TemplateFile"
20    }
21    
22    $template = Get-Content $TemplateFile -Raw
23    
24    # Parse template header (first lines until blank line)
25    $sections = $template -split "`r?`n`r?`n", 2
26    $headerLines = $sections[0] -split "`r?`n"
27    $bodyContent = $sections[1]
28    
29    # Extract subject from header
30    $subject = ""
31    $isHTML = $false
32    
33    foreach ($line in $headerLines) {
34        if ($line -match "^Subject:\s*(.+)$") {
35            $subject = $Matches[1]
36        }
37        if ($line -match "^Format:\s*HTML") {
38            $isHTML = $true
39        }
40    }
41    
42    # Replace variables in subject and body
43    foreach ($key in $Variables.Keys) {
44        $placeholder = "{{$key}}"
45        $subject = $subject -replace [regex]::Escape($placeholder), $Variables[$key]
46        $bodyContent = $bodyContent -replace [regex]::Escape($placeholder), $Variables[$key]
47    }
48    
49    # Add common variables
50    $bodyContent = $bodyContent -replace "{{Date}}", (Get-Date -Format "MMMM d, yyyy")
51    $bodyContent = $bodyContent -replace "{{Time}}", (Get-Date -Format "h:mm tt")
52    $bodyContent = $bodyContent -replace "{{DayOfWeek}}", (Get-Date -Format "dddd")
53    
54    # Send the email
55    Send-OutlookEmail -To $To -Subject $subject -Body $bodyContent `
56        -IsHTML:$isHTML -Attachments $Attachments
57}

Create template files like this:

Prompt
Subject: Weekly Status Report - {{Date}}
Format: HTML

<html>
<body style="font-family: Arial, sans-serif;">
<h2>Weekly Status Report</h2>
<p>Hi {{RecipientName}},</p>

<p>Here is your weekly status report for the week ending {{Date}}.</p>

<h3>Summary</h3>
<ul>
    <li>Tasks Completed: {{TasksCompleted}}</li>
    <li>Tasks In Progress: {{TasksInProgress}}</li>
    <li>Blockers: {{Blockers}}</li>
</ul>

<p>Please let me know if you have any questions.</p>

<p>Best regards,<br>
{{SenderName}}</p>
</body>
</html>

Use it like this:

powershell
1$variables = @{
2    RecipientName = "Sarah"
3    TasksCompleted = "12"
4    TasksInProgress = "5"
5    Blockers = "None"
6    SenderName = "Chris"
7}
8
9Send-TemplatedEmail -To "sarah@company.com" `
10    -TemplateFile "C:\Templates\WeeklyReport.html" `
11    -Variables $variables `
12    -Attachments "C:\Reports\WeeklyData.xlsx"

Step 4: Bulk Email from CSV

For sending to multiple recipients with personalized content:

powershell
1function Send-BulkEmails {
2    [CmdletBinding()]
3    param (
4        [Parameter(Mandatory = $true)]
5        [string]$CsvFile,
6        
7        [Parameter(Mandatory = $true)]
8        [string]$TemplateFile,
9        
10        [Parameter(Mandatory = $false)]
11        [string]$AttachmentColumn,
12        
13        [Parameter(Mandatory = $false)]
14        [int]$DelaySeconds = 2,
15        
16        [Parameter(Mandatory = $false)]
17        [switch]$WhatIf
18    )
19    
20    # Load CSV
21    $recipients = Import-Csv $CsvFile
22    
23    Write-Host "Found $($recipients.Count) recipients" -ForegroundColor Cyan
24    
25    $sent = 0
26    $failed = 0
27    
28    foreach ($recipient in $recipients) {
29        # Build variables from CSV columns
30        $variables = @{}
31        foreach ($property in $recipient.PSObject.Properties) {
32            $variables[$property.Name] = $property.Value
33        }
34        
35        # Get attachment if specified
36        $attachment = $null
37        if ($AttachmentColumn -and $recipient.$AttachmentColumn) {
38            $attachment = $recipient.$AttachmentColumn
39        }
40        
41        if ($WhatIf) {
42            Write-Host "[WhatIf] Would send to: $($recipient.Email)" -ForegroundColor Yellow
43            continue
44        }
45        
46        try {
47            Send-TemplatedEmail -To $recipient.Email `
48                -TemplateFile $TemplateFile `
49                -Variables $variables `
50                -Attachments $attachment
51            
52            $sent++
53            Write-Host "Sent to: $($recipient.Email)" -ForegroundColor Green
54            
55            # Delay between sends to avoid overwhelming Outlook
56            if ($DelaySeconds -gt 0 -and $sent -lt $recipients.Count) {
57                Start-Sleep -Seconds $DelaySeconds
58            }
59        }
60        catch {
61            $failed++
62            Write-Warning "Failed to send to $($recipient.Email): $_"
63        }
64    }
65    
66    Write-Host "`nBulk send complete: $sent sent, $failed failed" -ForegroundColor Cyan
67}

Create a CSV like this (recipients.csv):

csv
1Email,Name,Department,ReportPath
2john@company.com,John Smith,Engineering,C:\Reports\Engineering.pdf
3sarah@company.com,Sarah Johnson,Marketing,C:\Reports\Marketing.pdf
4mike@company.com,Mike Brown,Sales,C:\Reports\Sales.pdf

And send:

powershell
1Send-BulkEmails -CsvFile "C:\Data\recipients.csv" `
2    -TemplateFile "C:\Templates\MonthlyReport.html" `
3    -AttachmentColumn "ReportPath" `
4    -DelaySeconds 3

The Complete Script

Here's the full, production-ready email automation module:

powershell
1<#
2.SYNOPSIS
3    PowerShell module for automating Outlook email sending.
4
5.DESCRIPTION
6    Provides functions to:
7    - Send simple and HTML emails through Outlook
8    - Use email templates with variable substitution
9    - Send bulk personalized emails from CSV files
10    - Attach files and set priority
11
12.EXAMPLE
13    Send-OutlookEmail -To "user@example.com" -Subject "Test" -Body "Hello!"
14
15.EXAMPLE
16    Send-BulkEmails -CsvFile "recipients.csv" -TemplateFile "template.html"
17
18.NOTES
19    Author: Chris Anderson
20    Date: 2025-11-26
21    Version: 1.0
22    Requires: PowerShell 5.1+, Microsoft Outlook
23#>
24
25# ============================================================================
26# Configuration
27# ============================================================================
28
29$script:LogPath = "$env:USERPROFILE\Documents\EmailLogs"
30$script:LogFile = Join-Path $script:LogPath "EmailLog_$(Get-Date -Format 'yyyy-MM').log"
31
32# Ensure log directory exists
33if (-not (Test-Path $script:LogPath)) {
34    New-Item -ItemType Directory -Path $script:LogPath -Force | Out-Null
35}
36
37# ============================================================================
38# Helper Functions
39# ============================================================================
40
41function Write-EmailLog {
42    param (
43        [string]$Message,
44        [string]$Level = "INFO"
45    )
46    
47    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
48    $logMessage = "[$timestamp] [$Level] $Message"
49    Add-Content -Path $script:LogFile -Value $logMessage
50}
51
52# ============================================================================
53# Main Functions
54# ============================================================================
55
56function Send-OutlookEmail {
57    <#
58    .SYNOPSIS
59        Sends an email through Microsoft Outlook.
60    
61    .PARAMETER To
62        One or more recipient email addresses.
63    
64    .PARAMETER Subject
65        Email subject line.
66    
67    .PARAMETER Body
68        Email body content (plain text or HTML).
69    
70    .PARAMETER Cc
71        Carbon copy recipients.
72    
73    .PARAMETER Bcc
74        Blind carbon copy recipients.
75    
76    .PARAMETER Attachments
77        File paths to attach to the email.
78    
79    .PARAMETER IsHTML
80        Treat the body as HTML content.
81    
82    .PARAMETER Priority
83        Email priority: Low, Normal, or High.
84    
85    .PARAMETER SaveToSentItems
86        Save a copy in Sent Items folder.
87    
88    .EXAMPLE
89        Send-OutlookEmail -To "user@example.com" -Subject "Hello" -Body "Hi there!"
90    #>
91    [CmdletBinding()]
92    param (
93        [Parameter(Mandatory = $true)]
94        [string[]]$To,
95        
96        [Parameter(Mandatory = $true)]
97        [string]$Subject,
98        
99        [Parameter(Mandatory = $true)]
100        [string]$Body,
101        
102        [Parameter(Mandatory = $false)]
103        [string[]]$Cc,
104        
105        [Parameter(Mandatory = $false)]
106        [string[]]$Bcc,
107        
108        [Parameter(Mandatory = $false)]
109        [string[]]$Attachments,
110        
111        [Parameter(Mandatory = $false)]
112        [switch]$IsHTML,
113        
114        [Parameter(Mandatory = $false)]
115        [ValidateSet("Low", "Normal", "High")]
116        [string]$Priority = "Normal",
117        
118        [Parameter(Mandatory = $false)]
119        [switch]$SaveToSentItems = $true
120    )
121    
122    $outlook = $null
123    $mail = $null
124    
125    try {
126        # Create Outlook application
127        $outlook = New-Object -ComObject Outlook.Application
128        $mail = $outlook.CreateItem(0)
129        
130        # Set recipients
131        $mail.To = $To -join ";"
132        
133        if ($Cc) {
134            $mail.Cc = $Cc -join ";"
135        }
136        
137        if ($Bcc) {
138            $mail.Bcc = $Bcc -join ";"
139        }
140        
141        # Set subject and body
142        $mail.Subject = $Subject
143        
144        if ($IsHTML) {
145            $mail.HTMLBody = $Body
146        }
147        else {
148            $mail.Body = $Body
149        }
150        
151        # Set priority (0=Low, 1=Normal, 2=High)
152        $mail.Importance = switch ($Priority) {
153            "Low" { 0 }
154            "Normal" { 1 }
155            "High" { 2 }
156        }
157        
158        # Add attachments
159        if ($Attachments) {
160            foreach ($attachment in $Attachments) {
161                if (Test-Path $attachment) {
162                    $mail.Attachments.Add($attachment) | Out-Null
163                    Write-Verbose "Attached: $attachment"
164                }
165                else {
166                    Write-Warning "Attachment not found: $attachment"
167                }
168            }
169        }
170        
171        # Send the email
172        $mail.Send()
173        
174        # Log success
175        Write-EmailLog "Sent email to: $($To -join ', ') | Subject: $Subject"
176        Write-Host "Email sent successfully to: $($To -join ', ')" -ForegroundColor Green
177        
178        return $true
179    }
180    catch {
181        Write-EmailLog "Failed to send email: $_" -Level "ERROR"
182        Write-Error "Failed to send email: $_"
183        return $false
184    }
185    finally {
186        # Clean up COM objects
187        if ($mail) {
188            [System.Runtime.InteropServices.Marshal]::ReleaseComObject($mail) | Out-Null
189        }
190        if ($outlook) {
191            [System.Runtime.InteropServices.Marshal]::ReleaseComObject($outlook) | Out-Null
192        }
193        [System.GC]::Collect()
194        [System.GC]::WaitForPendingFinalizers()
195    }
196}
197
198function Send-TemplatedEmail {
199    <#
200    .SYNOPSIS
201        Sends an email using a template file with variable substitution.
202    
203    .PARAMETER To
204        Recipient email addresses.
205    
206    .PARAMETER TemplateFile
207        Path to the template file.
208    
209    .PARAMETER Variables
210        Hashtable of variables to substitute in the template.
211    
212    .PARAMETER Attachments
213        Files to attach.
214    
215    .EXAMPLE
216        Send-TemplatedEmail -To "user@example.com" -TemplateFile "template.html" -Variables @{Name="John"}
217    #>
218    [CmdletBinding()]
219    param (
220        [Parameter(Mandatory = $true)]
221        [string[]]$To,
222        
223        [Parameter(Mandatory = $true)]
224        [ValidateScript({ Test-Path $_ })]
225        [string]$TemplateFile,
226        
227        [Parameter(Mandatory = $false)]
228        [hashtable]$Variables = @{},
229        
230        [Parameter(Mandatory = $false)]
231        [string[]]$Attachments
232    )
233    
234    # Load template
235    $template = Get-Content $TemplateFile -Raw
236    
237    # Parse template header
238    $sections = $template -split "`r?`n`r?`n", 2
239    $headerLines = $sections[0] -split "`r?`n"
240    $bodyContent = if ($sections.Count -gt 1) { $sections[1] } else { $template }
241    
242    # Extract subject and format from header
243    $subject = "No Subject"
244    $isHTML = $false
245    
246    foreach ($line in $headerLines) {
247        if ($line -match "^Subject:\s*(.+)$") {
248            $subject = $Matches[1].Trim()
249        }
250        if ($line -match "^Format:\s*HTML") {
251            $isHTML = $true
252        }
253    }
254    
255    # If no header found, use entire template as body
256    if ($subject -eq "No Subject" -and -not $headerLines[0].StartsWith("Subject:")) {
257        $bodyContent = $template
258    }
259    
260    # Replace custom variables
261    foreach ($key in $Variables.Keys) {
262        $placeholder = "{{$key}}"
263        $subject = $subject -replace [regex]::Escape($placeholder), $Variables[$key]
264        $bodyContent = $bodyContent -replace [regex]::Escape($placeholder), $Variables[$key]
265    }
266    
267    # Replace built-in variables
268    $builtInVars = @{
269        "Date" = (Get-Date -Format "MMMM d, yyyy")
270        "ShortDate" = (Get-Date -Format "MM/dd/yyyy")
271        "Time" = (Get-Date -Format "h:mm tt")
272        "DayOfWeek" = (Get-Date -Format "dddd")
273        "Month" = (Get-Date -Format "MMMM")
274        "Year" = (Get-Date -Format "yyyy")
275        "ComputerName" = $env:COMPUTERNAME
276        "UserName" = $env:USERNAME
277    }
278    
279    foreach ($key in $builtInVars.Keys) {
280        $placeholder = "{{$key}}"
281        $subject = $subject -replace [regex]::Escape($placeholder), $builtInVars[$key]
282        $bodyContent = $bodyContent -replace [regex]::Escape($placeholder), $builtInVars[$key]
283    }
284    
285    # Send the email
286    Send-OutlookEmail -To $To -Subject $subject -Body $bodyContent `
287        -IsHTML:$isHTML -Attachments $Attachments
288}
289
290function Send-BulkEmails {
291    <#
292    .SYNOPSIS
293        Sends personalized emails to multiple recipients from a CSV file.
294    
295    .PARAMETER CsvFile
296        Path to CSV file with recipient data.
297    
298    .PARAMETER TemplateFile
299        Path to email template.
300    
301    .PARAMETER AttachmentColumn
302        CSV column name containing attachment paths.
303    
304    .PARAMETER DelaySeconds
305        Seconds to wait between sends.
306    
307    .PARAMETER WhatIf
308        Preview without sending.
309    
310    .EXAMPLE
311        Send-BulkEmails -CsvFile "recipients.csv" -TemplateFile "template.html"
312    #>
313    [CmdletBinding(SupportsShouldProcess)]
314    param (
315        [Parameter(Mandatory = $true)]
316        [ValidateScript({ Test-Path $_ })]
317        [string]$CsvFile,
318        
319        [Parameter(Mandatory = $true)]
320        [ValidateScript({ Test-Path $_ })]
321        [string]$TemplateFile,
322        
323        [Parameter(Mandatory = $false)]
324        [string]$AttachmentColumn,
325        
326        [Parameter(Mandatory = $false)]
327        [ValidateRange(0, 60)]
328        [int]$DelaySeconds = 2
329    )
330    
331    # Load CSV
332    $recipients = Import-Csv $CsvFile
333    
334    if ($recipients.Count -eq 0) {
335        Write-Warning "No recipients found in CSV file"
336        return
337    }
338    
339    # Verify Email column exists
340    if (-not ($recipients[0].PSObject.Properties.Name -contains "Email")) {
341        throw "CSV must contain an 'Email' column"
342    }
343    
344    Write-Host "`nBulk Email Sender" -ForegroundColor Cyan
345    Write-Host "=================" -ForegroundColor Cyan
346    Write-Host "Recipients: $($recipients.Count)" -ForegroundColor White
347    Write-Host "Template: $TemplateFile" -ForegroundColor White
348    Write-Host ""
349    
350    $sent = 0
351    $failed = 0
352    $skipped = 0
353    
354    foreach ($recipient in $recipients) {
355        # Skip if no email
356        if ([string]::IsNullOrWhiteSpace($recipient.Email)) {
357            $skipped++
358            Write-Warning "Skipping row with empty email"
359            continue
360        }
361        
362        # Build variables from all CSV columns
363        $variables = @{}
364        foreach ($property in $recipient.PSObject.Properties) {
365            $variables[$property.Name] = $property.Value
366        }
367        
368        # Get attachment if column specified
369        $attachments = @()
370        if ($AttachmentColumn -and $recipient.$AttachmentColumn) {
371            if (Test-Path $recipient.$AttachmentColumn) {
372                $attachments = @($recipient.$AttachmentColumn)
373            }
374            else {
375                Write-Warning "Attachment not found for $($recipient.Email): $($recipient.$AttachmentColumn)"
376            }
377        }
378        
379        # WhatIf mode
380        if ($PSCmdlet.ShouldProcess($recipient.Email, "Send email")) {
381            try {
382                Send-TemplatedEmail -To $recipient.Email `
383                    -TemplateFile $TemplateFile `
384                    -Variables $variables `
385                    -Attachments $attachments
386                
387                $sent++
388                
389                # Delay between sends
390                if ($DelaySeconds -gt 0 -and $sent -lt $recipients.Count) {
391                    Start-Sleep -Seconds $DelaySeconds
392                }
393            }
394            catch {
395                $failed++
396                Write-Warning "Failed to send to $($recipient.Email): $_"
397            }
398        }
399    }
400    
401    # Summary
402    Write-Host "`n========================" -ForegroundColor Cyan
403    Write-Host "Bulk Send Complete" -ForegroundColor Cyan
404    Write-Host "========================" -ForegroundColor Cyan
405    Write-Host "Sent: $sent" -ForegroundColor Green
406    Write-Host "Failed: $failed" -ForegroundColor $(if ($failed -gt 0) { "Red" } else { "White" })
407    Write-Host "Skipped: $skipped" -ForegroundColor $(if ($skipped -gt 0) { "Yellow" } else { "White" })
408    
409    Write-EmailLog "Bulk send completed: $sent sent, $failed failed, $skipped skipped"
410}
411
412function New-EmailTemplate {
413    <#
414    .SYNOPSIS
415        Creates a new email template file with standard structure.
416    
417    .PARAMETER Path
418        Where to save the template.
419    
420    .PARAMETER Name
421        Template name for the subject line.
422    
423    .PARAMETER HTML
424        Create an HTML template.
425    
426    .EXAMPLE
427        New-EmailTemplate -Path "C:\Templates\Welcome.html" -Name "Welcome" -HTML
428    #>
429    [CmdletBinding()]
430    param (
431        [Parameter(Mandatory = $true)]
432        [string]$Path,
433        
434        [Parameter(Mandatory = $false)]
435        [string]$Name = "Template",
436        
437        [Parameter(Mandatory = $false)]
438        [switch]$HTML
439    )
440    
441    if ($HTML) {
442        $content = @"
443Subject: $Name - {{Date}}
444Format: HTML
445
446<!DOCTYPE html>
447<html>
448<head>
449    <style>
450        body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
451        .header { background-color: #0078d4; color: white; padding: 20px; }
452        .content { padding: 20px; }
453        .footer { background-color: #f5f5f5; padding: 10px; font-size: 12px; color: #666; }
454    </style>
455</head>
456<body>
457    <div class="header">
458        <h1>$Name</h1>
459    </div>
460    <div class="content">
461        <p>Hi {{Name}},</p>
462        
463        <p>Your content goes here. You can use variables like:</p>
464        <ul>
465            <li>{{Name}} - Recipient name</li>
466            <li>{{Date}} - Current date</li>
467            <li>{{Time}} - Current time</li>
468            <li>{{DayOfWeek}} - Day of the week</li>
469        </ul>
470        
471        <p>Best regards,<br>
472        {{SenderName}}</p>
473    </div>
474    <div class="footer">
475        <p>This email was generated automatically on {{Date}} at {{Time}}.</p>
476    </div>
477</body>
478</html>
479"@
480    }
481    else {
482        $content = @"
483Subject: $Name - {{Date}}
484Format: Plain
485
486Hi {{Name}},
487
488Your content goes here. You can use variables like:
489- {{Name}} - Recipient name
490- {{Date}} - Current date  
491- {{Time}} - Current time
492- {{DayOfWeek}} - Day of the week
493
494Best regards,
495{{SenderName}}
496"@
497    }
498    
499    $content | Out-File -FilePath $Path -Encoding UTF8
500    Write-Host "Template created: $Path" -ForegroundColor Green
501}
502
503# ============================================================================
504# Export Functions
505# ============================================================================
506
507Export-ModuleMember -Function Send-OutlookEmail, Send-TemplatedEmail, Send-BulkEmails, New-EmailTemplate

How to Run This Script

Method 1: Dot Source the Functions

powershell
1# Load the functions
2. .\OutlookEmail.ps1
3
4# Now use them
5Send-OutlookEmail -To "colleague@company.com" -Subject "Test" -Body "Hello!"

Method 2: Save as Module

Save as OutlookEmail.psm1 in your modules folder:

powershell
1# Find your modules folder
2$env:PSModulePath -split ";"
3
4# Save to: Documents\PowerShell\Modules\OutlookEmail\OutlookEmail.psm1
5# Then import
6Import-Module OutlookEmail

Method 3: Scheduled Reports

Create a script that sends weekly reports:

powershell
1# WeeklySend.ps1
2. C:\Scripts\OutlookEmail.ps1
3
4Send-TemplatedEmail -To "team@company.com" `
5    -TemplateFile "C:\Templates\WeeklyReport.html" `
6    -Variables @{
7        ProjectName = "Q4 Initiative"
8        StatusSummary = "On track"
9    } `
10    -Attachments "C:\Reports\Weekly_$(Get-Date -Format 'yyyy-MM-dd').xlsx"

Schedule with Task Scheduler to run every Friday at 4 PM.

Customization Options

ParameterDefaultDescription
To(Required)Recipient email addresses
Subject(Required)Email subject line
Body(Required)Email content
Cc(Optional)Carbon copy recipients
Bcc(Optional)Blind carbon copy
Attachments(Optional)Files to attach
IsHTML$falseSend as HTML email
PriorityNormalLow, Normal, or High

Template Variables

Built-in variables available in templates:

VariableDescription
{{Date}}Full date (January 1, 2025)
{{ShortDate}}Short date (01/01/2025)
{{Time}}Current time (3:30 PM)
{{DayOfWeek}}Day name (Monday)
{{Month}}Month name (January)
{{Year}}Year (2025)

Security Considerations

⚠️ Important notes:

  • Never hardcode email credentials in scripts
  • Scripts run with your Outlook profile's permissions
  • Be careful with bulk sends—rate limits may apply
  • Log files may contain recipient information—secure them
  • Test thoroughly before sending to production recipients
  • Use BCC for large recipient lists to protect privacy

Common Issues & Solutions

IssueCauseSolution
"Cannot create COM object"Outlook not installedInstall Microsoft Outlook
"Operation failed"Outlook not runningStart Outlook first, or script will start it
Emails stuck in OutboxNetwork/auth issuesCheck Outlook send/receive
Attachments not sendingInvalid pathsVerify file paths exist
HTML not renderingMissing IsHTML flagAdd -IsHTML parameter
Rate limitingToo many sendsIncrease DelaySeconds

Taking It Further

Extend the email automation with:

  • Email queue: Save emails to send later
  • Read receipts: Request delivery confirmation
  • Calendar integration: Send meeting invites
  • Reply monitoring: Process incoming responses
  • Signature management: Dynamic email signatures
  • Distribution lists: Manage recipient groups

Conclusion

You've built a complete email automation toolkit that transforms how you handle recurring email tasks. From simple one-liners to complex bulk sends with personalization, PowerShell and Outlook together make email automation accessible and powerful.

The key insight is that email, despite being "communication," often follows predictable patterns. Status reports, notifications, reminders—these are perfect automation candidates. Instead of manually crafting the same emails, define them once as templates and let the script do the repetitive work.

Start with a simple use case—maybe a weekly report that you always forget to send on time. Automate that, schedule it, and move on to the next candidate. Before long, you'll have hours back in your week.

Your Outlook just got a lot more powerful. Happy emailing!

Sponsored Content

Interested in advertising? Reach automation professionals through our platform.

Share this article