読者です 読者をやめる 読者になる 読者になる

からめもぶろぐ。

ワタシ SharePoint チョット デキル

System.DirectoryServices 名前空間を使って LDAP 検索をする

C#

System.DirectoryServices.dll を参照設定すると LDAP (Lightweight Directory Access Protocol) による Active Directory 検索ができるようになります。やってみたらそれほど難しくはありませんでした。

ユーザーの検索をしてみる

domain.local ドメインに対してユーザーログオン名 (userPrincipalName) で検索してみます。DirectoryEntry クラスは Active Directory のオブジェクトをカプセル化します。DirectorySearcher クラスでクエリ式を作成します。

    public static class Program {

        private static void Main(string[] args) {
            // 検索するルート階層を指定する
            using (var entry = new DirectoryEntry("LDAP://domain.local/CN=Users,DC=domain,DC=local"))
            using (var searcher = new DirectorySearcher(entry)) {
                // ユーザーログオン名が hoge@domain.local のユーザーを検索する
                searcher.Filter = "(userPrincipalName=hoge@domain.local)";
                // 1 件だけ抽出するときは FindOne メソッドで
                // 結果が複数件戻ってくるときは FindAll メソッドを使う
                var result = searcher.FindOne();
                foreach (var value in result.GetDirectoryEntry().Properties
                    .Cast<PropertyValueCollection>()
                    .OrderBy(x => x.PropertyName)) {
                    // 属性と値をすべて表示する
                    Console.WriteLine("{0}={1}", 
                        value.PropertyName, 
                        string.Join(";", value.Cast<object>().Select(x => x.ToString())));
                }
            }
            Console.ReadKey();
        }

    }

LDAP クエリは書き方が特殊で、and 条件は (&(A=b)(B=b)) のように書き、or 条件は (|(A=a)(B=b)) のように書きます。慣れてしまえばそれほどは難しくありません。ワイルドカードも使えますので、ある程度自由の利いた検索ができるようになっています。
FindOne メソッドの戻り値は SearchResult クラスです。GetDirectoryEntry メソッドを呼び出せば DirectoryEntry クラスのオブジェクトが取得できるので、属性を取得したり、さらに別の検索をすることができます。ちなみに Active Directory の属性は単一の値を返すものと複数の値を返すものがあるので、PropertyValueCollection クラスには Value プロパティと Items プロパティの 2 種類の方法が用意されています。今回は LINQ でぶん回して複数の値を string.Join するようにしています。

ユーザーが属するグループの検索をしてみる

ちょっとだけ応用編。検索したユーザーが所属するグループを検索します。グループの member 属性には識別名が入っているのでユーザーの distinguishedName 属性の値を渡してあげます。今度は結果が複数あるので FindAll メソッドを使っています。LINQ には対応していないので Cast したり Select しなければいけないのは御愛嬌ということで。

    public static class Program {

        private static void Main(string[] args) {
            using (var entry = new DirectoryEntry("LDAP://domain.local/CN=Users,DC=domain,DC=local"))
            using (var searcher = new DirectorySearcher(entry)) {
                searcher.Filter = "(userPrincipalName=hoge@domain.local)";
                var result = searcher.FindOne();
                var user = result.GetDirectoryEntry();
                // 識別名を取得する
                var distinguishedName = user.Properties["distinguishedName"].Value;
                // メンバーにユーザーが含まれるグループを検索する
                searcher.Filter = string.Format("(&(objectClass=group)(member={0}))", distinguishedName);
                var groups = searcher.FindAll();
                foreach (var group in groups.Cast<SearchResult>().Select(x => x.GetDirectoryEntry())) {
                    Console.WriteLine(group.Properties["cn"].Value);
                }
            }
            Console.ReadKey();
        }

    }

もちろん Exchange が入っていれば拡張スキーマも同じように検索できます。