PowerShell SMTP: Send Email Without Credentials

PowerShell SMTP: Send Email Without Credentials

PowerShell Tips Editor 4 min read
PowerShell SMTP: Send Email Without Credentials

Internal monitoring and automation scripts need to send email alerts without requiring a cloud email account or storing credentials in the script. When your environment has a corporate SMTP relay configured to accept connections from internal servers, PowerShell can send alerts using PowerShell SMTP send email without any authentication at all. This post covers anonymous internal relay configuration, TLS-encrypted SMTP, testing connectivity, validating email addresses, and the modern Graph API alternative when relay is not an option.

Basic Anonymous SMTP Relay

The simplest send requires only a From address, a To address, a subject, a message body, and the SMTP server hostname. No credentials needed when the relay accepts anonymous connections from your server’s IP:

Send-MailMessage `
    -From    "[email protected]" `
    -To      "[email protected]" `
    -Subject "Disk Space Alert: Server01 is at 92%" `
    -Body    "Drive C: on Server01 has exceeded the 90% threshold at $(Get-Date)" `
    -SmtpServer "smtp-relay.corp.com"

If the relay accepts anonymous connections from your source IP, this sends immediately with no authentication prompt. The From address does not need to be a real mailbox — alerts commonly use a non-reply address like [email protected].

Internal Corporate Relay Setup

For multi-recipient alerts with HTML body and attachments, build the parameter set with splatting for readability:

$attachmentPath = "C:\Reports\disk-report-$(Get-Date -Format 'yyyyMMdd').csv"
Get-PSDrive -PSProvider FileSystem | Export-Csv $attachmentPath -NoTypeInformation

$mailParams = @{
    From        = "[email protected]"
    To          = @("[email protected]", "[email protected]")
    Cc          = "[email protected]"
    Subject     = "Weekly Disk Space Report — $(Get-Date -Format 'yyyy-MM-dd')"
    Body        = "<h3>Disk Space Report</h3><p>See attached CSV for details.</p>"
    BodyAsHtml  = $true
    Attachments = $attachmentPath
    SmtpServer  = "smtp-relay.corp.com"
    Port        = 25
}
Send-MailMessage @mailParams

TLS Encrypted SMTP

When sending to an SMTP server that requires authentication (such as Microsoft 365 or Gmail), use port 587 with STARTTLS and a credential object. Store the password in an encrypted file rather than plain text:

# Create encrypted credential file (run once interactively)
$cred = Get-Credential -Message "Enter SMTP credentials"
$cred.Password | ConvertFrom-SecureString | Set-Content "C:\Scripts\smtp-cred.txt"

# In the script — load from encrypted file
$password = Get-Content "C:\Scripts\smtp-cred.txt" | ConvertTo-SecureString
$cred     = New-Object System.Management.Automation.PSCredential("[email protected]", $password)

Send-MailMessage `
    -From       "[email protected]" `
    -To         "[email protected]" `
    -Subject    "Server Alert" `
    -Body       "Alert details here" `
    -SmtpServer "smtp.office365.com" `
    -Port       587 `
    -UseSsl     `
    -Credential $cred

Test SMTP Connection with PowerShell

Before deploying a script to production, verify that the SMTP relay is reachable and accepting connections from your source IP:

$smtpServer = "smtp-relay.corp.com"
$smtpPort   = 25

# Test TCP connectivity
$tcpTest = Test-NetConnection -ComputerName $smtpServer -Port $smtpPort
if ($tcpTest.TcpTestSucceeded) {
    Write-Host "SMTP server reachable on port $smtpPort" -ForegroundColor Green
} else {
    Write-Warning "Cannot reach $smtpServer on port $smtpPort"
}

# Send a test email
try {
    Send-MailMessage -From "[email protected]" -To "[email protected]" `
        -Subject "SMTP Test $(Get-Date)" -Body "Connection test" `
        -SmtpServer $smtpServer -Port $smtpPort -ErrorAction Stop
    Write-Host "Test email sent successfully" -ForegroundColor Green
}
catch {
    Write-Warning "Send failed: $($_.Exception.Message)"
}

Validate Email Addresses Before Sending

Validate email format with a regex check before attempting to send, to catch configuration errors early:

function Test-EmailAddress {
    param([string]$Address)
    $Address -match '^[\w.+-]+@([\w-]+\.)+[a-zA-Z]{2,}$'
}

$recipients = @("[email protected]", "not-an-email", "[email protected]")
$valid   = $recipients | Where-Object { Test-EmailAddress $_ }
$invalid = $recipients | Where-Object { -not (Test-EmailAddress $_) }

if ($invalid) {
    Write-Warning "Invalid addresses skipped: $($invalid -join ', ')"
}

if ($valid) {
    Send-MailMessage -From "[email protected]" -To $valid `
        -Subject "Alert" -Body "Content" -SmtpServer "smtp-relay.corp.com"
}

Modern Graph API Alternative

Microsoft deprecated basic authentication for Exchange Online in 2022. For M365 environments, use the Microsoft Graph API via Invoke-RestMethod with a registered app and client credentials:

# Get access token (using client credentials flow)
$tokenParams = @{
    Uri    = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
    Method = "POST"
    Body   = @{
        client_id     = $clientId
        client_secret = $clientSecret
        scope         = "https://graph.microsoft.com/.default"
        grant_type    = "client_credentials"
    }
}
$token = (Invoke-RestMethod @tokenParams).access_token

# Send email via Graph API
$graphParams = @{
    Uri     = "https://graph.microsoft.com/v1.0/users/[email protected]/sendMail"
    Method  = "POST"
    Headers = @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" }
    Body    = @{
        message = @{
            subject = "Alert"
            body    = @{ contentType = "Text"; content = "Alert body" }
            toRecipients = @(@{ emailAddress = @{ address = "[email protected]" } })
        }
    } | ConvertTo-Json -Depth 5
}
Invoke-RestMethod @graphParams

Common Errors and Fixes

  • Relay rejected — server not configured to allow relay from source IP. Anonymous SMTP relay requires the exchange or mail server to be configured to allow relay from your script server’s IP. Contact your mail admin to add the server IP to the relay allowed list, or use authenticated SMTP with credentials instead.
  • Port 25 often blocked — try 587 with STARTTLS. Many environments block outbound port 25 to prevent spam. If Test-NetConnection on port 25 fails, try port 587 with -UseSsl and credentials. For internal relay servers, confirm the listening port with the mail admin.

Related Cmdlets / See Also

Wrapping Up

Anonymous SMTP relay is the simplest path for internal alerting scripts — no credentials to store, no token refresh logic. Test connectivity with Test-NetConnection before deploying, validate email addresses before sending, and plan a migration to the Graph API if your organization has disabled basic authentication in Exchange Online.

Send-Item -To