PowerShell to Send Email with Attachment: A Practical Guide for Automation
Let’s be real — manually attaching files to emails gets old fast. Whether you’re an IT admin pushing reports, a developer sharing logs, or just someone trying to automate a tedious task, there’s a better way. And it involves PowerShell.
Sending emails through scripts isn’t just for tech wizards anymore. With a few lines of code, you can automate file sharing, notifications, and even entire workflows. But here’s the catch: most guides skip the messy parts. They don’t tell you about SMTP quirks, credential handling, or why your attachment might mysteriously vanish.
Some disagree here. Fair enough.
So let’s fix that. This guide walks through how to send email with attachments using PowerShell — the right way.
What Is PowerShell to Send Email with Attachment?
At its core, sending email via PowerShell means using the command line to trigger your email client or connect directly to an SMTP server. Which means it’s automation, plain and simple. Instead of clicking through Outlook or Gmail, you write a script that does the work for you.
The Basics: Cmdlets and SMTP
PowerShell uses cmdlets (pronounced "command-lets") to perform actions. For email, the go-to cmdlet used to be Send-MailMessage. Still, it’s straightforward and built into Windows. That said, Microsoft has marked it as obsolete in recent versions. That doesn’t mean it’s dead — just that newer alternatives exist.
Still, Send-MailMessage works fine for basic tasks. Here’s a minimal example:
Send-MailMessage -To "recipient@example.com" -From "sender@example.com" -Subject "Report Attached" -Body "Please find the report attached." -SmtpServer "smtp.example.com" -Attachments "C:\Reports\MonthlyReport.pdf"
That’s it. But real-world usage? Not so simple Turns out it matters..
Modern Alternatives: Using .NET or Modules
If you’re on PowerShell 7+, consider using the MailKit module or writing raw .NET code. These offer better security and flexibility. Here's one way to look at it: MailKit supports OAuth2 and modern encryption standards out of the box Which is the point..
But for now, let’s stick with Send-MailMessage since it’s still widely used and supported Easy to understand, harder to ignore..
Why It Matters: Real-World Use Cases
Why bother automating email? Because time is money. Here are a few scenarios where PowerShell shines:
- Daily Reports: Automatically email daily sales reports to stakeholders without lifting a finger.
- Log Sharing: Send error logs to developers when a script fails.
- Backup Notifications: Alert yourself when backups complete or fail.
- User Onboarding: Welcome new employees with a personalized email and onboarding documents.
Without automation, these tasks eat into your day. With PowerShell, they happen in the background Which is the point..
How It Works: Step-by-Step Implementation
Let’s break down the process of sending an email with attachments in PowerShell Not complicated — just consistent..
1. Set Up SMTP Configuration
Before sending anything, you need an SMTP server. Gmail, Outlook, and most corporate email services provide one. You’ll also need credentials — username and password or app-specific tokens Small thing, real impact..
For Gmail, enable "Less secure app access" or generate an app password. For corporate environments, check with your IT team for SMTP settings.
2. Write the Basic Script
Start with a simple script:
$SmtpServer = "smtp.gmail.com"
$SmtpPort = 587
$Username = "your-email@gmail.com"
$Password = "your-password" | ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential($Username, $Password)
Send-MailMessage -To "recipient@example.com" -From $Username -Subject "Test Email with Attachment" -Body "See attached file." -SmtpServer $SmtpServer -Port $SmtpPort -UseSsl -Credential $Cred -Attachments "C:\path\to\file.
This connects securely to Gmail’s SMTP server and sends a test email.
### 3. Handle Multiple Attachments
Need to send more than one file? Just pass an array:
```powershell
$Attachments = @("C:\Reports\Q1.pdf", "C:\Reports\Q2.pdf", "C:\Reports\Q3.pdf")
Send-MailMessage -To "boss@example.com" -From $Username -Subject "Quarterly Reports" -Body "All reports attached." -SmtpServer $SmtpServer -Port $SmtpPort -UseSsl -Credential $Cred -Attachments $Attachments
PowerShell handles the rest.
4. Add Error Handling
Scripts fail. Networks drop. Credentials expire Easy to understand, harder to ignore..
try {
Send-MailMessage -To "recipient@example.com" -From $Username -Subject "Report" -Body "See attached." -SmtpServer $SmtpServer -Port $SmtpPort -UseSsl -Credential $Cred -Attachments "C:\Reports\Final.pdf"
Write-Host "Email sent successfully."
} catch {
Write-Host "Failed to send email: $_"
}
### 5. Enhance the Email Body with HTML and Inline Images
Plain‑text messages work, but a well‑formatted HTML body makes reports easier to read and lets you embed charts or logos directly in the email.
```powershell
$htmlBody = @"
Weekly Sales Summary
Region Units Sold Revenue
North 1,240 $62,000
South 980 $49,000
East 1,105 $55,250
West 875 $43,750
See the attached detailed spreadsheet for the raw data.
"@
# Attach the logo as an inline resource
$logoPath = "C:\Assets\logo.png"
$attachment = New-Object System.Net.Mail.Attachment($logoPath)
$attachment.ContentDisposition.Inline = $true
$attachment.ContentDisposition.DispositionType = "inline"
$attachment.ContentId = "companyLogo"
try {
Send-MailMessage -To "team@example.On top of that, xlsx"), $attachment `
-Priority High
Write-Host "HTML email sent successfully. com" -From $Username `
-Subject "Weekly Sales Summary" -Body $htmlBody -BodyAsHtml `
-SmtpServer $SmtpServer -Port $SmtpPort -UseSsl -Credential $Cred `
-Attachments @("C:\Reports\WeeklySales."
} catch {
Write-Host "Failed to send HTML email: $_"
} finally {
$attachment.
*Key points*
- `-BodyAsHtml` tells PowerShell to interpret the body as HTML.
- Inline images require a `Content-ID` (`cid:`) reference in the HTML and an attachment whose `ContentDisposition.Inline` flag is set to `$true`.
- Setting `-Priority High` flags the message as urgent in most mail clients.
---
### 6. Secure Credential Storage
Hard‑coding passwords—or even storing them as plain secure strings in a script—is a security risk. Instead, retrieve credentials from a vault or the Windows Credential Manager at runtime.
**Using Get‑Credential (interactive prompt)**
```powershell
$Cred = Get-Credential -Message "Enter SMTP credentials"
This is ideal for ad‑hoc runs or when you can safely prompt a user.
Using Windows Credential Manager
Store a generic credential once:
# Run once to create the credential
$cred = Get-Credential
New-StoredCredential -Target "SMTP_Gmail" -UserName $cred.UserName -Password $cred.Password -Persist LocalMachine
Retrieve it later:
$cred = Get-StoredCredential -Target "SMTP_Gmail"
$Cred = New-Object System.Management.Automation.PSCredential($cred.UserName, $cred.Password)
Modules such as CredentialManager (available from the PowerShell Gallery) provide New-StoredCredential and Get-StoredCredential cmdlets Simple, but easy to overlook. Which is the point..
Using a Secret Management Extension
If you prefer Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault, install the appropriate SecretManagement extension and fetch the secret:
Import-Module Microsoft.PowerShell.SecretManagement
Import-Module Microsoft.PowerShell.SecretStore # local vault example
$secret = Get-Secret -Name "SmtpGmail" -Vault "MyLocalVault"
$Cred = New-Object System.Management.Automation.PSCredential($secret.Username, $secret.Secret)
Never commit passwords to source control; rely on these mechanisms to keep secrets out of your scripts Nothing fancy..
7. Schedule the Script with Task Scheduler
Automation truly shines when the script runs without manual intervention
on a regular cadence. To automate your reporting, use the Windows Task Scheduler to trigger the .ps1 file Easy to understand, harder to ignore..
Step-by-Step Setup:
- Open Task Scheduler and select Create Basic Task.
- Set your Trigger (e.g., Weekly, every Monday at 8:00 AM).
- Under Action, choose Start a program.
- In the Program/script box, enter:
powershell.exe - In the Add arguments box, enter the path to your script and the execution policy bypass:
-ExecutionPolicy Bypass -File "C:\Scripts\Send-WeeklyReport.ps1" - In the General tab, check Run whether user is logged on or not and Run with highest privileges to ensure the script has access to the local file system and network resources.
8. Troubleshooting Common Issues
When deploying automated email scripts, you may encounter a few common roadblocks:
- Authentication Failures: Many modern providers (Gmail, Outlook 365) have disabled "Basic Authentication." If your credentials are correct but the script fails, you likely need to generate an App Password from your account security settings or use OAuth2.
- Firewall Blocks: Port 587 (STARTTLS) or 465 (SSL) may be blocked by your corporate firewall. Ensure your network allows outbound traffic to your SMTP server on the specified port.
- Execution Policy: If the script fails to start via Task Scheduler, verify that the
-ExecutionPolicy Bypassflag is present, as Windows restricts script execution by default. - Attachment Paths: Always use absolute paths (e.g.,
C:\Reports\File.xlsx) rather than relative paths (.\File.xlsx), as Task Scheduler may run the script from a different default working directory.
Conclusion
Automating HTML email reports with PowerShell transforms a tedious manual task into a reliable, professional background process. By combining the flexibility of HTML for presentation, the power of Send-MailMessage for delivery, and the security of the SecretManagement module, you can build a reporting system that is both aesthetically pleasing and enterprise-grade.
Whether you are sending a simple system health alert or a complex weekly sales summary, the key to success lies in decoupling your credentials from your code and ensuring your scheduling is reliable. With these patterns in place, you can spend less time formatting emails and more time analyzing the data they deliver Worth knowing..