Exchange のジャーナル受信サービスを .NET / C# で作るときの現実解
Fabric + Event Hubs 前提なら、なぜ Postfix を挟むのか
Exchange のジャーナルを受信して、監査や分析のために別基盤へ連携したい、という要件は今でもあります。
今回の前提は、受信したジャーナルメールを .NET / C# で解析し、JSON に正規化して Azure Event Hubs に流し、その先は Microsoft Fabric で扱う、というものです。 (Microsoft Learn)
結論から書くと、この構成では Exchange → Postfix などの専用 SMTP 受信基盤 → C# 解析サービス → Event Hubs → Fabric という形がかなり現実的です。
特に Exchange Online を使う場合、ジャーナリング先に Exchange Online のメールボックスは使えません。さらに、配送失敗時に備える alternate journaling mailbox も Exchange Online のメールボックスにはできません。そのため、ジャーナルの受け口は Microsoft 365 の外側に用意する必要があります。 (Microsoft Learn)
まず押さえたい前提
Exchange のジャーナリングは、いわゆる「通常のメール転送」とは少し違います。
ジャーナリングでは、Exchange が journal report を生成し、その中に元メールを添付する形で外部へ配送します。Microsoft Learn でも、ジャーナリングは Exchange の古いコンプライアンス機能であり、Microsoft 365 の外へデータを出す仕組みなので、重複や配送失敗の監視は利用者側の責任になる、と明記されています。 (Microsoft Learn)
つまり、この仕組みを使うときは、単に「メールを受ける」だけでは足りません。
少なくとも次を考える必要があります。
- ジャーナル配送先の用意
- 配送失敗時の alternate journaling mailbox
- SMTP 受信基盤
- 原本保全
- 重複排除
- 再送や障害時のリカバリ
- 後段分析基盤への連携
このあたりを考えると、C# のサービス単体で SMTP 受信まで全部背負うより、SMTP は専用基盤に任せ、C# は解析と連携に集中するほうがきれいです。これは Exchange の制約と、ジャーナリング運用の責務分担を素直に反映した設計です。 (Microsoft Learn)
今回の前提構成
今回想定しているのは、次のような構成です。
Exchange Online / Exchange Server
↓ Journal Rule
journal@archive.example.com
↓
Postfix などの SMTP 受信基盤
↓ .eml 保存
.NET / C# Journal Receiver
↓ MIME 解析 / JSON 正規化
Azure Event Hubs
↓
Microsoft Fabric Eventstream
↓
Eventhouse または Lakehouse
Fabric 側は、Eventstream で Azure Event Hubs をソースとして受けられます。さらに Eventstream はイベントの変換やルーティングを行い、宛先として Lakehouse や Eventhouse を使えます。リアルタイム検索や監視寄りなら Eventhouse、長期分析や Delta テーブル化を重視するなら Lakehouse が相性のよい選択です。 (Microsoft Learn)
Azure Event Hubs は、フルマネージドのイベントストリーミング基盤で、低遅延・高スループットでイベントを受け付けられます。.NET 向けの公式クライアントも提供されており、複数コンシューマーへ同じイベントを配る pub/sub の中継点として使いやすいです。 (Microsoft Learn)
なぜ Postfix を推すのか
この記事のポイントはここです。
「C# でサービスを作る」のに、なぜあえて Postfix を前段に置くのか。
理由は、SMTP を受ける責務と、ジャーナルを業務データとして扱う責務は分けたほうが圧倒的に安定するからです。
参考
https://qiita.com/lowgain/items/6c4083a047b06dfb4310
1. SMTP サーバーとしての責務を自前で持たなくてよい
ジャーナル受信サービスを C# 単体で作る場合、単に MIME を読むだけではなく、SMTP サーバーとしての振る舞いも考えなければいけません。
- SMTP セッション処理
- TLS
- 一時障害時の再送
- キュー管理
- 受信失敗時のエラー応答
- 受信順序や同時接続
- ディスクへの安全なスプール
この部分は、アプリケーションの本質ではありません。
しかも、ジャーナリングは「取りこぼしが気持ち悪い」タイプの要件です。そこで SMTP の成熟した実装に寄せたほうが安全です。Postfix はそのための定番の一つで、メール受信の責務を安定して肩代わりしてくれる存在です。これは一般的な設計判断ですが、Exchange のジャーナリングが外部配送と NDR 監視を利用者側責任としていることを考えると、かなり相性がよいです。 (Microsoft Learn)
2. C# 側を「解析サービス」に集中させられる
Postfix を前段に置くと、C# 側は SMTP をしゃべる必要がなくなります。
やることはかなり明確です。
-
.emlを読む - journal report を解析する
- 添付された元メールを取り出す
- 必要項目を JSON に正規化する
- Event Hubs に送る
- 原本保全用ストレージへ保存する
つまり、C# 側は メールサーバーではなく、ジャーナル正規化パイプラインになります。
この分離は、実装もテストもかなり楽です。
3. 障害時の切り分けがしやすい
構成が分かれていると、障害の切り分けも明快です。
- Exchange から届いていないのか
- SMTP 受信までは来ているのか
-
.eml保存までは成功しているのか - C# パーサーで失敗したのか
- Event Hubs 送信で失敗したのか
- Fabric 側取り込みで詰まっているのか
全部を 1 サービスに押し込むと、この切り分けがかなり苦しくなります。
4. 一時的な後段障害に強くできる
たとえば Event Hubs が一時的に失敗した、Fabric 側がメンテ中だった、といったときでも、Postfix 側で受信済みのメールが安全にスプールされていれば、C# サービスは後から再処理できます。
この「まず受け切る」「後で処理する」という分離は、ジャーナルのような証跡系データではかなり重要です。
Event Hubs 自体も高スループットですが、正本保存というよりはイベント中継に向いた基盤なので、前段に SMTP スプール、後段に原本保全を持つ設計のほうが安心です。 (Microsoft Learn)
Postfix を使わない案はどうか
もちろん、Postfix 以外の選択肢もあります。
C# が直接 SMTP を受ける
PoC ならできます。
ただし本番では、SMTP サーバー品質の実装まで背負うことになり、かなり重くなります。
別メールサーバーで受けて IMAP で回収する
これもできます。
ただ、今度はメールボックス運用や既読管理、回収遅延、肥大化対策が必要になります。
Exchange Online のメールボックスで受ける
これは Exchange Online のジャーナリングでは不可です。
ジャーナリング先も alternate journaling mailbox も Exchange Online のメールボックスにはできません。 (Microsoft Learn)
この比較をすると、専用 SMTP 受信基盤として Postfix を置く案が、実装量と運用安定性のバランスがよいと感じます。
.NET / C# 側の役割
C# サービスの役割は、受信した .eml を「分析しやすい JSON」に変換することです。
主な処理は次の通りです。
- journal report 原文を読む
- 添付されている元メール
message/rfc822を抽出する -
Message-Id,Subject,From,To,Cc,Dateなどを取り出す - 原本の SHA-256 を計算する
- JSON に正規化する
- Event Hubs に送る
- 必要なら原本
.emlを Blob などへ保存する
ここで大事なのは、Event Hubs に送る JSON を正本にしすぎないことです。
JSON は分析用、原本 .eml は証跡用、という分離が無難です。これは記事としては設計判断ですが、ジャーナリングが元メッセージを外部に保持するための仕組みであることを考えると、元の .eml をどこかに残しておく意味は大きいです。 (Microsoft Learn)
Event Hubs 経由にする意味
Event Hubs を挟むメリットは、受信と消費を疎結合にできることです。
たとえば後段で、
- Fabric でリアルタイム分析する
- 別サービスでアラートを出す
- 他システムにも転送する
といった拡張がしやすくなります。
Event Hubs は publish-subscribe 型の高スケーラビリティなイベント基盤で、複数の受信側を持ちやすいのが強みです。 (Microsoft Learn)
また、Fabric Eventstream が Azure Event Hubs をソースとして扱えるので、Microsoft 系サービスで流れを組みやすいのも大きいです。 (Microsoft Learn)
Fabric 側はどこに着地させるべきか
Fabric では、用途に応じて主に 2 つの候補があります。
Eventhouse
向いているのは次のようなケースです。
- リアルタイム検索
- 監査ダッシュボード
- 異常検知
- 時系列集計
- KQL での即時分析
Lakehouse
向いているのは次のようなケースです。
- 長期保管
- Delta テーブル化
- Notebook / Spark 分析
- Power BI での二次活用
Eventstream は Event Hubs から受けたイベントを加工し、Lakehouse や Eventhouse に送れます。
そのため、まずは Eventhouse に入れて運用監視寄りに使い、必要に応じて Lakehouse にも広げる、という構成も取りやすいです。 (Microsoft Learn)
この構成の実装イメージ
かなりシンプルに書くと、全体の責務はこうなります。
Exchange
- Journal Rule を設定する
- 配送先は
journal@archive.example.com - alternate journaling mailbox も外部に用意する
Postfix
-
journal@archive.example.comを受信する -
.emlとしてスプール / 保存する - SMTP レイヤーの信頼性を担保する
.NET / C#
-
.emlをポーリングまたは監視する - MimeKit などでパースする
- JSON に正規化する
- Event Hubs に送信する
- 原本保存も行う
Fabric
- Eventstream で Event Hubs を受信する
- Eventhouse / Lakehouse に着地させる
この分離にすると、各層の責務がかなり明確です。
まとめ
Exchange のジャーナル受信サービスを .NET / C# で作るとき、
「C# で全部やる」のではなく、SMTP 受信は Postfix に任せ、C# はジャーナルの解析と JSON 化に集中するという設計はかなり相性がよいです。
特に Exchange Online では、
- ジャーナリング先に Exchange Online メールボックスは使えない
- alternate journaling mailbox も Exchange Online に置けない
- ジャーナリングは古い機能で、外部配送や NDR 監視は利用者責任
という制約があるため、外部に専用受信基盤を持つ前提で考えるのが自然です。 (Microsoft Learn)
そのうえで、
Exchange → Postfix → C# → Event Hubs → Fabric
という流れにすると、
- 受信の安定性
- 解析ロジックの単純化
- 後段との疎結合
- Fabric でのリアルタイム分析や長期分析
を両立しやすくなります。 (Microsoft Learn)
ジャーナルの受信は地味ですが、要件としてはかなり重い領域です。
だからこそ、SMTP は SMTP に強い仕組みに任せ、アプリケーションは「証跡を壊さず、扱いやすいデータに変換する」ことに集中したほうが、結果として堅い構成になると思います。