次の方法で共有


ディレクトリ サービス 使っていますか

.NET Framework 2.0 を用いた新しい Active Directory の管理方法

Ethan Wilansky


この記事で取り上げる話題:

  • System.DirecrotyServices 名前空間の強化
  • ADSI プロパティの管理への InvokeSet と InvokeGet メソッドの使用
  • Active Directory セキュリティ記述子の読み込みと書き込み
  • Active Directory の拡張検索機能の使用
この記事で使用する技術:
.NET Framework 2.0、Active Directory
サンプルコードのダウンロード:
DirectoryServices.exe (183KB)

目次

InvokeGet と InvokeSet へようこそ
Active Directory セキュリティ記述子
DirectorySearcher の扱い方
Tombstone 検索
ExtendedDN 検索
Attribute Scoped Query
Virtual List View 検索
ほかには


より多くの企業が Active Directory を使用するようになるにつれ、Microsoft は、そのインストール、導入、管理が容易になるよう、時間と努力を費やしてきました。開発者は、.NET Framework 2.0 中の System.DirectoryServices 名前空間の強化を見ていることでしょう。さらに、Microsoft は System.DirectoryServices:ActiveDirectory の下に 2 つの新しい名前空間を加えており、それらは Active Directory の管理と、Lightweight Directory Access Protocol (LDAP) 3.0 と Directory Services Markup Language (DSML) 2.0 標準との連携を実現するプロトコルを提供するものです。

.NET Framework 1.x でも同じように、System.DirectoryService は Active Directory Service Interfaces (ADSI) のトップにあるマネージ コード レイヤです。しかしここでは、Active Directory 名前空間中の一部のクラスのみが ADSI レイヤに依存しており、プロトコル名前空間には ADSI レイヤに依存するものはないことに注意してください。マネージ コードから利用できない ADSI インターフェイスにアクセスする必要があるとき、最も簡単なやり方は COM interop を使うことでした。Microsoft .NET Framework 2.0 では、COM interop を使用する必要が大きく減らされています。この記事では、これらのマネージ コード中の多数の改良点について、ADSI プロパティの呼び出しから、セキュリティ記述子との相互作用、そして最後にいくつかの強力な検索拡張機能の使用についてまで記述します

InvokeGet と InvokeSet へようこそ

ディレクトリ オブジェクトの属性の読み込みと書き込みは、Active Directory のようなディレクトリに共通の管理アクティビティです。.NET Framework で属性の読み込みと書き込みを行うには、まず DirectoryEntry クラス (System.DirectoryServices 名前空間の一部) を使いディレクトリ オブジェクトにバインドしたのち、値の読み込みと書き込みに PropertyCalueCollection クラスを使用します。値の書き込み後には、DirectoryEntry クラスの CommitChanges メソッドをコールし、ディレクトリへの変更を明言します。図 1 は単一値属性の読み込みと書き込み操作の標準的な例を示しています。

属性の読み込みと書き込みの双方にて、System.DirectoryServices は ADSI IAD のコア インターフェイスに依存しています。IAD への依存の利点は、属性から属性へ提供される一貫性にあります。いずれにしても、"displayName" が、読み込みあるいは書き込みを行うべき属性の lDAPDisplayName です。Active Directory 中の全ての属性が、関連する lDAPDisplayName を持っています。

多値属性の読み込みと書き込みも、同じくらい容易です。ディレクトリ オブジェクトへのバインドののち、図 1 に示したように、多値属性のコンテンツを通して繰り返すために foreach ループを使います。多値属性の書き込みには、PropertyValueCollection Add あるいは Insert メソッドを使います。複数値を加えるには、AddRange メソッドを使います。そして、ご想像のように、値の消去には Remove メソッドを使います。以下のコードは、2 つの値を otherTelephone 多値属性に書き込むための AddRange メソッドの使い方を説明しています。

... // 以前のコードの抜粋で示したように、ディレクトリ オブジェクトにバインド

// AddRange メソッドを使い、複数の値を追加
de.Properties["otherTelephone"].AddRange(
    new object[] { "<phone number 1>", "<phone number 2>" });
de.CommitChanges();

ディレクトリ属性の管理の際は、詳細な記述が最も重要です。属性には、読み込みと書き込みを複雑にする 3 つの特性が存在しています。

  • 属性の構文
  • その属性上でシステムが実行を許している操作
  • 属性中のデータの意味

 

これらの特性に関するより詳細な情報については、Platform SDK Directory スキーマのトピックスをご覧ください。そのほかに、.NET Framework 2.0 には、attributeSchema オブジェクトに対する条件検索に使用できる新しい System.DirectoryServices.ActiveDirectory 名前空間が含まれています。この名前空間は、最初の 2 つの属性の特性の取り扱いに役立ちます。3 つ目の特性、すなわち属性中のデータの意味に関しては、データを理解するため SDK ドキュメントを読む必要があります。

Active Directory のスキーマには、simple、string、time そして object reference といった主なデータ型のカテゴリが含まれています。これらそれぞれの主なカテゴリの中で、Active Directory は、Boolean や integer といった単純データ型、NTSecurityDescriptor や OctetString といった文字列データ型、そして GeneralizedTime や UTCTime といった日付データ型など、複数の属性構文 (全部で 28) を定義しています。データ型の全リストは、トピック「Syntaxes for Active Directory Attributes」 (英語) 中の Platform SDK から、あるいは ADSTypeEnum 一覧表のコンテンツを参照することによって、入手できます。属性を読み込むコードを書く前に認識しておくべき、多様な構文の選択肢が存在しています。

userAccountControl 属性の値を読み込み表示する以下のコードについて、考察してみましょう。

Console.WriteLine("userAccountControl: {0}",
  de.Properties["userAccountControl"].Value);

userAccountControl 属性は、戻り値が 544 のような値となる integer データ型です。ただし、この属性に少しばかり詳しい場合を除けば、この 10 進の値は無意味なものです。実は、userAccountControl 属性はユーザー アカウントに関連付けられた様々な設定のビット マスクとなっています。userAccountControl 中のビット値を変更することで、フラグ (ADSI ADS_USER_FLAG 一覧表にて定義) を設定します。たとえば、以下のコードに示すように、userAccountControl 属性の 2 つ目のビット (ADS_UF_ACCOUNTDISABLE フラグ) を評価することで、ユーザーアカウントが無効となっているかどうかを確認することができます。

using System;
using System.DirectoryServices;
using ActiveDs;  // ここで AD ユーザーのフラグ一覧を入手
...
bool isDisabled;
isDisabled = ((int)de.Properties["userAccountControl"].Value &
   (int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE) != 0;
Console.WriteLine("Account disabled: {0}", isDisabled);

PropertyValueCollection.Value プロパティを使った userAccountControl 中のビット設定の例が、この記事用のコード ダウンロード中に含まれています。(コードをコンパイルし実行する前に、"コードについて" サイドバーを良くお読みください。)

以前のコードは、ADSI ActiveDS タイプ ライブラリを使用することに注意してください。この ADSI タイプ ライブラリでは、様々なものとともに、ADS_USER_FLAG の一覧表が公開されています。ADS_UF_ACCOUNTDISABLE の値に定数を設定することもできますが、マネージ コードを介して一覧表がまだ公開されていないなら、ActiveDs 中の、この、そしてその他の一覧表を巧みに利用するという、より洗練された方法をとることができます。

コードについて

添付のコード  ダウンロードには、この記事に記述されている機能をカバーする、サンプル アプリケーションが含まれています。これは、可能な限り直接的なコードを維持したコンソール アプリケーションとして書かれています。このアプリケーションをコンパイルし実行すると、スイッチが並んで現れます。これらのスイッチのいずれかをコールすることで、サンプルを実行できます。以下に、このアプリケーションをコンパイルし実行する前の重要な注意事項を列挙します。

  1. このコードは、製品化可能なユーティリティとしては作成されていません。コード中には、ほとんどエラーハンドリングが含まれていません。
  2. program.cs ファイル中のパス値を、Active Directory のそれぞれの実装環境における有効なパス値に変更する必要があります。
  3. コンピュータは、コードが実行されるドメインのメンバである必要があります。
  4. このコードは、ADAM ではなく Active Directory 上で実行する必要があります。
  5. administrator あるいはそれと等価なユーザーとしてログインしている必要があります。
  6. .NET Framework 2.0 が、コードをコンパイルし実行するコンピュータ上にインストールされていることを確認してください。
  7. InvokeGet スイッチを実行する前に InvokeSet スイッチを実行してください。

これまでは、全ての例で、読み込みと書き込み操作への PropertyValueCollection クラスの単純な使用を、無理なく示してきました。しかしながら、これは表面を擦っているようなものに過ぎません。内在する ADSI IAD のコア インターフェイス単独の使用では読み込みや設定の行えない属性も、いくつか存在しています。この顕著な例がユーザー アカウントのロックアウト ステータスです。Active Directory が userAccountControl 属性の 5 つ目のビットにロックアウト ステータスを保存していることが、ADSI の文書によって示唆されています。おそらくこれは、SAMAccounts のロックアウト ステータスには当てはまりますが、Active Directory アカウントには当てはまりません。そのかわり、アカウントがロックされているのか、あるいはいつロックされていたのかを判断するためには、lockoutTime 属性の値を読み込む必要があります。lockoutTime 属性には、開始日 1601 年 1 月 1 日から数えて、いつアカウントがロックされたかを正確に示す Universal Time Coordinate (UTC) 値が、100 ナノ秒刻みで保存されています。lockoutTime 向けの属性構文は LargeInteger です。PropertyValueCollection クラスは、large integer を理解できる値に変換するメソッドを提供しません。かわりに、COM interop を使い ADSI IAD の LargeInteger インターフェイスにアクセスすることで、PropertyValueCollection クラスを補助する必要があります。[図 2](https://msdn.microsoft.com/ja-jp/ee677406#fig2) は、このタスクを実行するコードを示しています。

しかしながら、このコードの目的は lockTime 属性の読み取り方を示すことでも、large integers として保存されている属性の読み取り方を示すことでもありません。むしろ、重労働の価値の無い IAD のコア インターフェイスをたまには使い続けるようにという提案の前口上としてとらえてください。

ありがたいことに、Active Directory 属性のかわりに ADSI プロパティを値の読み込みに使うという、別のオプションも存在します。たとえば、IADsUser 永続インターフェイスの IsAccountLocked プロパティを使うことで、アカウントがロックされているかどうかを判断できます。.NET Framework 1.1 を使いこのプロパティを読み込むには、以下のコードに示したように、Type.InvokeMember メソッドとリフレクションを使う必要があります。

using System.DirectoryServices;
using System.Reflection;
...
object ads = de.NativeObject;
Type type = ads.GetType();
bool isLocked = (bool)type.InvokeMember
    ("IsAccountLocked", BindingFlags.GetProperty, 
    null, ads, null);
Console.WriteLine("The IsAccountLocked property is: {0}",
    isLocked);

これは、単一プロパティの読み出しには魅力的な作業量ですし、さらにありがたいことに、.NET Framework 2.0 では ADSI オブジェクトのプロパティの読み込みと書き込み用に InvokeGet と InvokeSet メソッドが用意されていることから、この作業はさらに容易となっています。[図 2](https://msdn.microsoft.com/ja-jp/ee677406#fig2) のコードや直前で示したコードとは対照的な、InvokeGet を用いた IsAccountLocked プロパティの読み込みと表示を達成するコードをここに示します。

using System.DirectoryServices;
...
Console.WriteLine("Account locked: {0}", 
    de.InvokeGet("IsAccountLocked"));

プロパティの書き込みも同様に容易です。InvokeSet は、プロパティ名と設定したい値の 2 つのパラメータを必要とします。しかし、Active Directory では、内在する lockoutTime 属性については、その読み込みと、それを 0 に設定すること、すなわちロックを外すことのみに制限されています。そのため、この場合は InvokeSet は 1 パラメータのみ、すなわち Boolean の false のみを必要とします。

de.InvokeSet("IsAccountLocked", false);
de.CommitChanges();

ADSI プロパティは Active Directory オブジェクトに関連する全ての属性へのアクセスを提供しないこと、および ADSI プロパティの名前は、それが変更を行う属性の、内在する lDAPDisplayName からそれていることから、ADSI プロパティの使用を勧めません。ADSI プロパティが Active Directory 属性の設定のうちの 1 つのみを示すものであるにもかかわらず、単一の Active Directory 属性に Active Directory の複数設定の値が保存されることがあることから、ある場合にはこの名前の分岐は必須です。userAccountControl 属性と IsAccountDisabled プロパティは、その名前分岐の格好の例を提供しています。しかしながら、その他の方法での読み込みと書き込みが難しい属性に対しては、InvokeGet と InvokeSet は DirectoryEntry クラスへのありがたい追加機能です。この記事のコード ダウンロード中に、追加の例が含まれています。たとえば、コード ダウンロードには、InvokeGet と InvokeSet をそれぞれ、userParameters 属性に含まれるターミナル サービスのユーザー プロファイル設定の読み込みと書き込みに使う例や、複数値プロパティの読み込みと書き込みに使う例が示されています。

Active Directory セキュリティ記述子

.NET Framework 1.x では、4 つの ADSI セキュリティ インターフェイスのうちの 3 つ (IADsSecurityDescriptor、IADsAccessControlList そして IADsAccessControlEntry) にアクセスするために、COM interop を使い Active Directory セキュリティ記述子を変更しました。おそらく、.NET Framework version 1.x から version 2.0 への移行による、System.DirectoryServices 中の最も顕著な改善点の 1 つは、マネージ コードから Active Directory と Active Directory Application Mode (ADAM) セキュリティ記述子の変更が必須となったことです。

もし、まだセキュリティ記述子にあまりなじみがないようでしたら、MSDN Magazine 2005 年 1 月号の Keith Brown による Security Brief コラム (英語)、および MSDN Magazine 2004 年 11 月号の Mark Pustilnik による記事 "Safety in Windows (英語)" を見てみることをお勧めします。これら 2 つの記事はセキュリティ記述子についての入門書ではありませんが、Active Directory セキュリティ記述子管理の多くの改善点の根拠に対する深い洞察を与えてくれます。

図 3 には、Active Directory セキュリティ記述子の構造と、それと情報のやり取りを行う際に使用する .NET Framework 2.0 の主な名前空間とクラスが示されています。

**
図 3 **セキュリティ記述子の構造

Active Directory ntSecurityDescriptor 属性は、複雑な構造をしています。マネージ コードからこの属性と連携するには、System.DirectoryServices、System.Security.Principal および System.Security.AccessControl 名前空間中のさまざまなクラスが必要です。この複数の名前空間との深い関係は、マネージ Access Control List (ACL) クラスの構築中に Microsoft が始めた作業の結果です。これらのクラスは、セキュリティ記述子変更のための統一的な扱い方を提供します。このクラスとの深い関係全体により ntSecurityDescriptor 属性とのやり取りを難しく感じるかもしれませんが、これはそんなに悪いものでもありません。

セキュリティ記述子との連携に先立って、最初のコード例で示したように、まず Active Directory オブジェクトとバインドする必要があります。オブジェクトとのバインド後、新しいSystem.DirectoryServices ObjectSecurity プロパティを使い、セキュリティ記述子を読み出します。しかし、そう簡単にはことは運びません。ObjectSecurity をコールする前に、System Access Control List (SACL) からの読み込み、あるいは SACL への書き込みが重要であるかどうかを決定する必要があります。SACL には、Active Directory オブジェクトへの監査権限を適用するための Access Control Entries (ACEs) が含まれています。

SACL の読み込みが重要ならば、DirectoryEntryConfguration クラスのサービスを使用します。.NET Framework 2.0 で新しく登場したこのクラスは、プロバイダ固有のオプションにアクセスするための ADSI IADsObjectOptions インターフェイスをコールします。現在のところ IADsObjectOptions は Active Directory に対応しており、この場合はこれをセキュリティ記述子からの SACL の読み出しに使用します。以下のコードにて、SACL を得る方法と、引き続いて Active Directory セキュリティ記述子の管理操作の実行に先立って ntSecurityDescriptor を得る方法を示します。

// セキュリティ記述子の SACL をインクルード
de.Options.SecurityMasks = SecurityMasks.Owner | SecurityMasks.Group |
    SecurityMasks.Dacl | SecurityMasks.Sacl;

// ObjectSecurity プロパティを使い、
// Active Directory セキュリティ記述子を得る
ActiveDirectorySecurity sd = de.ObjectSecurity;

図 4 は、セキュリティ記述子のプロパティを読み込む方法を示しています。Active Directory オブジェクトにバインドし、セキュリティ記述子を読み出したあと、プライマリ グループとセキュリティ記述子の所有者を得るために GetGroup と GetOwner メソッドを使います。いずれの戻り値も、コードが示しているとおり、SecurityIdentifier クラスの変換メソッドを使い Windows NT のユーザー アカウントとして簡単に表示できる SecurityIdentifier となります。

[図 4](https://msdn.microsoft.com/ja-jp/ee677406#fig4) に登場する残りのプロパティは、セキュリティ記述子が正しい順序で ACEs を含んでいるかどうか、そして ACEs が継承されているかどうかに関する情報を提供します。Windows Server? 2003 は ACEs を正しく並べますが、Windows 2000 Server では、ユーザーが、ローカル プロパティ キャッシュからセキュリティ記述子に、ACEs を適切な順序で書き込む必要があります。したがって、Windows 2000 Server 上のセキュリティ記述子を管理する、あるいは読み込む際には、AreAccessRulesCanonical と AreAuditRulesCanonical プロパティの確認は無駄ではありません。

Active Directory セキュリティ記述子には、2 つの ACL、Discretionary Access Control List (DACL) と SACL が含まれています。図 3に示したとおり、これらのリストは ACEs を含んでいます。DACL の場合、これらはアクセス許可であり、SACL の場合、これらは監査許可です。これら 2 タイプの ACEs の管理は、いくらかの差はありますが、マネージ コード中では本質的に同じです。

この文書とともにダウンロードできるソリューションには、ACL 中の ACEs の読み込みと書き込みのための例が含まれています。ここでは、ACEs 読み込みと書き込みのためのコード全体に多くの時間を割くかわりに、DACL と SACL の ACEs 読み込みの間の、いくつかの重要な違いを指摘しておきます。

セキュリティ記述子中の ACEs へのアクセスのためのメソッドには、DACL 中の ACEs 向けの GetAccessRules と SACL 中の ACEs 向けの GetAuditRules の 2 つがあります。これらのメソッドにより、明示的に割り当てられた ACEs のみを得たいのか、引き継がれた ACEs のみを得たいのか、あるいはその両方を得たいのかを指定できます。以下のコードにて、sd という名のセキュリティ記述子から明示的に割り当てられた ACEs と引き継がれた ACEs の両方を得る方法を示します。

sd.GetAccessRules(true, true, typeof(SecurityIdentifier))

GetAccessRules メソッドが権限ルールのコレクション (AuthorizationRuleCollection) を返すのに対し、GetAuditRules メソッドは監査ルールのコレクション (AuditRuleCollection) を返します。どちらの場合でも、コレクションの Count プロパティが 0 より大きいことを確認するところから始めてください。確認が取れたら、返された ACEs が ActiveDirectoryAccessRule 型かそれとも ActiveDirectoryAuditRule 型かを判断するために GetType メソッドをコールしてください。この情報から、引き続きコレクションを反復し、ACE の適切なプロパティをコールできます。たとえば ActiveDirectoryAccessRule に対しては ACE が Allow 型か Deny 型かを判断するために AccessControlType プロパティをコールし、ACE が ActiveDirectoryAuditRule であれば ACE が Success 型か Failure 型かを判断するために AuditFlags プロパティをコールします。

DirectorySearcher の扱い方

.NET Framework 2.0 の System.DirectoryServices の検索機能は、.NET Framework 1.0 には存在しなかった強力な新機能を提供します。以下のセクションでは、これらの 4 つの新しい検索機能について記述します。

  • Directory Synchronization (DirSync) 検索
  • Tombstone 検索
  • Attribute Scoped Query (ASQ) 検索
  • Virtual List View (VLV) 検索

Directory Synchronization 検索は、.NET Framework 2.0 の System.DirectoryServices に新しく追加された興味深い機能です。この機能は、Active Directory のドメイン、スキーマ、あるいは構成のパーティションに対する変更を追跡する、効果的なアプローチを提供します。DirSync は、ADSI DirSync コントロールのトップにあるマネージ コード レイヤです。このコントロールは、Active Directory 中の変更を明確に検索するように設計された、LDAP サーバ機能拡張です。

DirSync の操作は 2 段階のプロセスになっています。まず、パーティションのスナップショットを撮り、それをブロブ データ (クッキー) として保存します。次に、しばらく経ってからクッキーを読み込みなおし、それを同じパーティションと比較します。Active Directory と連携して比較や同期操作を実行するには、使用するデータがどこに保存されていたとしても、そのクッキーを保存する必要があります (たとえば、SQL Server の中であっても)。

この例そのままに、図 5 は、パーティションのスナップショットをファイルシステム上にキャプチャする方法を示しています。マネージ コードによるすべての検索操作のように、検索の設定と実行のためのオブジェクトの作成に DirectorySearcher クラスを使用します。DirectorySearcher はディレクトリ エントリ オブジェクトを必要とします。DirSync 検索の場合、ディレクトリ エントリ オブジェクトはパーティションのルートから始まっている必要があります。たとえば、fabrikam.com ドメインのルートにて検索を開始するには、ディレクトリ エントリ オブジェクトのパス プロパティは以下のようになります。

de.Path = "LDAP://DC=Fabrikam,DC=Com";

DirectorySercher オブジェクトを作成するには、DirectorySearcher コンストラクタにパーティションのルートを示すディレクトリ エントリ オブジェクトを与えてください。DirectorySearcher の作成を受けて、オブジェクトへのプロパティの設定が可能となります。たとえば、[図 5](https://msdn.microsoft.com/ja-jp/ee677406#fig5) は、一組の組織単位 (OUs) に検索を限定する際の Filter プロパティの設定方法や、検索の開始ポイント以下のすべてを検索する際のサブツリーへの検索スコープの設定方法を示しています。

次に、検索結果を保持するためのクッキーを作成するため、DirectorySercher から DirectorySynchronization オブジェクトを作成してください。SearchResult コレクションを反復したのち、DirSync クッキーをどこかに保存する必要があります。[図 5](https://msdn.microsoft.com/ja-jp/ee677406#fig5) では、ファイル システムの作成に FileStream オブジェクトと BinaryFormatter オブジェクトを用い、データをバイナリ フォーマットにて連続的に並べています。DirectorySynchronization オブジェクトの GetDirectorySynchronizationCookie メソッドが、データの連続化に関与しています。

スナップショット操作のあと、そして最初のキャプチャ操作のスコープの範囲内でディレクトリ オブジェクトに多少の変化が起こったのち、変化を検出するため再度 DirSync 検索を実行します。図 6 は、Active Directory パーティションをファイル システム上に保存されたクッキーと比較するコードを示しています。メソッドにより返される Boolean 値より、ユーザーは変化が検出されたかどうかを知ることができます。以下に、DirSyncChanges クラスを呼び出すコード例を示します。

if (!DirSynch.DirSyncChanges(de))
    Console.WriteLine("No changes detected");

比較操作を開始するために、データ ストアからクッキー (ディレクトリ パーティションの以前のスナップショット) をロードしてください。[図 5](https://msdn.microsoft.com/ja-jp/ee677406#fig5)では、コードはデータ ストアとしてファイル システムを利用しており、ADSync.data という名のファイルを作成しています。したがって、[図 6](https://msdn.microsoft.com/ja-jp/ee677406#fig6)に示したように、ADSync.data ファイルに保存されたクッキーを開くため、FireStream オブジェクトを作成してください。次に、BinaryFormatter オブジェクトを作成し、ストリーム ファイルのデータを復元化してください。変数名のクッキーとしてメモリ上に保存された復元化されたバイナリ データをともない、DirectorySynchronization コンストラクタをコールし、syncData という名の DirectorySynchronization オブジェクトを作成するために、コンストラクタの 1 つ目のパラメータとして今回はクッキーの内容を受け渡してください。DirectorySynchronization クラスには、DirSync 検索にてどのデータが返されるのかを完全に制御するための、オプション プロパティも含まれています。[図 6](https://msdn.microsoft.com/ja-jp/ee677406#fig6) に登場する ObjectSecurity オブジェクトは、検出された変更を見るためには検索を実行するコール元が複製の変更権限を持っていなくてはならないという要求を緩和します。しかしながら ObjectSecurity オブジェクトは、コール元が読み込み権限をもつオブジェクトおよび属性への検索結果の制限も行います。

DirectorySynchronization オブジェクトが元のスナップショットを表すようになったところで、DirectorySearcher オブジェクトを作成し、それにクッキーと比較するパーティションを表すディレクトリ エントリ オブジェクトを与えてください。DirectorySearcher コンストラクタにパラメータとして与えたルート パーティションが、クッキーの作成に使われたパーティションと、必ず同じになるようにしてください。次に、DirectorySearcher オブジェクトの DirectorySynchronization プロパティを syncData (パーティションの元のスナップショットを表す DirectorySynchronization オブジェクト) と同じものに設定し、検索結果のコレクションを反復してください。反復するものが何もない場合、クラス コードの先頭に初期化された changesDetected 変数が true に設定されておらず、またクラスが false を返すことから変更が検出されなかったことが示されます。

foreach ループの内部で、SearchResultCollection オブジェクトの標準的な反復以上にもう少し反復が発生していることに気づくかもしれません。ディレクトリ同期の結果には、常に ADsPath とオブジェクトの objectGUID、instanceType と distinguishedName 属性が含まれます。この情報はディレクトリ同期操作に役立ちますが、ディレクトリ パーティション内の検出された変更を単純に表示するためには、さほど興味深いものではないかもしれません。したがって、新しい、あるいは変更されたオブジェクトの ADsPath が返されたあとには、デフォルトではすべての新しい、あるいは変更されたオブジェクトに対して、コードは ADsPath の繰り返しを省略し 3 つの返り属性をリストアップしません。

[図 6](https://msdn.microsoft.com/ja-jp/ee677406#fig6)のコードでは、複合属性に対応するために何も特別なことは行っていません。たとえば、オブジェクトのセキュリティ記述子を変更することを想像してみてください。コードを実行すると、ntSecurityDescriptor が変化していることが通知されますが、変化の詳細までは通知されないでしょう。すでに述べてきたように、独自のコードにて読み込みあるいは書き込みを行うには、属性構文などの属性の独特な特性に対応しなければなりません。この記事のコード ダウンロードに含まれるさまざまな例から、複合属性に発生した変更の詳細を得るための第一歩が得られるはずです。

Tombstone 検索

マネージ コードにおける DirSync 検索は新規オブジェクトの作成や既存オブジェクトの変更の監査には非常に役立つ機能ですが、オブジェクトがディレクトリからいつ消去されたのか、あるいはオブジェクトはディレクトリから消去されているのかどうかを判断したいときもあります。こんなときが、非常に便利な .NET Framework 2.0 の新しい Tombstone 検索機能の出番です。Tombstone 検索により、ディレクトリ中に残る消去されたオブジェクトを DirectorySearcher の戻り値にできます。消去されたオブジェクトは、通常 DerectorySeracher の戻り値にはなりません。Active Directory の消去されたディレクトリ オブジェクトのデフォルト残存ポリシは 60 日です。オブジェクトの残存期間が終了していなければ、[図 7](https://msdn.microsoft.com/ja-jp/ee677406#fig7)に示したように、消去されたオブジェクトに関する情報を読み込むためにマネージ コードで Tombstone 検索を使用できます。

Tombstone 検索を開始するには、検索を設定し実行するためのオブジェクトを作成するために DirectorySearcher クラスを使ってください。Tombstone 検索のためのディレクトリ エントリ オブジェクトは、ドメイン パーティションとすることができます。しかしながら、Deleted Objects コンテナ内で検索を開始する方が、より効率的です。これは、デフォルト残存期間の間、すべての消去されたオブジェクト (親のないオブジェクトを含む) が保存されるコンテナです。たとえば、以下のディレクトリ エントリ パスは、fabrikam.com ドメイン内のこの特別なコンテナを指定します。

de.Path = "LDAP://cn=Deleted Objects,DC=Fabrikam,DC=Com";

SearchResultCollection オブジェクトの反復を試みる前に、コードが System.DirectoryServices.AuthenticationTypes 一覧表の FastBind オプションを指定している必要があります。これにより、クエリ実行前に Active Directory 中にオブジェクトが実際に存在しているかの確認を行わないよう、ADSI に指示しておきます。なお、FastBind はその他の目的も果たしますが、Tombstone 検索の実行という目標からすると、その他の機能はコードの実行に必須ではありません。DirectorySearcher の Filter プロパティを ‘isDeteled=TRUE’ に設定することで、DirectorySearcher は消去された項目のみを返すようになります。ActiveDirectory がすべての消去されたオブジェクトを Deleted Objects コンテナに保存していることから、この場合 DirectorySearcher の SearchScope プロパティの OneLevel への設定は妥当です。[図 7](https://msdn.microsoft.com/ja-jp/ee677406#fig7) にて、それぞれのオブジェクトに関するほんの一握りの返り属性にのみ、関心を集中させていることが見て取れるでしょう。PropertiesToLoad.AddRange メソッドを使うことで、これらの関心のある属性を指定しています。コードの次の部分は、消去されたオブジェクトへの検索を行うために必須です。すなわち、DirectorySearcher オブジェクトの Tombstone プロパティの true への設定です。これを行わないと、DirectorySearcher は検索結果中に消去されたオブジェクトを返しません。

DirectorySearcher を Tombstone 検索用に設定したあと、FindAll メソッドを使い、コードが SearchResultCollection を読み出しています。検索結果の反復の中で、コードが whenCreated 属性を local time で返せるように、データのちょっとした操作を行っています。

このコードを実行すると、少々おかしな ADsPath に気づくでしょう。消去されたオブジェクトの ADsPath フォーマットは、以下の形で与えられます。

LDAP://<original RDN name>\OADEL:<previous objectGUID string>,
<DN of current location>

Active Directory は、その現在位置の完全修飾ドメイン名を受け、元の相対識別名 (RDN) および元の GUID とともに消去されたオブジェクトを保存します。たとえば、以下の結果は group1 という名前のグループ向けの ADsPath を示しています。アウトプットは 1 行目および 2 行目に示されています。

ADsPath: LDAP://CN=group1\0ADEL:939a7dd5-02be-4616-abeb-59751b315f0f,
    CN=Deleted Objects,DC=FABRIKAM,DC=COM
LastKnownParent:        OU=HR,DC=FABRIKAM,DC=COM
WhenCreated (local):    7/21/2005 11:06:23 AM

このコードにて返されるように選択した、他の属性にも注目してみてください。LastKnownParent 属性は、オブジェクトが消去される前に最後に存在した位置を示します。WhenCreated 値は、Deleted Objects フォルダにオブジェクトが作成された時刻、すなわち Active Directory からオブジェクトが消去されたおおよその時刻を示します。

ExtendedDN 検索

次に述べる検索機能は ExtendedDN 検索です。たいていの場合、System.DirectoryServices を扱う仕事をする誰もが、識別名フォーマットに沿ったオブジェクトの distinguishedName 属性について考察したことがあるでしょう。たとえば、fabrikam.com ドメインの HR OU 内の User1 の DN は、以下のようになります。

CN=User1,OU=HR,DC=fabrikam,DC=com

共通フォーマットは便利ですが、Active Directory 内の、セキュリティ プリンシパルの SID や、いずれかのオブジェクトの GUID を読み込みたいときもあるでしょう。これを実現するために、ディレクトリ内のオブジェクトにバインドし、新しい InvokeGet メソッドを使用して IAD のコア インターフェイスから容易に GUID プロパティを得たり、SecurityIdentifier クラスを使用して SID をアカウント名に変換したりすることができます。しかしながら、これらのやり方は、ともに ADSI に設けられ System.DirectoryServices 向けに拡張された高性能の検索能力を使用しています。こんなときは ExtendedDN 検索の出番です。ExtendedDN 一覧表を使い、None、Standard あるいは HexString いずれかの DN フォーマットを指定できます。None はデフォルトの識別名フォーマットです。Standard は、文字列フォーマットにて GUID、SID そして DN を返します。HexString は 16 進法フォーマットにて GUID と SID を返します。以下は、Standard フォーマットにおける User1 アカウントの例です。

<GUID=9564482a-4822-4da4-bd38-ab6a740dc2d8>;<SID=S-1-5-21-2422933499-
3002364838-2613214872-1173>;CN=User1,OU=TechWriters,DC=FABRIKAM,DC=COM

そして以下が、HexFormat における同じアカウントの例です。

<GUID=2a4864952248a44dbd38ab6a740dc2d8>;<SID=010500000000000515000000f
b076b90a673f4b2987ec29b95040000>;CN=User1,OU=TechWriters,DC=FABRIKAM,D
C=COM

コード ダウンロードに、OU 内のオブジェクトや特別な Deleted Objects コンテナ内のオブジェクトに対して ExtendedDN 検索を実行する方法を示した例が含まれています。ExtendedDN 検索の実行における本質的要素は、DirectorySearcher の ExtendedDN プロパティを指定すること、その際に ExtendedDN 一覧表内の 3 つの値のうちの 1 つに等しい値を用いることです。図 8 内のコードは、文字列配列作成のための String.Split メソッドの使い方と、独立した文字列値として GUID と SID を表示させる方法を示しています。

Attribute Scoped Query

.NET Framework 2.0 の DirectorySercher に追加されたもう 1 つの有用な機能が ASQ です。標準的な検索では、検索スコープをドメイン パーティションや OU など、パーティションのどこかに設定します。ASQ は、検索操作の開始点として、属性の設定を可能とすることで、より高い精度の検索を実現しています。この検索は、Active Directory グループ オブジェクトのメンバ属性などの、大きな多値属性に対して特に有用です。以下に、ASQ を使い、グループのメンバ属性を読み込む単純な検索例を示します。

public static void ForMembers(DirectoryEntry de)
{
    using(DirectorySearcher srch = new DirectorySearcher(de))
    {
        srch.SearchScope = SearchScope.Base;
        srch.AttributeScopeQuery = "member";
        srch.PropertiesToLoad.Add("cn");

        using(SearchResultCollection results = srch.FindAll())
        {
            foreach (SearchResult res in results)
            {
                Console.WriteLine(res.Properties["cn"][0]);    
            }
        }
    }
}

ASQ 検索に対する DirectoryEntry は、ユーザー アカウントやグループのような Active Directory のリーフオブジェクトです。たとえば、上記の ForMembers コード向けの、DirectoryEntryPath プロパティのためのコードは以下のとおりです。

de.Path = "LDAP://CN=administrators,cn=Builtin,DC=Fabrikam,DC=Com";

検索を SearchScope.Base にスコープしていることに注目してください。クエリを属性にスコープしているとき OneLevel やサブツリーのスコープは意味が無いため、これは非常に重要な意味を持ちます。もし、Base 以外のものを指定したとすると、コードは途中で例外処理に陥ってしまうでしょう。クエリを属性にスコープするには、AttributeScopeQuery に属性名を設定します。

 

Virtual List View 検索

ここで議論する .NET Framework 2.0 最後の新検索機能は、Virtual List View 検索です。VLV 検索の目的は、ADSI より返される大きなデータ セットから小さなサブセットを読み出すことです。これは、大きなディレクトリ内でたくさんの情報を検索する際、ユーザに良いパフォーマンスを与えてくれる便利な機能です。VLV は、巨大なデータ セットによるユーザーのローカル キャッシュへの過負荷を回避します。この機能に関するもう少し詳しい情報については、前述した Keith Brown の Security Briefs コラムをご覧ください。また、ダウンロードの中にも、この機能の実例を含めてあります。

ほかには

この文書で示したように、System.DirectoryServices 名前空間にはかなりの数の重要な機能強化があります。SecurityMask 検索機能などの他の機能強化も、詳しく述べるに値します。SecurityMask は、検索にて指定したオブジェクトに関するセキュリティ記述子情報を返す、高性能なやり方を提供します。

System.DirectoryServices の域を超えて、System.DirectoryServices.ActiveDirectory と System.DirectoryServices.Protocols 名前空間をもっとよく見てみてください。System.DirectoryServices.ActiveDirectory は、システム アドミニストレータが Active Directory を管理するために設計されたクラスにより Active Directory と ADAM へのアクセスを改善しています。たとえば、この名前空間のクラスを、複製、信頼関係そしてスキーマの管理に使うことが出来ます。System.DirectoryServices.Protocols 名前空間は、LDAP 3.0 と DSML 2.0 標準と連携するためのクラスを提供します。ご承知のとおり Microsoft は、ディレクトリへのアクセスの強化と、予想されているとおり、マネージ コードからの Active Directory と ADAM へのアクセスの著しい改善に取り組み続けています。


Ethan Wilansky は、ディレクトリ サービスの最優秀プログラマであり、EDS 社向けの次世代テクノロジおよびアーキテクチャ業務にてチーフ テクノロジストを勤めています。彼は、Microsoft のために 10 冊を超える書籍を執筆、あるいは共著しており、最近では Microsoft Shell (MSH) 言語リファレンスの共著を終えたばかりです。また彼は、Windows IT Pro 誌の寄稿編集者でもあります。


 この記事は、 MSDN マガジン - 2005 年 12 月号からの翻訳です。 .


Back to top