はじめに
PowerShellからメール送信ができるのか気になりました。
まあ、できるんでしょうけど、実際に手を動かして体験してみたかったので、調べてみました。
GmailのSMTPサーバを利用したメール送信は、簡単に実現できましたが、Yahoo!JAPANのSMTPサーバを利用したメール送信に苦労したので、メモを残しておきます。
PowerShell でメール送信を実現するための、主要なコマンドレットやクラスは、次を利用しました。
Microsoftのサイトを見ると、どれも非推奨になっていたけど、他の選択肢が限られるので、使ってみました。
- Send-MailMessageコマンドレット
- Send-MailMessage (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Docs
- 当コマンドレットの-Portオプションを使うには、PowerShell 3.0 以降が必要。
- GmailのSMTPサーバを利用可能。Yahoo!JAPANのSMTPサーバを使うことはできなかった。
- SmtpClientクラス
- SmtpClient クラス (System.Net.Mail) | Microsoft Docs
- .NET Framework 2.0 以降(なので PowerShell 2.0 以降)
- データ転送方式は、STARTTLSのみで、SMTP over SSLには対応していないらしい。
- GmailのSMTPサーバを利用可能。Yahoo!JAPANのSMTPサーバを使うことはできなかった。
- SmtpMailクラス
- SmtpMail クラス (System.Web.Mail) | Microsoft Docs
- .NET Framework 1.1 以降(なので PowerShell 1.0 以降)で使用可能だが、使用を推奨されていない。
- データ転送方式は、STARTTLSではなく、SMTP over SSLとなるらしい。
- GmailとYahoo!JAPANのSMTPサーバを利用可能。
メール送信の実装
Send-MailMessageコマンドレット
下記のように記述して、GmailのSMTPサーバを利用したメール送信ができました。
SMTPサーバへの接続ポートは、TLS/STARTTLSのポート「587」を使用しました。
SSLのポート「465」だと、エラーになってしまいました。
私の書き方が悪いのか、Send-MailMessageコマンドレットが対応していないのか、原因は分かりません。
SMTPサーバ接続の認証情報として利用するGmailのアカウントですが、次の2パターンで利用可能でした。
- 2段階認証を設定されていないアカウントを使用する。
- 2段階認証を設定しているアカウントで、アプリパスワードを払い出し、下記コードの認証情報のパスワードに設定する。
認証情報として設定する下記「Gmailのアカウント名」は、ネットでいくつか実装例をみると、「@gmail.com」を付けないのが多い気がしました。
でも、「@gmail.com」を付けなくても問題なく動作しました。
Yahoo!JAPANのSMTPサーバを使うとエラーになってしまいました。
$password = ConvertTo-SecureString "Gmailのパスワード" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential(
"Gmailのアカウント名", $password)
$from = "送信元のGmailメールアドレス"
$to = "送信先メールアドレス"
Send-MailMessage `
-From $from `
-To $to `
-Subject "テストメール" `
-Body "テストメールです。" `
-Attachments "D:\tmp\dummy.txt" `
-Encoding UTF8 `
-SmtpServer "smtp.gmail.com" `
-Port 587 `
-UseSsl `
-Credential $credential
以下は、失敗例です。
# SMTPサーバへの接続ポートに、SSLポート「465」を使用するとエラーになってしまいました。
Send-MailMessage `
-From $from `
-To $to `
-Subject "テストメール" `
-Body "テストメールです。" `
-Encoding UTF8 `
-SmtpServer "smtp.gmail.com" `
-Port 465 `
-UseSsl `
-Credential $credential
#=> 転送接続からデータを読み取れません: net_io_connectionclosed
# 2段階認証を設定しアプリパスワードを払い出していないアカウントを使用した。
Send-MailMessage `
-From $from `
-To $to `
-Subject "テストメール" `
-Body "テストメールです。" `
-Encoding UTF8 `
-SmtpServer "smtp.gmail.com" `
-Port 587 `
-UseSsl `
-Credential $credential
#=> SMTP サーバーにセキュリティで保護された接続が必要であるか、またはクライアントが認証されていません。 サーバーの応答:5.7.0 Authentication Required.
# Yahoo!JAPANのSMTPサーバを使用
$password = ConvertTo-SecureString "Yahoo!JAPANのパスワード" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential(
"Yahoo!JAPANのアカウント名", $password)
Send-MailMessage `
-From $from `
-To $to `
-Subject "テストメール" `
-Body "テストメールです。" `
-Encoding UTF8 `
-SmtpServer "smtp.mail.yahoo.co.jp" `
-Port 465 `
-UseSsl `
-Credential $credential
#=> 転送接続からデータを読み取れません: net_io_connectionclosed
SmtpClientクラス
以下のようにして、GmailのSMTPサーバでTLS/STARTTLSのポート「587」を使用したメール送信ができました。
ですが、GmailのSMTPサーバでSSLのポート「465」を使用した場合や、Yahoo!JAPANのSMTPサーバを使用した場合は、エラーとなってしまいました。
Send-MailMessageコマンドレットとの違いは、Send-MailMessageコマンドレットよりも古い環境でも動作すること。
$user = "Gmailのアカウント名"
$password = "Gmailのパスワード"
$from = "送信元のGmailメールアドレス"
$to = "送信先メールアドレス"
$client = New-Object Net.Mail.SmtpClient("smtp.gmail.com", 587)
$client.EnableSsl = $true
$client.Credentials = New-Object System.Net.NetworkCredential($user, $password)
$client.Send($from, $to, "テストメール", "テストメールです。")
$client.Dispose()
次は、失敗例です。
# GmailのSMTPサーバでSSLポート「465」
$client = New-Object Net.Mail.SmtpClient("smtp.gmail.com", 465)
$client.EnableSsl = $true
$client.Credentials = New-Object System.Net.NetworkCredential($user, $password)
$client.Send($from, $to, "テストメール", "テストメールです。")
#=> "4" 個の引数を指定して "Send" を呼び出し中に例外が発生しました: "メールを送信できませんでした。"
# Yahoo!JAPANのSMTPサーバ
$user = "Yahoo!JAPANのアカウント名"
$password = "Yahoo!JAPANのパスワード"
$from = "送信元のYahoo!JAPANメールアドレス"
$client = New-Object Net.Mail.SmtpClient("smtp.mail.yahoo.co.jp", 465)
$client.EnableSsl = $true
$client.Credentials = New-Object System.Net.NetworkCredential($user, $password)
$client.Send($from, $to, "テストメール", "テストメールです。")
#=> "4" 個の引数を指定して "Send" を呼び出し中に例外が発生しました: "メールを送信できませんでした。"
SmtpMailクラス
この方法で、Yahoo!JAPANのSMTPサーバを使用することができました。
GmailのSMTPサーバについては、SSLのポート「465」で使用できましたが、TLS/STARTTLSのポート「587」では使用できませんでした。
SmtpMailクラスでは、データ転送方式がSTARTTLSではなく、SMTP over SSLとなるらしいので、SSLポート「465」しか使用できないのかもしれません。
Add-Type -AssemblyName System.Web
$to = "送信先メールアドレス"
# GmailのSMTPサーバをSSLポート「465」で使用
$user = "Gmailのアカウント名"
$password = "Gmailのパスワード"
$from = "送信元のGmailメールアドレス"
$mail = New-Object System.Web.Mail.MailMessage
$mail.From = $from
$mail.To = $to
$mail.Subject = "テストメール"
$mail.Body = "テストメールです。"
$mail.Attachments.Add(
(New-Object System.Web.Mail.MailAttachment("D:\tmp\dummy.txt", [System.Web.Mail.MailEncoding]::Base64))
) | Out-Null
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusing"] = 2 # 外部メールサーバへの接続
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserver"] = "smtp.gmail.com"
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserverport"] = 465
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] = 1 # 1:Basic認証、2:NTLM認証
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusername"] = $user
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendpassword"] = $password
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpusessl"] = $true # SMTP over SSL
[System.Web.Mail.SmtpMail]::SmtpServer = "smtp.gmail.com"
[System.Web.Mail.SmtpMail]::Send($mail)
# Yahoo!JAPANのSMTPサーバを使用
$user = "Yahoo!JAPANのアカウント名"
$password = "Yahoo!JAPANのパスワード"
$from = "送信元のYahoo!JAPANメールアドレス"
$mail = New-Object System.Web.Mail.MailMessage
$mail.From = $from
$mail.To = $to
$mail.Subject = "テストメール"
$mail.Body = "テストメールです。"
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusing"] = 2
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserver"] = "smtp.mail.yahoo.co.jp"
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserverport"] = 465
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] = 1
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusername"] = $user
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendpassword"] = $password
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpusessl"] = $true
[System.Web.Mail.SmtpMail]::SmtpServer = "smtp.mail.yahoo.co.jp"
[System.Web.Mail.SmtpMail]::Send($mail)
次は、失敗例です。
# GmailのSMTPサーバをTLS/STARTTLSポート「587」で使用
$mail = New-Object System.Web.Mail.MailMessage
$mail.From = $from
$mail.To = $to
$mail.Subject = "テストメール"
$mail.Body = "テストメールです。"
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusing"] = 2
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserver"] = "smtp.gmail.com"
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserverport"] = 587
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] = 1
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusername"] = $user
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendpassword"] = $password
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpusessl"] = $true
[System.Web.Mail.SmtpMail]::SmtpServer = "smtp.gmail.com"
[System.Web.Mail.SmtpMail]::Send($mail)
#=> "1" 個の引数を指定して "Send" を呼び出し中に例外が発生しました: "転送においてサーバーに接続できませんでした。
# GmailのSMTPサーバをTLS/STARTTLSのポート「587」で使用。smtpusessl=false
$mail = New-Object System.Web.Mail.MailMessage
$mail.From = $from
$mail.To = $to
$mail.Subject = "テストメール"
$mail.Body = "テストメールです。"
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusing"] = 2
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserver"] = "smtp.gmail.com"
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserverport"] = 587
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] = 1
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusername"] = $user
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendpassword"] = $password
$mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpusessl"] = $false
[System.Web.Mail.SmtpMail]::SmtpServer = "smtp.gmail.com"
[System.Web.Mail.SmtpMail]::Send($mail)
#=> "1" 個の引数を指定して "Send" を呼び出し中に例外が発生しました: "サーバーによって送信者アドレスが拒否されました。サーバーからの応答は次のとおりです。530 5.7.0 Must issue a STARTTLS command first. w1sm5388841pfu.153 - gsmtp"
メール送信に関連する処理
SMTPサーバ認証情報を暗号化してファイル管理
コード内に認証情報を含めてしまうと、セキュリティ的に問題があるので、暗号化してファイル保管する方法をメモしておきます。
Get-Credential、ConvertFrom-SecureString、ConvertTo-SecureStringコマンドレットを使用しました。
# ファイル保存
$path = "D:\tmp\password.json"
$credential = Get-Credential
ConvertTo-Json @{
userId = $credential.UserName;
password = $credential.Password | ConvertFrom-SecureString;
} | Set-Content $path
# ファイル読み込み
$jsonObj = Get-Content $path | ConvertFrom-Json
$password = $jsonObj.password | ConvertTo-SecureString
$credential = New-Object System.management.Automation.PsCredential($jsonObj.userId, $password)
# メール送信
$from = "送信元のGmailメールアドレス"
$to = "送信先メールアドレス"
Send-MailMessage `
-From $from `
-To $to `
-Subject "テストメール" `
-Body "テストメールです。" `
-Encoding UTF8 `
-SmtpServer "smtp.gmail.com" `
-Port 587 `
-UseSsl `
-Credential $credential
SMTPサーバのポート確認
メール送信処理でエラーが出たとき、ポートが開いていることを確認するためには、次のコマンドレットが使えました。
Test-NetConnection "smtp.gmail.com" -port 465 # OK
Test-NetConnection "smtp.gmail.com" -port 587 # OK
Test-NetConnection "smtp.mail.yahoo.co.jp" -port 465 # OK
Test-NetConnection "smtp.mail.yahoo.co.jp" -port 587 # NG
Test-NetConnection "smtp.mail.yahoo.co.jp" -port 25 # NG
補足
動作確認環境
- Windows 10
- PowerShell 5.1
関連する用語・情報
- SMTP
- ポート 25
- SMTPS (SMTP over SSL)
- ポート 465
- TLS (Transport Layer Security)を用いてSMTPをセキュアにする手法。
- Transport Layer Security (TLS)
- Transport Layer Security - Wikipedia
- 1999年に TLS 1.0 が公開され、2020年現在の最新は TLS 1.3。
- Secure Sockets Layer (SSL)
- STARTTLS
- STARTTLS - Wikipedia
- 平文の通信プロトコルを暗号化通信に拡張する方法のひとつ。
- 2002年頃から規定されている模様。
- GmailのSMTPサーバ
- 他のメール プラットフォームで Gmail のメールをチェックする - Gmail ヘルプ
- SMTPサーバ:smtp.gmail.com、SSLのポート:465、TLS/STARTTLSのポート:587
- 認証で使用するアカウントでは、2段階認証を設定していないこと。または、2段階認証を設定しているアカウントで、アプリパスワードを払い出すこと。
- Yahoo!JAPANのSMTPサーバ
- メールソフトで送受信するには(Yahoo!メールアドレスの場合)
- 新着情報ページ - Yahoo!メール
- SMTPサーバ:smtp.mail.yahoo.co.jp、SSLのポート:465
- 2021/1/19で、SMTPサーバ接続時の非暗号化ポート(ポート番号 25, 587)の提供が終了した。
- 認証で使用するアカウントのメール設定で、「IMAP/POP/SMTPアクセスとメール転送」のSMTP設定を有効にする。
参考にしたサイト
- Send-MailMessage (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Docs
- SmtpClient クラス (System.Net.Mail) | Microsoft Docs
- SmtpMail クラス (System.Web.Mail) | Microsoft Docs
- 【PowerShell】Windows PowerShell を使用して GMail や Office 365 からメールを送信する | Microsoft Docs
- SMTPでメールを送信する - .NET Tips (VB.NET,C#...)
- SSL/TLSを使用してSMTPでメールを送信する - .NET Tips (VB.NET,C#...)
- 【Windows】メーラーやWebメールを使わずにメールを送信する ? スイーツ好きエンジニアの備忘録