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?
- Open Outlook, click New Email
- Type the same recipient addresses you always use
- Copy the same subject line format
- Paste content, maybe update a date or number
- Attach the same type of file
- Click Send
- 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:
- Send simple emails with one command
- Use templates with placeholder substitution
- Attach files automatically
- Send HTML-formatted emails
- 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:
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-NullThat'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:
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:
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:
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:
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:
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:
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):
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.pdfAnd send:
1Send-BulkEmails -CsvFile "C:\Data\recipients.csv" `
2 -TemplateFile "C:\Templates\MonthlyReport.html" `
3 -AttachmentColumn "ReportPath" `
4 -DelaySeconds 3The Complete Script
Here's the full, production-ready email automation module:
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-EmailTemplateHow to Run This Script
Method 1: Dot Source the Functions
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:
1# Find your modules folder
2$env:PSModulePath -split ";"
3
4# Save to: Documents\PowerShell\Modules\OutlookEmail\OutlookEmail.psm1
5# Then import
6Import-Module OutlookEmailMethod 3: Scheduled Reports
Create a script that sends weekly reports:
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
| Parameter | Default | Description |
|---|---|---|
| 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 | $false | Send as HTML email |
| Priority | Normal | Low, Normal, or High |
Template Variables
Built-in variables available in templates:
| Variable | Description |
|---|---|
| {{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
| Issue | Cause | Solution |
|---|---|---|
| "Cannot create COM object" | Outlook not installed | Install Microsoft Outlook |
| "Operation failed" | Outlook not running | Start Outlook first, or script will start it |
| Emails stuck in Outbox | Network/auth issues | Check Outlook send/receive |
| Attachments not sending | Invalid paths | Verify file paths exist |
| HTML not rendering | Missing IsHTML flag | Add -IsHTML parameter |
| Rate limiting | Too many sends | Increase 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.