6
5

More than 3 years have passed since last update.

PowerShellで Gmail/Yahoo!JAPAN SMTPを利用したメール送信

Posted at

はじめに

PowerShellからメール送信ができるのか気になりました。
まあ、できるんでしょうけど、実際に手を動かして体験してみたかったので、調べてみました。
GmailのSMTPサーバを利用したメール送信は、簡単に実現できましたが、Yahoo!JAPANのSMTPサーバを利用したメール送信に苦労したので、メモを残しておきます。

PowerShell でメール送信を実現するための、主要なコマンドレットやクラスは、次を利用しました。
Microsoftのサイトを見ると、どれも非推奨になっていたけど、他の選択肢が限られるので、使ってみました。

  • Send-MailMessageコマンドレット
  • 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

関連する用語・情報

参考にしたサイト

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5