🕌

オンプレExchange - 階層型アドレス帳取得

2024/08/20に公開

はじめに

Hello, Zenn World!
初めまして。大阪公立大学 工学部 航空宇宙工学科 3年の田上満喜 (たのうえみつき)です。
今回は、オンプレミスの Exchange Server にて階層型アドレス帳のデータ取得を行う方法を共有したいと思います。

この記事を書こうと思った理由

勤務先の大学で用いられているExchangeがオンプレ環境であり、Graph APIが使えずに困っていました。EWSのAPIが複雑なため、Pythonのexchangelibライブラリを用いたデータ抽出を行います。
https://github.com/ecederstrand/exchangelib

対象読者と本記事のゴール

  • オンプレExchangeのデータ抽出に困っている人
  • Exchangeから取れるデータを開発に使いたい人
  • MSの解説サイトが難解な人 (Me too)

階層型アドレス帳をjson形式で取得できるようになるのが、本記事のゴールです。
階層型アドレス帳
階層型アドレス帳

https://learn.microsoft.com/en-us/exchange/address-books/hierarchical-address-books/hierarchical-address-books

では、いきましょう!

導入

必要なもの

  • Python
  • exchangelibライブラリ
  • Exchangeサーバーへのアクセス権限を持つユーザーアカウント

インストール

pipを使ってexchangelibをインストールします。

pip install exchangelib

セットアップ

USERNAME PASSWORD SMTPを適宜置き換えてください。
USERNAMEには WINDOMAIN\username PrimarySMTPAddress User Principal Name (UPN)
のどれかをいれましょう。

from exchangelib import Credentials, Account, DELEGATE
USERNAME = "WINDOMAIN\\yourusername"
PASSWORD = "yourpassword"
SMTP = "yourusername@example.com"

credentials = Credentials(username=USERNAME, password=PASSWORD)
my_account = Account(
    primary_smtp_address=SMTP,
    credentials=credentials,
    autodiscover=True,
    access_type=DELEGATE,
)

サーバー側がautodiscoverに対応していない場合は自力で指定してやります。

from exchangelib import Credentials, Configuration, Account, DELEGATE
USERNAME = "WINDOMAIN\\yourusername"
PASSWORD = "yourpassword"
SMTP = "yourusername@example.com"
SERVER = "mail.example.com"

credentials = Credentials(username=USERNAME, password=PASSWORD)
config = Configuration(server=SERVER, credentials=credentials)
my_account = Account(
    primary_smtp_address=SMTP,
    credentials=credentials,
    autodiscover=False,
    config=config,
    access_type=DELEGATE,
)

接続テスト

print(my_account.root.tree())

を実行しましょう。

root
├── inbox
│   └── todos
└── archive
    ├── Last Job
    ├── GitLab issues
    └── Mom

このような出力を得たらほぼ勝ちです。お疲れ様でした。

本題

Exchangeの階層型アドレス帳は配布リスト (以下、DL) を用いて実装されています。
exchangelibのプロトコルexpand_dlを使いましょう。

階層型アドレス帳

この例を使って説明します。
expand_dlに、まずは最上階層の名前orメールアドレスを指定します。

print(my_account.protocol.expand_dl("Contoso,Ltd"))

このような出力を得ます。

[Mailbox(name='John Smith', email_address='john@example.com', routing_type='SMTP', mailbox_type='Mailbox', item_id=None),
 Mailbox(name='Corporate Office', email_address='corporate@example.com', routing_type='SMTP', mailbox_type='PublicDL', item_id=None),
 Mailbox(name='Product Support Organization', email_address='product@example.com', routing_type='SMTP', mailbox_type='PublicDL', item_id=None),
 Mailbox(name='Sales & Marketing Organization', email_address='sales@example.com', routing_type='SMTP', mailbox_type='PublicDL', item_id=None)]

John SmithContoso,Ltd直下の連絡先です。mailbox_typeMailboxのときは直下の連絡先、PublicDLのときは、組織を表していることが分かります。
この点を考慮して、コードを作成しましょう。

コード例

コピペして使おう。

from exchangelib import Credentials, Account, DELEGATE
import json

USERNAME = "WINDOMAIN\\yourusername"
PASSWORD = "yourpassword"
SMTP = "yourusername@example.com"
TOP = "toplevelname"

credentials = Credentials(username=USERNAME, password=PASSWORD)
my_account = Account(
    primary_smtp_address=SMTP,
    credentials=credentials,
    autodiscover=True,
    access_type=DELEGATE,
)


def build_hierarchy(TOP):
    hierarchy = []
    for i in my_account.protocol.expand_dl(TOP):
        if i.mailbox_type == "PublicDL":
            hierarchy.append(
                {
                    "name": i.name,
                    "email_address": i.email_address,
                    "mailbox_type": i.mailbox_type,
                    "children": build_hierarchy(i.email_address),
                }
            )
        else:
            hierarchy.append(
                {
                    "name": i.name,
                    "email_address": i.email_address,
                    "mailbox_type": i.mailbox_type,
                }
            )
    return hierarchy


hierarchy = build_hierarchy(TOP)

with open("output.json", "w", encoding="utf-8") as f:
    json.dump(
        hierarchy,
        f,
    )

出力例

output.json
[
  {
    "name": "a_dl",
    "email_address": "a_dl@example.com",
    "mailbox_type": "PublicDL",
    "children": [
      {
        "name": "user A",
        "email_address": "a@example.com",
        "mailbox_type": "Mailbox"
      }
    ]
  },
  {
    "name": "b_dl",
    "email_address": "b_dl@example.com",
    "mailbox_type": "PublicDL",
    "children": [
      {
        "name": "b_a_dl",
        "email_address": "b_a_dl@example.com",
        "mailbox_type": "PublicDL",
        "children": [
          {
            "name": "user D",
            "email_address": "d@example.com",
            "mailbox_type": "Mailbox"
          }
        ]
      }
    ]
  }
]

コードの説明

build_hierarchy関数は、指定されたDL TOPの階層構造を再帰的に構築しています。

  • my_account.protocol.expand_dl(TOP)で、TOPのDLを展開し、そのメンバーを取得します。
  • メンバーがさらにDL(PublicDL)である場合、そのメンバーに対して再帰的にbuild_hierarchyを呼び出し、子リスト(children)として構造に追加します。
  • メンバーが通常のメールボックスである場合、そのメンバー情報を追加します。

まとめ

exchangelibを使うことで、PythonからExchangeサーバーにアクセスし、階層型アドレス帳データを取得することができました。これで、自由に組織図を作ったり、開発に活用できたりといろいろありますね。サンプルコードは以下においてあります。
https://github.com/miikun77/exchangelib-sample
次回に乞うご期待!

TryAngle@大阪公立大学

Discussion