オンプレExchange - 階層型アドレス帳取得
はじめに
Hello, Zenn World!
初めまして。大阪公立大学 工学部 航空宇宙工学科 3年の田上満喜 (たのうえみつき)です。
今回は、オンプレミスの Exchange Server にて階層型アドレス帳のデータ取得を行う方法を共有したいと思います。
この記事を書こうと思った理由
勤務先の大学で用いられているExchangeがオンプレ環境であり、Graph APIが使えずに困っていました。EWSのAPIが複雑なため、Pythonのexchangelib
ライブラリを用いたデータ抽出を行います。
対象読者と本記事のゴール
- オンプレExchangeのデータ抽出に困っている人
- Exchangeから取れるデータを開発に使いたい人
- MSの解説サイトが難解な人 (Me too)
階層型アドレス帳をjson形式で取得できるようになるのが、本記事のゴールです。
階層型アドレス帳
では、いきましょう!
導入
必要なもの
- 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 Smith
はContoso,Ltd
直下の連絡先です。mailbox_type
がMailbox
のときは直下の連絡先、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,
)
出力例
[
{
"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サーバーにアクセスし、階層型アドレス帳データを取得することができました。これで、自由に組織図を作ったり、開発に活用できたりといろいろありますね。サンプルコードは以下においてあります。
次回に乞うご期待!
Discussion