OSの死活監視やバックアップ取得の成否メールなど、人によるメール送受信ではなくシステムによるメール送受信をExchange Onlineで実施する方法を自分なりに考えて実装してみたのでそのまとめです。
具体的な実装方法はタイトルの通りです。
Microsoft 365(Exchange Online)は「一人一アカウント(ひとり いちあかうんと)」の原則に基づいて設計されており、
人間以外のシステムプロセスやスクリプトがメール送受信に利用することは原則として想定されていません。
したがって、このような実装を行う場合はライセンス体系や運用ルールを十分確認のうえ、各自の責任で実施してください。
1つアカウントとライセンスを準備する
上述の警告のnote alertの通りシステムで利用するアカウントにライセンスが必要かどうかわかりませんでした。
ですのでとりあえず1つ専用のアカウントとExO Plan1のライセンスを準備して検証しました。
証明書を準備する
今回の用途だとメールを送信したいサーバやクライアント端末が存在するはずです。
登録したアプリケーションへの認証に証明書認証を使うため、証明書を作成します。
証明書発行手順
今回はWindows 11から登録されたアプリケーション経由でExOからメールを送りたいため、Windows 11にデフォルトで搭載されているPower Shellを用いて自己署名証明書を発行します。
以下のコマンドを実行します。
$certName = "cert_ExO_SystemMail01"
$CertStoreLocation = "cert:\CurrentUser\My"
$KeyAlgorithm = "RSA"
$KeyLength = 2048
$KeySpec = "Signature"
$KeyExportPolicy = "NonExportable"
$Expire_Years = "1"
$Cert01 = New-SelfSignedCertificate -Subject $certName -CertStoreLocation $CertStoreLocation -KeyAlgorithm $KeyAlgorithm -KeyLength $KeyLength -KeySpec $KeySpec -KeyExportPolicy $KeyExportPolicy -NotAfter (Get-Date).AddYears($Expire_Years)
$Thumbprint = $Cert01.thumbprint
Write-Output $Thumbprint
パラメーターは以下の通りです。
| パラメーター名 | 変数名 | 本記事での設定値 | 説明 |
|---|---|---|---|
| 証明書名 | $certName | cert_ExO_SystemMail01 | 生成される証明書の識別名 |
| 証明書ストア | $CertStoreLocation | cert:\CurrentUser\My | 証明書の保存先(現在のユーザーの「個人」ストア) |
| 鍵アルゴリズム | $KeyAlgorithm | RSA | 公開鍵暗号方式を利用 |
| 鍵長 | $KeyLength | 2048 | 鍵の長さ(ビット数) |
| 鍵の利用用途 | $KeySpec | Signature | デジタル署名・認証用途(署名鍵専用で暗号化用途ではない) |
| 鍵のエクスポートの可否 | $KeyExportPolicy | NonExportable | 秘密鍵のエクスポートを禁止 |
| 証明書有効期限 | $Expire_Years | 1 | 証明書の有効期間(年単位) |
ExOはExchange Onlineの略称です。
今回は実験的ですがRSAではなく楕円曲線暗号(ECDSA)を利用しました。
楕円曲線暗号(ECDSA)を使用すると「認証キーアルゴリズムはサポートされていません」というエラーが発生します。
これは、Microsoft Entra ID のアプリケーション証明書認証がRSAのみをサポートしているためです(記事執筆時の2025年11月3日現在)。
ECDSAはRSAと同等のセキュリティをより短い鍵長で実現できる優れた方式ですが、現行のMicrosoft GraphやMSALライブラリでは利用できません。
参考:Microsoft Learn - How to create a self-signed certificate for Microsoft Entra ID

また-KeyExportPolicy NonExportable により鍵(秘密鍵)のエクスポートもさせないようにしているので、証明書の複製や他OSへの移植も不可能です。
仮にこのメール送信を行うマシンが乗っ取られても、証明書を複製されてExOの踏み台サーバを増やされるリスクを低減しています。
証明書の有効期限はPower Shell内でAddYearsオプションを利用しているため年単位ですが、AddMonthsオプションだと月単位、AddWeeksだと週単位、AddDaysだと日単位での日数調整が可能です。
最近はセキュリティ強化の流れから、主要な認証局(CA)による証明書有効期間の短縮(最大398日)も進んでおり、短期証明書運用を想定した設計を行うことが望まれます。
証明書エクスポート手順
Power Shellで指定した証明書の作成先である[カレントユーザー(現在のユーザー)]を開くために[ファイル名を指定して実行]にcertmgr.mscと入力して[OK]をクリックします。

以下の画面が出てくるので、更に一つ下の階層の[個人]を開き更に[証明書]を開くと、先ほどPower Shellで作成した証明書が出てきます。

対象の証明書を右クリックして[すべてのタスク]をクリックし[エクスポート]をクリックします。

[証明書のエクスポート ウィザードの開始]画面が出てくるので[次へ]をクリックします。

秘密キー(秘密鍵)のエクスポートを選択する画面が出てくるので[いいえ、秘密キーをエクスポートしません]を選択して[次へ]をクリックします。
証明書作成時にオプションで-KeyExportPolicyのパラメーターをNonExportableを選択しているので秘密キーをエクスポートするラジオボタンがグレーアウトしていますね。

次にエクスポート形式を選択する画面になりますので、[DER]を選択して[次へ]をクリックします。

エクスポートする場所とファイル名を指定する画面が出てくるので、お好きな場所とファイル名を指定しましょう。
私はデスクトップに、ファイル名は証明書名と同じにしました。

証明書作成手順はここまでです。
Entra IDに専用のアプリケーションを登録する
Entra ID管理センターの[アプリの登録]からアプリケーションを登録します。
アプリケーション登録
Entra ID管理センターにログインし、[Entra ID]内の[アプリの登録]から[+新規登録]をクリックします。

[名前]に任意の名前を付けます。
私はわかりやすく[ExO_SystemMail01]にしました。
[サポートされているアカウントの種類]は、以前までは[この組織ディレクトリのみに含まれるアカウント]で良かったのですが、少し前から[任意の組織ディレクトリ内のアカウント(任意の Microsoft Entra ID テナント - マルチテナント)]でないと権限不足でエラーになります。
[リダイレクト URI]は今回のアプリ利用用途では不要なので設定しなくて大丈夫です。
最後に[登録]をクリックします。

登録されると以下の画面に遷移します。

登録したアプリケーションに証明書をインポートする
登録したアプリケーション内の[証明書とシークレット]をクリックし、[証明書]をクリックします。
[証明書のアップロード]というボタンが出てきますのでクリックします。

先ほどPower Shellで作成してエクスポートした証明書をアップロードして[追加]をクリックします。

登録されたアプリケーションに権限を付与する
登録したアプリケーションの中の[APIのアクセス許可]をクリックして、[アクセス許可の追加]をクリックします。(すみません。画像では途中で見切れていますが・・・)
右からブレードが出てきますので、一番大きい[Microsoft Graph]をクリックします。

Microsoft Graphの中でどの種類の許可を与えるか聞かれるので[アプリケーションの許可]を選択して、[アクセス許可の追加]をクリックします。

検索ボックスに[Mail.Sned]と入力し、検索結果で抽出される[Mail.Send]にチェックを入れて[アクセス許可の追加]をクリックします。

アクセス権の許可を与える際に管理者の同意を与える操作を実施します。
先ほどの[アクセス許可の追加]の隣にある[「テナント名」に管理者の同意を与えます]をクリックします。
[状態]のエクスクラメーションマークが状態遷移を表すので見ておきます。

[「テナント名」に管理者の同意を与えます]をクリックすると[管理者の同意の確認を与えます]画面が出てくるので[はい]をクリックします。
この操作により、登録したアプリケーションがテナント全体のMicrosoft Graph APIを利用できるようになります。Mail.Send権限は組織内すべてのユーザーのメール送信を許可する強い権限であるため、用途を限定して利用してください。

同意が与えられると[状態]のエクスクラメーションマークが緑のチェックマークに変化します。

これでアプリケーションの登録は終わりです。
メール送信テスト
では実際にメールが送信できるか検証してみます。
証明書と同じくPower Shellでメールを送付します。
初回実行のPower Shell
# モジュールインストール
Install-Module Microsoft.Graph.Users.Actions
Install-Module MSAL.PS
# モジュールインポート
Import-Module Microsoft.Graph.Users.Actions
Import-Module MSAL.PS
このモジュールのインストールとインポートは初回1回のみです。
MSAL認証とメール送信に必要なPower Shellモジュールのインストールとインポート(読み込み)です。
実際のメール送信用Power Shell
# アプリ指定
$tenantId = '[テナントID]'
$clientId = '[登録したアプリケーションのクライアントID]'
$clientCert = Get-ChildItem -Path "Cert:\CurrentUser\My\[証明書の拇印]"
$MSALToken = Get-MsalToken -TenantId $tenantId -ClientId $clientId -ClientCertificate $clientCert
# Microsoft Graphログイン
$SecureAccessToken = ConvertTo-SecureString $MSALToken.AccessToken -AsPlainText -Force
Connect-MgGraph -AccessToken $SecureAccessToken -NoWelcome
# メール送信(Send-MgUserMail)
$Sender = "[送信元メールアドレス]"
$To = "[送信先メールアドレス]"
$params = @{
Message = @{
Subject = "Graph app-only send test"
Body = @{ ContentType = "Text"; Content = "This is a test from app-only Graph (Power Shell)." }
ToRecipients = @(@{ EmailAddress = @{ Address = $To } })
}
SaveToSentItems = $true
}
Send-MgUserMail -UserId $Sender -BodyParameter $params
以上でメール送信ができます。
証明書の拇印は証明書作成コマンドで最後にWrite-Outputコマンドで出力している変数$Thumbprintの値です。
-UserId パラメーターに指定する送信元アドレス ($Sender) は、アプリ登録に紐づくアカウントである必要があります。
異なるユーザーアドレスを指定すると「Access is denied」エラーになります。
まとめ
- OutlookやOWA以外からもメール送信が可能
- MSAL認証は証明書認証が使える
- 必要な権限はMail.Sendだけ
MSAL認証でこれまではシークレットを用いた事実上のID/パスワード認証しか知らなかったのですが、証明書認証での認証でメール送信者の身元確認ができるというのは驚きでした。
非常に便利ですね。
補足・応用の可能性
今回はExchange Onlineへのメール送信を例にしましたが、同じMSAL認証・証明書登録の仕組みを使えば、Microsoft Graphを経由してTeams通知やSharePoint更新などの自動化も可能です。
証明書認証により、パスワードレスかつセキュアにM365環境と連携できる点が最大のメリットです。

