Office 365×Graph API×PowerShellでのメール送信エラーを解消する方法

企業や組織でOffice 365(Microsoft 365)とMicrosoft Graph APIを使ったメール送信を自動化する際に、PowerShellスクリプトはとても便利です。しかし、一部のDMZサーバー環境などで「The underlying connection was closed: An unexpected error occurred on a send」というエラーが発生し、原因がわからず苦労される方も多いのではないでしょうか。本記事ではそのようなエラーの原因や解消策をわかりやすく解説し、ネットワーク環境やTLSバージョンの設定を見直すポイントを具体的にご紹介します。

Office 365(Microsoft 365)×Graph APIでのメール送信エラーとは

Office 365(Microsoft 365)とGraph APIを組み合わせたメール送信は、従来のSMTP認証を使ったメール送信よりもセキュアで柔軟な実装が可能になります。ところが、一部のDMZサーバーを経由する環境では、PowerShellスクリプト実行時に以下のようなエラーが発生することがあります。

The underlying connection was closed: An unexpected error occurred on a send

このエラーは、主にセキュリティプロトコル(TLS)のバージョンが合っていない、あるいはDMZなどのネットワーク環境でのFirewallや暗号スイートの設定が原因で起きることが多いです。とくにFirewallログに「tcp-rst-from-client」と記録されている場合は、クライアント側(PowerShellを実行しているサーバー)から接続がリセットされている形跡があるため、TLSハンドシェイクがうまくいっていない可能性が高いと考えられます。

「tcp-rst-from-client」の意味

Firewallログに記録される「tcp-rst-from-client」というのは、クライアント側がRSTパケットを送ってTCP接続を終了したことを意味します。アプリケーションが正常に通信を完了して接続を閉じるのではなく、突然接続をリセットするケースであり、TLSの初期ハンドシェイクや暗号化通信に関する設定不整合によって通信が確立できなかった可能性があります。

よくある原因1:TLSバージョンや暗号スイートの不整合

PowerShellのInvoke-RestMethodInvoke-WebRequestを使ってGraph APIにアクセスする際、標準設定で使用されるTLSバージョンがサーバー側(Office 365側)と合っていないと通信エラーが発生します。とくに古いWindows ServerやPowerShell環境だと、デフォルトのTLSバージョンが1.0や1.1しか有効になっておらず、Office 365側がTLS 1.2以上を必須とするケースが増えています。

PowerShellでTLSを指定する設定例

PowerShellスクリプト中でTLSバージョンを強制的に指定することで、多くの場合この問題を解消できます。よく使われる設定例を以下に示します。

# TLS1.2を含む複数バージョンを許可する例
[Net.ServicePointManager]::SecurityProtocol = 
    [Net.SecurityProtocolType]::Tls -bor 
    [Net.SecurityProtocolType]::Tls11 -bor 
    [Net.SecurityProtocolType]::Tls12

# Graph APIにアクセスする例
$body = @{
    "message" = @{
        "subject" = "テストメール"
        "body" = @{
            "contentType" = "Text"
            "content" = "PowerShellからGraph APIで送信テスト"
        }
        "toRecipients" = @(
            @{
                "emailAddress" = @{
                    "address" = "[email protected]"
                }
            }
        )
    }
}

# アクセストークンはAzure ADで取得したものを使う
$token = "Bearer eyJ0eXAiOiJKV1Q..."

Invoke-RestMethod `
  -Uri "https://graph.microsoft.com/v1.0/users/[email protected]/sendMail" `
  -Method Post `
  -Headers @{ Authorization = $token } `
  -Body ($body | ConvertTo-Json)

このように、Graph APIへの通信を行う前に[Net.ServicePointManager]::SecurityProtocolでTLSバージョンを指定しておくと、通信エラーが解消されることが多いです。DMZサーバー上でのみ失敗する場合は、他のサーバーとのPowerShellバージョンやWindowsの更新状況を比べてみると、TLS 1.2が有効になっていないなどの違いが見つかる可能性があります。

WindowsのTLS設定をレジストリで確認・変更する方法

OSレベルでTLS 1.2を有効化するには、Windowsレジストリを編集することも検討します。サーバーのポリシーによっては、PowerShellスクリプトの[Net.ServicePointManager]::SecurityProtocol設定だけでは十分でない場合があります。
以下のレジストリキーがTLS 1.2を有効にしているかをチェックするとよいでしょう。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2

ここに、ClientServerというサブキーが存在し、それぞれのEnabledDisabledByDefaultが正しい値に設定されている必要があります。もしこれらが設定されていない、あるいは古いサーバーイメージで未設定のままだと、OS全体でTLS 1.2通信が許可されないケースがあります。

よくある原因2:DMZ環境やFirewallのルール設定

DMZサーバーはネットワークの境界に位置するため、外部との通信を厳格に制限するFirewallルールが設定されています。Graph APIを利用する場合は、以下のポイントを確認するとよいでしょう。

必要なポートと宛先の許可

  • Graph APIのエンドポイント:graph.microsoft.com
  • ポート:HTTPS(443)
  • その他Microsoft 365関連のドメイン(login.microsoftonline.comlogin.windows.netなど)も必要に応じて許可

もし上記の宛先やポートがFirewallでブロックされていると、TLSハンドシェイクが完了する前に切断される場合があります。Firewallログを確認し、該当する通信が拒否されていないかをネットワーク担当者と連携して確認しましょう。

DMZサーバー固有のセキュリティミドルウェア

組織によっては、DMZ上にプロキシサーバーやSSLインスペクションを行う装置が導入されていることがあります。これにより、特定の暗号スイートが使用できない、または暗号化通信を中継できずにRSTを返してしまう可能性があります。その場合は、以下のような作業を検討します。

  1. プロキシやSSLインスペクションのログを確認し、Graph APIやOffice 365の通信を正常に通しているか確認する。
  2. 証明書の再配置やルート証明書の信頼関係を確認する。
  3. ポリシー上、特定の暗号スイートやTLSバージョンのみ許可する設定になっていないか調べる。

よくある原因3:OSやPowerShellバージョンの古さ

Windows Server 2008 R2や2012といった古いOSの場合、初期状態ではTLS 1.2が有効化されていないケースがあります。また、PowerShell 4.0以前の場合、TLS 1.2に対応する設定が標準で含まれていないこともあります。
いずれもWindows Updateや.NET Frameworkの更新によってTLS 1.2通信がサポートされることがありますので、以下をチェックします。

  • Windows Updateを最新まで適用しているか。
  • .NET Frameworkのバージョンを必要に応じて更新しているか。
  • PowerShell 5.1以上の環境を用意できるか。

特にDMZ環境のサーバーは安定運用を優先して更新が止まっていることが多いです。しかし、Office 365側のセキュリティ要件は年々厳しくなるため、定期的な更新が必要です。

具体的なトラブルシューティング手順

ここでは、DMZサーバーだけ接続に失敗する場合の代表的なトラブルシューティング手順を示します。問題が発生したサーバーと、問題なく動作するサーバーの設定やバージョンを比較しながら進めると原因を特定しやすくなります。

1. TLSバージョンの強制指定を試す

PowerShellスクリプトを実行する直前に、以下の設定を追加してみてください。

[Net.ServicePointManager]::SecurityProtocol = 
    [Net.SecurityProtocolType]::Tls12 -bor
    [Net.SecurityProtocolType]::Tls11 -bor
    [Net.SecurityProtocolType]::Tls

このコードを入れるだけで問題が解決する場合は、サーバーやPowerShellのデフォルトTLSバージョン設定が原因と考えられます。

2. Firewallルールの再チェック

  • DMZサーバーが外部に出る際にプロキシサーバーを経由していないか。
  • 特定のポートやドメインがブロック対象になっていないか。
  • ログに「tcp-rst-from-client」がある場合、クライアント(DMZサーバー)側の設定やSSLインスペクションの影響が考えられる。

3. OSレベルのTLS設定を確認

PowerShellでTLSを有効化していても、OS側の設定が旧式のままだと失敗しやすいです。必要があればレジストリを編集してTLS 1.2を有効化した上で再起動し、設定が反映されるかどうか確認しましょう。

4. PowerShellや.NET Frameworkのバージョン確認

  • PowerShellのバージョンは$PSVersionTable.PSVersionで確認可能。
  • .NET Frameworkのバージョンはレジストリや「プログラムと機能」から確認。
  • 古すぎる場合は更新し、最新のセキュリティプロトコルを扱える環境にする。

5. 追加ログの取得

  • Invoke-RestMethod-Debugスイッチや-Verboseスイッチを試してみる。
  • Windowsイベントログのシステムログやアプリケーションログにエラーが出ていないかチェック。
  • Firewallやプロキシサーバーのアクセスログを合わせて確認。

これらの手順を踏むことで、原因を特定しやすくなります。

暗号スイートの確認方法と例

TLSのバージョンだけでなく、利用できる暗号スイート(Cipher Suite)が不一致で接続できないケースもあります。DMZ環境でSSLインスペクションを行うアプライアンスがあると、一部の強力な暗号スイートがブロックされる場合も想定されます。

Windows ServerでのCipher Suite確認コマンド

Windows Server 2016以降では、以下のようなコマンドを使って有効なCipher Suiteを確認できます。

Get-TlsCipherSuite | Format-Table Name, ExchangeAlgorithm, CipherAlgorithm, HashAlgorithm

設定の誤りや組織ポリシーの制約で、Graph API接続に必要なCipher Suiteが使用できないときは通信が切断されてしまう可能性があります。もし原因が暗号スイートにある場合は、ネットワークセキュリティ担当者に相談し、必要な暗号スイートを追加または許可してもらうことで問題が解消されるケースがあります。

暗号スイートの具体例

たとえば、Office 365やGraph APIで主に利用されるTLS 1.2の代表的な暗号スイートとしては以下のようなものが挙げられます(あくまでも例です)。

暗号スイート名方式
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256ECDHE + AES 128 + SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384ECDHE + AES 256 + SHA384
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256DHE + AES 128 + SHA256
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384DHE + AES 256 + SHA384

組織やOSバージョン、セキュリティポリシーによって利用できる暗号スイートの種類や強度は異なります。DMZサーバー側でこれらの暗号スイートを使えない設定になっているときは、tcp-rst-from-clientが発生する要因になる可能性があります。

問題解決に向けたアクションプラン

以上の内容を踏まえると、DMZサーバーでGraph APIによるメール送信が失敗する場合、以下のようなアクションプランを立てるとよいでしょう。

  1. PowerShellスクリプトのTLS設定見直し
  • スクリプト冒頭で[Net.ServicePointManager]::SecurityProtocolを指定。
  • それでも失敗する場合は、OSのTLS 1.2設定(レジストリ)を確認。
  1. Firewallやプロキシ設定の確認
  • DMZ環境固有の制限がないかネットワーク担当者に相談。
  • 必要なエンドポイント(graph.microsoft.comなど)が許可されているかを確認。
  1. OSやPowerShell、.NET Frameworkのバージョンアップ
  • 更新が滞っている場合は最新版へのアップデートを検討。
  • セキュリティパッチでTLSの利用可否や暗号スイート対応が変わる。
  1. 暗号スイートの整合性チェック
  • Get-TlsCipherSuiteやレジストリを用いて、Office 365で使用可能な暗号スイートと一致しているかを確認。
  • DMZサーバーのSSLインスペクションが原因で一部暗号スイートを遮断していないかを調べる。
  1. 追加のログやデバッグ情報の収集
  • Invoke-RestMethodのVerbose出力やFirewallログ、アプリケーションログを詳細に分析する。
  • エラーがどの段階で発生しているか(TLSハンドシェイク、認証、データ送信など)を切り分ける。

まとめ:TLSとネットワーク制限を総点検しよう

Office 365(Microsoft 365)とMicrosoft Graph APIを使ったPowerShellでのメール送信エラーは、DMZ環境などの制限が厳しいネットワークで起きやすいトラブルです。主な原因はTLSバージョンの不整合やFirewallのルール、あるいは古いOS・PowerShellバージョンで最新のセキュリティ要件を満たしていないことにあります。

これらの要因を総点検する際は、まずはPowerShellのTLS指定とネットワーク設定の確認を最初に試すと効果的です。そこからOSや暗号スイート、Firewallポリシーを順番に洗い出すことで、問題の根本原因を突き止められる可能性が高まります。セキュリティ要件が日に日に厳格化していくOffice 365の世界では、環境の更新や設定の最適化が必須となるでしょう。ぜひ本記事を参考に、快適な自動化メール送信環境を構築してみてください。

コメント

コメントする