[ADSI] スキーマキャッシュのトラブルシュート

ご無沙汰しております。ぴろとでございます。先日、Provision された Person Object (おそらく娘) が、Pending Export の状態で、予定日を迎えましたが、まだ Export されていません。母子共に健康であればいいのですが、不安な毎日を過ごしております。

前回の記事はこちらになります。https://blogs.technet.com/jpilmblg/archive/2009/11/27/SchemaCashe.aspx

- エラー番号の確認

A さんは、発生したエラー番号(0x8000500c)について、”bing” を使用して、確認を実施したところ、サポート技術情報を発見しました。

ASP.NET で System.DirectoryServices 名前空間を使用する方法
https://support.microsoft.com/kb/329986/ja

===== 抜粋 =====
ADSI スキーマ キャッシュが使用できない場合に表示される可能性のあるエラー

サーバーを再起動した後、Web アプリケーションが応答を停止し、次のエラー メッセージが表示されることがあります。

0x8000500C, "The property in cache cannot be converted from native datatype"

ADSI スキーマ キャッシュのトラブルシューティング

前に記載されているエラーが表示される場合、ADSI で Active Directory スキーマが正常にキャッシュされていません。 レジストリ キーが作成されているかどうか、また、他の資料に記載されているようにファイルに書き込みが行われているかどうかを確認する方法および関連情報を参照するには、以下の「サポート技術情報」 (Microsoft Knowledge Base) をクリックしてください。

251189 (https://support.microsoft.com/kb/251189/ ) [INFO] ADSI によってキャッシュされた LDAP サーバー スキーマの特定

==========

上記のサポート技術情報から、0x8000500c は、キャッシュ内のプロパティが、ネイティブデータタイプから、変換できない事象が発生していることと、スキーマ キャッシュがキャッシュできていない可能性が高いことがわかりました。スキーマ キャッシュが正しく行われれば、データタイプ変換も成功するのではないかという観点から、スクリプトの見直しを実施しました。

- スクリプトの見直し

前回のトラブルシュートをはじめるにあたり、確認した内容を元に、スクリプト・環境の見直しを実施しました。

===== 前回の復習 =====

1) 環境の確認
クライアント : ワークグループ環境の Windows Server 2003 SP2
サーバー : Windows Server 2003 Active Directory

2) 使っているスクリプト

スクリプトは、パスワードの有効期限の設定を読み取るためのものです。

Const ONE_HUNDRED_NANOSECOND = .000000100   ' .000000100 は 10^-7 と等価です
Const SECONDS_IN_DAY = 86400
Set objDomain=GetObject("LDAP://jpdsilm-dc-01/DC=jpdsilm,DC=extest,DC=microsoft,DC=com")
Set objMaxPwdAge = objDomain.Get("maxPwdAge")
If objMaxPwdAge.LowPart = 0 Then
   WScript.Echo "パスワードの最長有効期間がドメイン内で 0 に設定されています。" & _
                "したがって、パスワードに有効期限は設定されていません。"
  WScript.Quit
Else
   dblMaxPwdNano = Abs(objMaxPwdAge.HighPart * 2^32 + objMaxPwdAge.LowPart)
  dblMaxPwdSecs = dblMaxPwdNano * ONE_HUNDRED_NANOSECOND
  dblMaxPwdDays = Int(dblMaxPwdSecs / SECONDS_IN_DAY)   
  WScript.Echo "パスワードの最長有効期間は " & dblMaxPwdDays & " 日です。"
End If

===== 前回の復習ここまで =====

スクリプトの見直しを実施していたところ、GetObject にて、LDAP パスを指定していることがわかりました。ここで、GetObject 関数を行ったときのどの ADSI 関数が呼ばれるかを確認が必要だと感じました。

ADsGetObject Function
https://msdn.microsoft.com/en-us/library/aa772184.aspx

Remarks の項目を確認し、GetObject 関数が呼ばれた場合に、ADsGetObject が呼ばれることを確認しました。この場合の動作は、呼び出し元のスレッドのセキュリティコンテキストを用いて認証を行い、認証失敗時には、Anonymous でバインドが行われる記載があります。上記確認点より、ドメインにログインした状態で実施した場合とワークグループからのログインした状態での動作に違いがあり、ドメインにログインして実行したスクリプトは、そのドメインユーザーのセキュリティコンテキストが使用されるが、ワークグループの場合に認証に失敗した後に Anonymous にて認証される流れであることがわかりました。

そのため、A さんは、スクリプトの修正を行うことにしました。

===== 修正後のスクリプト =====

Const ADS_SECURE_AUTHENTICATION = 1
Const ONE_HUNDRED_NANOSECOND = .000000100   ' .000000100 は 10^-7 と等価です
Const SECONDS_IN_DAY = 86400
Set objDSO=GetObject("LDAP:")
Set objDomain=objDSO.OpenDSObject("LDAP://jpdsilm-dc-01/DC=jpdsilm,DC=extest,DC=microsoft,DC=com",”Domain\UserName”,”Password”, _
ADS_SECURE_AUTHENTICATION)
Set objMaxPwdAge = objDomain.Get("maxPwdAge")
If objMaxPwdAge.LowPart = 0 Then
   WScript.Echo "パスワードの最長有効期間がドメイン内で 0 に設定されています。" & _
                "したがって、パスワードに有効期限は設定されていません。"
  WScript.Quit
Else
   dblMaxPwdNano = Abs(objMaxPwdAge.HighPart * 2^32 + objMaxPwdAge.LowPart)
  dblMaxPwdSecs = dblMaxPwdNano * ONE_HUNDRED_NANOSECOND
  dblMaxPwdDays = Int(dblMaxPwdSecs / SECONDS_IN_DAY)   
  WScript.Echo "パスワードの最長有効期間は " & dblMaxPwdDays & " 日です。"
End If

=======================

また、スキーマキャッシュの所在について気になった A さんは、合わせてスキーマ キャッシュ破損についても気になっていました。また、Bing で調べたところ、以下のサポート技術情報を発見しました。

INFO: Locating an LDAP Server Schema Cached by ADSI
https://support.microsoft.com/kb/251189/en-us

A さんは、スキーマキャッシュはレジストリとファイルにて管理されていて、タイムスタンプによる管理が行われていることをがわかりました。
そのため、レジストリ(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ADs\Providers\LDAP\distinguished name of the schema container) と、その先がさしているファイルを削除を実施しました。

上記対処を実施後、無事 MaxPwdAge を取得することに成功しました!!めでたしめでたし!