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 object2$outlook = New-Object -ComObject Outlook.Application34# Create a new mail item5$mail = $outlook.CreateItem(0) # 0 = Mail item67# Set properties8$mail.To = "recipient@example.com"9$mail.Subject = "Test Subject"10$mail.Body = "This is the email body"1112# Send the email13$mail.Send()1415# Clean up16[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:
1function Send-OutlookEmail {2 [CmdletBinding()]3 param (4 [Parameter(Mandatory = $true)]5 [string[]]$To,67 [Parameter(Mandatory = $true)]8 [string]$Subject,910 [Parameter(Mandatory = $true)]11 [string]$Body,1213 [Parameter(Mandatory = $false)]14 [string[]]$Cc,1516 [Parameter(Mandatory = $false)]17 [string[]]$Bcc,1819 [Parameter(Mandatory = $false)]20 [string[]]$Attachments,2122 [Parameter(Mandatory = $false)]23 [switch]$IsHTML,2425 [Parameter(Mandatory = $false)]26 [ValidateSet("Low", "Normal", "High")]27 [string]$Priority = "Normal"28 )2930 try {31 # Create Outlook application32 $outlook = New-Object -ComObject Outlook.Application33 $mail = $outlook.CreateItem(0)3435 # Set recipients36 $mail.To = $To -join ";"3738 if ($Cc) {39 $mail.Cc = $Cc -join ";"40 }4142 if ($Bcc) {43 $mail.Bcc = $Bcc -join ";"44 }4546 # Set subject and body47 $mail.Subject = $Subject4849 if ($IsHTML) {50 $mail.HTMLBody = $Body51 }52 else {53 $mail.Body = $Body54 }5556 # Set priority57 $mail.Importance = switch ($Priority) {58 "Low" { 0 }59 "Normal" { 1 }60 "High" { 2 }61 }6263 # Add attachments64 foreach ($attachment in $Attachments) {65 if (Test-Path $attachment) {66 $mail.Attachments.Add($attachment) | Out-Null67 }68 else {69 Write-Warning "Attachment not found: $attachment"70 }71 }7273 # Send the email74 $mail.Send()7576 Write-Host "Email sent successfully to: $($To -join ', ')" -ForegroundColor Green77 return $true78 }79 catch {80 Write-Error "Failed to send email: $_"81 return $false82 }83 finally {84 # Clean up COM objects85 if ($mail) {86 [System.Runtime.InteropServices.Marshal]::ReleaseComObject($mail) | Out-Null87 }88 if ($outlook) {89 [System.Runtime.InteropServices.Marshal]::ReleaseComObject($outlook) | Out-Null90 }91 [System.GC]::Collect()92 [System.GC]::WaitForPendingFinalizers()93 }94}
Now you can send emails with a single command:
1# Simple email2Send-OutlookEmail -To "colleague@company.com" -Subject "Quick Update" -Body "The report is ready!"34# With attachment5Send-OutlookEmail -To "boss@company.com" -Subject "Weekly Report" `6 -Body "Please find the weekly report attached." `7 -Attachments "C:\Reports\WeeklyReport.xlsx"89# HTML email with high priority10Send-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,67 [Parameter(Mandatory = $true)]8 [string]$TemplateFile,910 [Parameter(Mandatory = $false)]11 [hashtable]$Variables = @{},1213 [Parameter(Mandatory = $false)]14 [string[]]$Attachments15 )1617 # Load template18 if (-not (Test-Path $TemplateFile)) {19 throw "Template file not found: $TemplateFile"20 }2122 $template = Get-Content $TemplateFile -Raw2324 # Parse template header (first lines until blank line)25 $sections = $template -split "`r?`n`r?`n", 226 $headerLines = $sections[0] -split "`r?`n"27 $bodyContent = $sections[1]2829 # Extract subject from header30 $subject = ""31 $isHTML = $false3233 foreach ($line in $headerLines) {34 if ($line -match "^Subject:\s*(.+)$") {35 $subject = $Matches[1]36 }37 if ($line -match "^Format:\s*HTML") {38 $isHTML = $true39 }40 }4142 # Replace variables in subject and body43 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 }4849 # Add common variables50 $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")5354 # Send the email55 Send-OutlookEmail -To $To -Subject $subject -Body $bodyContent `56 -IsHTML:$isHTML -Attachments $Attachments57}
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}89Send-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,67 [Parameter(Mandatory = $true)]8 [string]$TemplateFile,910 [Parameter(Mandatory = $false)]11 [string]$AttachmentColumn,1213 [Parameter(Mandatory = $false)]14 [int]$DelaySeconds = 2,1516 [Parameter(Mandatory = $false)]17 [switch]$WhatIf18 )1920 # Load CSV21 $recipients = Import-Csv $CsvFile2223 Write-Host "Found $($recipients.Count) recipients" -ForegroundColor Cyan2425 $sent = 026 $failed = 02728 foreach ($recipient in $recipients) {29 # Build variables from CSV columns30 $variables = @{}31 foreach ($property in $recipient.PSObject.Properties) {32 $variables[$property.Name] = $property.Value33 }3435 # Get attachment if specified36 $attachment = $null37 if ($AttachmentColumn -and $recipient.$AttachmentColumn) {38 $attachment = $recipient.$AttachmentColumn39 }4041 if ($WhatIf) {42 Write-Host "[WhatIf] Would send to: $($recipient.Email)" -ForegroundColor Yellow43 continue44 }4546 try {47 Send-TemplatedEmail -To $recipient.Email `48 -TemplateFile $TemplateFile `49 -Variables $variables `50 -Attachments $attachment5152 $sent++53 Write-Host "Sent to: $($recipient.Email)" -ForegroundColor Green5455 # Delay between sends to avoid overwhelming Outlook56 if ($DelaySeconds -gt 0 -and $sent -lt $recipients.Count) {57 Start-Sleep -Seconds $DelaySeconds58 }59 }60 catch {61 $failed++62 Write-Warning "Failed to send to $($recipient.Email): $_"63 }64 }6566 Write-Host "`nBulk send complete: $sent sent, $failed failed" -ForegroundColor Cyan67}
Create a CSV like this (recipients.csv):
1Email,Name,Department,ReportPath2john@company.com,John Smith,Engineering,C:\Reports\Engineering.pdf3sarah@company.com,Sarah Johnson,Marketing,C:\Reports\Marketing.pdf4mike@company.com,Mike Brown,Sales,C:\Reports\Sales.pdf
And send:
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:
1<#2.SYNOPSIS3 PowerShell module for automating Outlook email sending.45.DESCRIPTION6 Provides functions to:7 - Send simple and HTML emails through Outlook8 - Use email templates with variable substitution9 - Send bulk personalized emails from CSV files10 - Attach files and set priority1112.EXAMPLE13 Send-OutlookEmail -To "user@example.com" -Subject "Test" -Body "Hello!"1415.EXAMPLE16 Send-BulkEmails -CsvFile "recipients.csv" -TemplateFile "template.html"1718.NOTES19 Author: Chris Anderson20 Date: 2025-11-2621 Version: 1.022 Requires: PowerShell 5.1+, Microsoft Outlook23#>2425# ============================================================================26# Configuration27# ============================================================================2829$script:LogPath = "$env:USERPROFILE\Documents\EmailLogs"30$script:LogFile = Join-Path $script:LogPath "EmailLog_$(Get-Date -Format 'yyyy-MM').log"3132# Ensure log directory exists33if (-not (Test-Path $script:LogPath)) {34 New-Item -ItemType Directory -Path $script:LogPath -Force | Out-Null35}3637# ============================================================================38# Helper Functions39# ============================================================================4041function Write-EmailLog {42 param (43 [string]$Message,44 [string]$Level = "INFO"45 )4647 $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"48 $logMessage = "[$timestamp] [$Level] $Message"49 Add-Content -Path $script:LogFile -Value $logMessage50}5152# ============================================================================53# Main Functions54# ============================================================================5556function Send-OutlookEmail {57 <#58 .SYNOPSIS59 Sends an email through Microsoft Outlook.6061 .PARAMETER To62 One or more recipient email addresses.6364 .PARAMETER Subject65 Email subject line.6667 .PARAMETER Body68 Email body content (plain text or HTML).6970 .PARAMETER Cc71 Carbon copy recipients.7273 .PARAMETER Bcc74 Blind carbon copy recipients.7576 .PARAMETER Attachments77 File paths to attach to the email.7879 .PARAMETER IsHTML80 Treat the body as HTML content.8182 .PARAMETER Priority83 Email priority: Low, Normal, or High.8485 .PARAMETER SaveToSentItems86 Save a copy in Sent Items folder.8788 .EXAMPLE89 Send-OutlookEmail -To "user@example.com" -Subject "Hello" -Body "Hi there!"90 #>91 [CmdletBinding()]92 param (93 [Parameter(Mandatory = $true)]94 [string[]]$To,9596 [Parameter(Mandatory = $true)]97 [string]$Subject,9899 [Parameter(Mandatory = $true)]100 [string]$Body,101102 [Parameter(Mandatory = $false)]103 [string[]]$Cc,104105 [Parameter(Mandatory = $false)]106 [string[]]$Bcc,107108 [Parameter(Mandatory = $false)]109 [string[]]$Attachments,110111 [Parameter(Mandatory = $false)]112 [switch]$IsHTML,113114 [Parameter(Mandatory = $false)]115 [ValidateSet("Low", "Normal", "High")]116 [string]$Priority = "Normal",117118 [Parameter(Mandatory = $false)]119 [switch]$SaveToSentItems = $true120 )121122 $outlook = $null123 $mail = $null124125 try {126 # Create Outlook application127 $outlook = New-Object -ComObject Outlook.Application128 $mail = $outlook.CreateItem(0)129130 # Set recipients131 $mail.To = $To -join ";"132133 if ($Cc) {134 $mail.Cc = $Cc -join ";"135 }136137 if ($Bcc) {138 $mail.Bcc = $Bcc -join ";"139 }140141 # Set subject and body142 $mail.Subject = $Subject143144 if ($IsHTML) {145 $mail.HTMLBody = $Body146 }147 else {148 $mail.Body = $Body149 }150151 # Set priority (0=Low, 1=Normal, 2=High)152 $mail.Importance = switch ($Priority) {153 "Low" { 0 }154 "Normal" { 1 }155 "High" { 2 }156 }157158 # Add attachments159 if ($Attachments) {160 foreach ($attachment in $Attachments) {161 if (Test-Path $attachment) {162 $mail.Attachments.Add($attachment) | Out-Null163 Write-Verbose "Attached: $attachment"164 }165 else {166 Write-Warning "Attachment not found: $attachment"167 }168 }169 }170171 # Send the email172 $mail.Send()173174 # Log success175 Write-EmailLog "Sent email to: $($To -join ', ') | Subject: $Subject"176 Write-Host "Email sent successfully to: $($To -join ', ')" -ForegroundColor Green177178 return $true179 }180 catch {181 Write-EmailLog "Failed to send email: $_" -Level "ERROR"182 Write-Error "Failed to send email: $_"183 return $false184 }185 finally {186 # Clean up COM objects187 if ($mail) {188 [System.Runtime.InteropServices.Marshal]::ReleaseComObject($mail) | Out-Null189 }190 if ($outlook) {191 [System.Runtime.InteropServices.Marshal]::ReleaseComObject($outlook) | Out-Null192 }193 [System.GC]::Collect()194 [System.GC]::WaitForPendingFinalizers()195 }196}197198function Send-TemplatedEmail {199 <#200 .SYNOPSIS201 Sends an email using a template file with variable substitution.202203 .PARAMETER To204 Recipient email addresses.205206 .PARAMETER TemplateFile207 Path to the template file.208209 .PARAMETER Variables210 Hashtable of variables to substitute in the template.211212 .PARAMETER Attachments213 Files to attach.214215 .EXAMPLE216 Send-TemplatedEmail -To "user@example.com" -TemplateFile "template.html" -Variables @{Name="John"}217 #>218 [CmdletBinding()]219 param (220 [Parameter(Mandatory = $true)]221 [string[]]$To,222223 [Parameter(Mandatory = $true)]224 [ValidateScript({ Test-Path $_ })]225 [string]$TemplateFile,226227 [Parameter(Mandatory = $false)]228 [hashtable]$Variables = @{},229230 [Parameter(Mandatory = $false)]231 [string[]]$Attachments232 )233234 # Load template235 $template = Get-Content $TemplateFile -Raw236237 # Parse template header238 $sections = $template -split "`r?`n`r?`n", 2239 $headerLines = $sections[0] -split "`r?`n"240 $bodyContent = if ($sections.Count -gt 1) { $sections[1] } else { $template }241242 # Extract subject and format from header243 $subject = "No Subject"244 $isHTML = $false245246 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 = $true252 }253 }254255 # If no header found, use entire template as body256 if ($subject -eq "No Subject" -and -not $headerLines[0].StartsWith("Subject:")) {257 $bodyContent = $template258 }259260 # Replace custom variables261 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 }266267 # Replace built-in variables268 $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:COMPUTERNAME276 "UserName" = $env:USERNAME277 }278279 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 }284285 # Send the email286 Send-OutlookEmail -To $To -Subject $subject -Body $bodyContent `287 -IsHTML:$isHTML -Attachments $Attachments288}289290function Send-BulkEmails {291 <#292 .SYNOPSIS293 Sends personalized emails to multiple recipients from a CSV file.294295 .PARAMETER CsvFile296 Path to CSV file with recipient data.297298 .PARAMETER TemplateFile299 Path to email template.300301 .PARAMETER AttachmentColumn302 CSV column name containing attachment paths.303304 .PARAMETER DelaySeconds305 Seconds to wait between sends.306307 .PARAMETER WhatIf308 Preview without sending.309310 .EXAMPLE311 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,318319 [Parameter(Mandatory = $true)]320 [ValidateScript({ Test-Path $_ })]321 [string]$TemplateFile,322323 [Parameter(Mandatory = $false)]324 [string]$AttachmentColumn,325326 [Parameter(Mandatory = $false)]327 [ValidateRange(0, 60)]328 [int]$DelaySeconds = 2329 )330331 # Load CSV332 $recipients = Import-Csv $CsvFile333334 if ($recipients.Count -eq 0) {335 Write-Warning "No recipients found in CSV file"336 return337 }338339 # Verify Email column exists340 if (-not ($recipients[0].PSObject.Properties.Name -contains "Email")) {341 throw "CSV must contain an 'Email' column"342 }343344 Write-Host "`nBulk Email Sender" -ForegroundColor Cyan345 Write-Host "=================" -ForegroundColor Cyan346 Write-Host "Recipients: $($recipients.Count)" -ForegroundColor White347 Write-Host "Template: $TemplateFile" -ForegroundColor White348 Write-Host ""349350 $sent = 0351 $failed = 0352 $skipped = 0353354 foreach ($recipient in $recipients) {355 # Skip if no email356 if ([string]::IsNullOrWhiteSpace($recipient.Email)) {357 $skipped++358 Write-Warning "Skipping row with empty email"359 continue360 }361362 # Build variables from all CSV columns363 $variables = @{}364 foreach ($property in $recipient.PSObject.Properties) {365 $variables[$property.Name] = $property.Value366 }367368 # Get attachment if column specified369 $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 }378379 # WhatIf mode380 if ($PSCmdlet.ShouldProcess($recipient.Email, "Send email")) {381 try {382 Send-TemplatedEmail -To $recipient.Email `383 -TemplateFile $TemplateFile `384 -Variables $variables `385 -Attachments $attachments386387 $sent++388389 # Delay between sends390 if ($DelaySeconds -gt 0 -and $sent -lt $recipients.Count) {391 Start-Sleep -Seconds $DelaySeconds392 }393 }394 catch {395 $failed++396 Write-Warning "Failed to send to $($recipient.Email): $_"397 }398 }399 }400401 # Summary402 Write-Host "`n========================" -ForegroundColor Cyan403 Write-Host "Bulk Send Complete" -ForegroundColor Cyan404 Write-Host "========================" -ForegroundColor Cyan405 Write-Host "Sent: $sent" -ForegroundColor Green406 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" })408409 Write-EmailLog "Bulk send completed: $sent sent, $failed failed, $skipped skipped"410}411412function New-EmailTemplate {413 <#414 .SYNOPSIS415 Creates a new email template file with standard structure.416417 .PARAMETER Path418 Where to save the template.419420 .PARAMETER Name421 Template name for the subject line.422423 .PARAMETER HTML424 Create an HTML template.425426 .EXAMPLE427 New-EmailTemplate -Path "C:\Templates\Welcome.html" -Name "Welcome" -HTML428 #>429 [CmdletBinding()]430 param (431 [Parameter(Mandatory = $true)]432 [string]$Path,433434 [Parameter(Mandatory = $false)]435 [string]$Name = "Template",436437 [Parameter(Mandatory = $false)]438 [switch]$HTML439 )440441 if ($HTML) {442 $content = @"443Subject: $Name - {{Date}}444Format: HTML445446<!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>462463 <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>470471 <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: Plain485486Hi {{Name}},487488Your content goes here. You can use variables like:489- {{Name}} - Recipient name490- {{Date}} - Current date491- {{Time}} - Current time492- {{DayOfWeek}} - Day of the week493494Best regards,495{{SenderName}}496"@497 }498499 $content | Out-File -FilePath $Path -Encoding UTF8500 Write-Host "Template created: $Path" -ForegroundColor Green501}502503# ============================================================================504# Export Functions505# ============================================================================506507Export-ModuleMember -Function Send-OutlookEmail, Send-TemplatedEmail, Send-BulkEmails, New-EmailTemplate
How to Run This Script
Method 1: Dot Source the Functions
1# Load the functions2. .\OutlookEmail.ps134# Now use them5Send-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 folder2$env:PSModulePath -split ";"34# Save to: Documents\PowerShell\Modules\OutlookEmail\OutlookEmail.psm15# Then import6Import-Module OutlookEmail
Method 3: Scheduled Reports
Create a script that sends weekly reports:
1# WeeklySend.ps12. C:\Scripts\OutlookEmail.ps134Send-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.
