PowerShell Send Email with Send-MailMessage

Your script finishes at 3 AM and you want to wake up to the results in your inbox — not log into a server to check. PowerShell send email via Send-MailMessage or the modern Graph API approach lets you deliver automated reports, alerts, and logs as email from any script. This post covers basic SMTP, Office 365 connectivity, HTML bodies, attachments, secure credential handling, and the modern replacement for environments where Send-MailMessage is deprecated.
Basic Send-MailMessage Syntax
Send-MailMessage is built into Windows PowerShell and PowerShell 7 (with a deprecation warning in PS7). For internal SMTP relays or simple lab environments it still works perfectly well.
Send-MailMessage `
-To "[email protected]" `
-From "[email protected]" `
-Subject "Nightly Backup Report - $(Get-Date -Format 'yyyy-MM-dd')" `
-Body "Backup completed successfully." `
-SmtpServer "mail.example.com"
For an SMTP server on a non-default port, add -Port 587. For plain-text SMTP without authentication, this is all you need.
Connect to Office 365 SMTP
Office 365 SMTP requires TLS and authentication. Use -UseSsl with port 587 and supply credentials. Microsoft is phasing out Basic Auth for this, but for SMTP AUTH with a licensed mailbox or a shared mailbox, the pattern below still applies.
$cred = Get-Credential # Use an app password if MFA is enabled
Send-MailMessage `
-To "[email protected]" `
-From "[email protected]" `
-Subject "Disk Space Alert" `
-Body "Drive C: is below 10% free on SERVER01" `
-SmtpServer "smtp.office365.com" `
-Port 587 `
-UseSsl `
-Credential $cred
Sending HTML Email Body
Pass -BodyAsHtml to render the body as HTML. Combine with a here-string for readable templates. This lets you produce formatted tables and highlighted alerts in your email reports.
$htmlBody = @"
<html><body>
<h2>Daily Disk Report — $(Get-Date -Format 'MMMM d, yyyy')</h2>
<table border='1' cellpadding='4'>
<tr><th>Drive</th><th>Free GB</th><th>Total GB</th></tr>
$(Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Used -gt 0 } | ForEach-Object {
"<tr><td>$($_.Name)</td><td>$([math]::Round($_.Free/1GB,1))</td><td>$([math]::Round(($_.Used+$_.Free)/1GB,1))</td></tr>"
})
</table>
</body></html>
"@
Send-MailMessage `
-To "[email protected]" `
-From "[email protected]" `
-Subject "Disk Report" `
-Body $htmlBody `
-BodyAsHtml `
-SmtpServer "mail.example.com"
Add File Attachments
Attach one or more files with the -Attachments parameter. Provide full file paths. For reports generated by the same script, build the file first and then pass its path.
$reportPath = "C:\Logs\report-$(Get-Date -Format yyyyMMdd).csv"
Get-Process | Select-Object Name, CPU, WS | Export-Csv -Path $reportPath -NoTypeInformation
Send-MailMessage `
-To "[email protected]" `
-From "[email protected]" `
-Subject "Process Report" `
-Body "See attached CSV report." `
-Attachments $reportPath `
-SmtpServer "mail.example.com"
Credential Handling Securely
Avoid storing plain-text passwords in scripts. Export credentials using Export-Clixml — the SecureString is encrypted with DPAPI tied to the current user and machine. Only that user on that machine can decrypt it.
# Save credentials once (run interactively)
Get-Credential | Export-Clixml -Path "C:\Logs\smtp-cred.xml"
# Load saved credentials in your script
$cred = Import-Clixml -Path "C:\Logs\smtp-cred.xml"
Send-MailMessage `
-To "[email protected]" -From "[email protected]" `
-Subject "Alert" -Body "Test" `
-SmtpServer "smtp.office365.com" -Port 587 -UseSsl `
-Credential $cred
Modern Alternative with Graph API
In PowerShell 7 environments or when Basic Auth is disabled, use the Microsoft Graph API to send email. This requires an Azure AD app registration with Mail.Send permission.
# Assumes $accessToken is already obtained (see graph-api post)
$messageBody = @{
message = @{
subject = "Automated Alert"
body = @{ contentType = "Text"; content = "This is an automated message." }
toRecipients = @(@{ emailAddress = @{ address = "[email protected]" } })
}
} | ConvertTo-Json -Depth 5
Invoke-RestMethod `
-Uri "https://graph.microsoft.com/v1.0/users/[email protected]/sendMail" `
-Method POST `
-Headers @{ Authorization = "Bearer $accessToken"; "Content-Type" = "application/json" } `
-Body $messageBody
Common Errors and Fixes
- Send-MailMessage deprecated in PowerShell 7: The cmdlet is marked obsolete in PowerShell 7 because it does not implement all modern TLS security requirements. It still works and will not be removed immediately, but Microsoft recommends migrating to the Graph API for new scripts targeting PS7. For Windows PowerShell 5.1 scripts,
Send-MailMessageis fully supported with no issues. - App password needed for O365 with MFA: If the sender account has MFA enabled, normal password authentication fails for SMTP AUTH. You must create an app password in the Microsoft 365 account settings, or — for production use — configure SMTP AUTH with a dedicated service account that uses a certificate or client secret via Graph API instead.
Related Cmdlets / See Also
Wrapping Up
Send-MailMessage remains the quickest way to add email output to any PowerShell script — but plan your migration to Graph API for new PS7 projects. As a next step, save your SMTP credentials with Export-Clixml and add email alerts to your existing monitoring scripts so you get notified the moment something needs attention.


