[PowerShell]Get-AD* 系コマンドレットの検索クエリは既定値 30 分 でタイムアップする(MaxEnumContextExpiration)

皆さんごきげんよう。ういこです。最近 PowerShell の案件も微妙に増えております。SEO 対策をあざとく狙い、今回も逝ってみましょう PowerShell! 今日のお題は、「Active Directory 系コマンドレットは 30 分でタイムアップすることがある」です。

【今日のお題】 Active Directory 系コマンドレットは 30 分でタイムアップすることがある (対象 OS とサービス : Windows Server 2008 R2 / Active Directory Web Service)

さて、「夏を待てない」と歌ったのは國生さゆりさん (おニャン子クラブ会員番号は 8 番) ですが、コマンドレット君も、あみんのようにいつまでも待てないことがあるのです。
Active Directory といっても、運用形態は様々。オブジェクト数も数個から数万ということもありえます。たとえば数万のオブジェクトを動かしたり、取得したりするとき、いつまでも時間がかかるような事態はパフォーマンスの観点など、まあ色々よろしくありません。というわけで、既定では get-ad 系コマンドレットは、検索クエリのオープン時間に対し、30 分の制限値を持ちます。この制限値は Active Directory Web サービスの Config ファイルのパラメーターで設定されています。

image

1. 30 分以上のクエリ要求を行うと SocketException やらいろいろな例外が発生することがある

なかなかいやらしいことに、30 分以上経過したときの PowerShell コンソール上に出てくる例外、エラーの発生パターンは一定ではありません。ADException が返る場合も、構文が無効とか、SocketException が出るときとかもございます。ただ、例外の出方に惑わされてはいけません。この問題のポイントは、「30 分以内に終われば何事もない、ところが 30 分を少しでも過ぎると何か、異常終了しちゃう」というところです。 これが満たされている場合は、今回ご紹介する問題に合致する可能性がかなり高いです。

・これまで確認されている PowerShell 上のエラーメッセージの出方(これ以外にもあるかもしれません)

サーバーから、無効な列挙コンテキストのエラーが返されまし。,Microsoft.ActiveDirectory.Management.Commands.GetADComputer Category   : NotSpecified Activity   : Get-ADComputer Reason     : ADException TargetName : TargetType :

(2) RuntimeException Number of exceptions of this type:        1 Exception type: System.Management.Automation.RuntimeException Message: null 値の式ではメソッド InnerException: <none> StackTrace (generated):     SP               IP               Function     000000001EC2DAC0 000007FEE9C5FE25 System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(System.Object, System.Collections.Hashtable, Boolean)     000000001EC2DB90 000007FEE9C69D96 System.Management.Automation.PipelineNode.Execute(System.Array, System.Management.Automation.Internal.Pipe, System.Collections.ArrayList ByRef, System.Management.Automation.ExecutionContext)     000000001EC2DC50 000007FEE9C69A0F System.Management.Automation.StatementListNode.ExecuteStatement(System.Management.Automation.ParseTreeNode, System.Array, System.Management.Automation.Internal.Pipe, System.Collections.ArrayList ByRef, System.Management.Automation.ExecutionContext)

・ ちょっとダンプでも見てみましょうか
気が向いたら、おダンプでも一発取ってみてみると楽しいかもしれません。Windows Server 2008 R2 だからタスク マネージャ上で指先一つでダンプさ! ができるんです。ああ便利。
Powershell コンソール上で実行し、エラーが出ている状態で、PowerShell.exe に対してダンプを取ります。これで OK です。

image

ダンプを採ったら、Windbg で開いてみましょう。psscor2.dll でもダウンロードしていただき、!dae コマンドを実行すると、ほらこんなに例外が。ただし、ロードとかするのにコストがかかる StackOverflowException とか、OutOfMemoryException はあらかじめロードされており、これが !dae で見えるからと言って、必ずしも発生しているわけではありません。あっ OOM 出てる!と思う前に、そういう処理かどうかを状況から見極めて判断してください。

- PSSCOR2 の入手先
Psscor2 Managed-Code Debugging Extension for WinDbg
https://www.microsoft.com/download/en/details.aspx?displayLang=en&id=1073

- PSSCOR2 で使えるコマンドを紹介してくださってる Jin さんのブログです。
psscor2
https://blogs.msdn.com/b/jink/archive/2010/04/01/psscor2.aspx

Windbg のセットアップ、簡単な使い方は以下で紹介させていただいています。ご参考になれば幸いです。

[Debugging] Windbg を使ってご機嫌ナナメな彼女の心を激しくデバッグ!(1)
https://blogs.technet.com/jpilmblg/archive/2009/02/21/debugging-windbg-1.aspx

[Debugging] Windbg を使ってご機嫌ナナメな彼女の心を激しくデバッグ!(2) Ver 1.1
https://blogs.technet.com/jpilmblg/archive/2009/02/25/debugging-windbg-2.aspx

[Debugging] Windbg を使ってご機嫌ナナメな彼女の心を激しくデバッグ!(3)
https://blogs.technet.com/jpilmblg/archive/2009/03/06/debugging-windbg-3-tips.aspx

・スタック上に残っている例外のパターンのサンプル
ダンプを取って例外を見てみると、「タイムアウトで切りました」「リモート ホストが応答しませんでした」というメッセージが見られます。PowerShell コンソール上でも、これらの例外が通知される場合もあります。

image

Number of exceptions of this type:        1 Exception MethodTable: 000007fee6d7fd58 Exception object: 0000000003538348 Exception type: System.ServiceModel.CommunicationException Message: ソケット接続が中止されました。これは、メッセージ処理時のエラー、リモート ホストでの受信タイムアウトの超過、または基になるネットワーク リソースの問題が原因で発生する可能性があります。ローカル ソケットのタイムアウトは '00:01:00' でした。 InnerException: System.Net.Sockets.SocketException, use !PrintException 00000000035061a0 to see more StackTrace (generated):     SP               IP               Function     000000001C1EC690 000007FEE7A4B60C System.ServiceModel.Channels.SocketConnection.ReadCore(Byte[], Int32, Int32, System.TimeSpan, Boolean)     000000001C1EE770 000007FEE6B43D75 System.ServiceModel.Channels.SocketConnection.Close(System.TimeSpan)     000000001C1EE840 000007FEE6B46969 System.ServiceModel.Channels.BufferedConnection.Close(System.TimeSpan)     000000001C1EE8A0 000007FEE6B44DBE System.ServiceModel.Channels.ConnectionPool.CloseItem(System.ServiceModel.Channels.IConnection, System.TimeSpan)     000000001C1EE8D0 000007FEE73A93BF System.ServiceModel.Channels.CommunicationPool`2+EndpointConnectionPool[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].CloseItem(System.__Canon, System.TimeSpan)     000000001C1EE900 000007FEE6C126DE System.ServiceModel.Channels.IdlingCommunicationPool`2+IdleTimeoutEndpointConnectionPool[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].CloseItem(System.__Canon, System.TimeSpan)     000000001C1EE940 000007FEE73A9562 System.ServiceModel.Channels.CommunicationPool`2+EndpointConnectionPool[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].CloseIdleConnection(System.__Canon, System.TimeSpan)

StackTraceString: <none> HResult: 80131501

Number of exceptions of this type:        1 Exception MethodTable: 000007fee7f9b090 Exception object: 00000000033f3ad8 Exception type: Microsoft.ActiveDirectory.Management.ADException Message: サーバーから、無効な列挙コンテキストのエラーが返されました。 InnerException: System.ServiceModel.FaultException, use !PrintException 00000000033f2590 to see more StackTrace (generated):     SP               IP               Function     000000001EC2C7C0 000007FEE8012473 Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowException(Microsoft.ActiveDirectory.Management.Faults.AdwsFault, System.ServiceModel.FaultException)     000000001EC2C810 000007FEE7E9C8F9 Microsoft.ActiveDirectory.Management.AdwsConnection.Search(Microsoft.ActiveDirectory.Management.ADSearchRequest)     000000001EC2C9A0 000007FEE7E9572B Microsoft.ActiveDirectory.Management.ADWebServiceStoreAccess.Microsoft.ActiveDirectory.Management.IADSyncOperations.Search(Microsoft.ActiveDirectory.Management.ADSessionHandle, Microsoft.ActiveDirectory.Management.ADSearchRequest)     000000001EC2C9E0 000007FEE7EA39DE Microsoft.ActiveDirectory.Management.ADObjectSearcher.PagedSearch(System.Object ByRef, Boolean ByRef, Int32, Int32)     000000001EC2CAC0 000007FEE7EB6917 Microsoft.ActiveDirectory.Management.ADObjectSearchResultEnumerator.System.Collections.IEnumerator.MoveNext()     000000001EC2CB20 000007FEE7ECA9EF Microsoft.ActiveDirectory.Management.Commands.ADFactory`1+<GetExtendedObjectFromFilter>d__0[[System.__Canon, mscorlib]].MoveNext()     000000001EC2CBA0 000007FEE7EF5D03 Microsoft.ActiveDirectory.Management.Commands.ADGetCmdletBase`3[[System.__Canon, mscorlib],[System.__Canon, mscorlib],[System.__Canon, mscorlib]].OutputSearchResults(Microsoft.ActiveDirectory.Management.IADOPathNode)     000000001EC2CC60 000007FEE7EF650F Microsoft.ActiveDirectory.Management.Commands.ADGetCmdletBase`3[[System.__Canon, mscorlib],[System.__Canon, mscorlib],[System.__Canon, mscorlib]].BeginProcessingOverride()     000000001EC2CCF0 000007FEE7EF3D41 Microsoft.ActiveDirectory.Management.Commands.ADCmdletBase.BeginProcessing()

StackTraceString: <none> HResult: 80131500

Number of exceptions of this type:        1 Exception MethodTable: 000007fee9d1e810 Exception object: 00000000033f3f50 Exception type: System.Management.Automation.CmdletInvocationException Message: サーバーから、無効な列挙コンテキストのエラーが返されました。 InnerException: Microsoft.ActiveDirectory.Management.ADException, use !PrintException 00000000033f3ad8 to see more StackTrace (generated):     SP               IP               Function     000000001EC2D350 000007FEE9C5FE25 System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(System.Object, System.Collections.Hashtable, Boolean)     000000001EC2D420 000007FEE9C69D96 System.Management.Automation.PipelineNode.Execute(System.Array, System.Management.Automation.Internal.Pipe, System.Collections.ArrayList ByRef, System.Management.Automation.ExecutionContext)     000000001EC2D4E0 000007FEE9C69747 System.Management.Automation.ParseTreeNode.Execute(System.Array, System.Management.Automation.Internal.Pipe, System.Management.Automation.ExecutionContext)     000000001EC2D530 000007FEEA3A824A System.Management.Automation.AssignmentStatementNode.Execute(System.Array, System.Management.Automation.Internal.Pipe, System.Management.Automation.ExecutionContext)     000000001EC2D620 000007FEE9C699F2 System.Management.Automation.StatementListNode.ExecuteStatement(System.Management.Automation.ParseTreeNode, System.Array, System.Management.Automation.Internal.Pipe, System.Collections.ArrayList ByRef, System.Management.Automation.ExecutionContext)

StackTraceString: <none> HResult: 80131501

Number of exceptions of this type:        2 Exception MethodTable: 000007fef2b475c0 Exception object: 0000000003539400 Exception type: System.TimeoutException Message: ソケット転送が 00:01:00 後にタイムアウトしました。バインドに設定されているタイムアウトを超えました。この操作に割り当てられた時間は、より長いタイムアウト時間の一部であった可能性があります。 InnerException: System.Net.Sockets.SocketException, use !PrintException 0000000003538948 to see more StackTrace (generated):     SP               IP               Function     000000001C1EC690 000007FEE7A4B60C System.ServiceModel.Channels.SocketConnection.ReadCore(Byte[], Int32, Int32, System.TimeSpan, Boolean)     000000001C1EE770 000007FEE6B43D75 System.ServiceModel.Channels.SocketConnection.Close(System.TimeSpan)

StackTraceString: <none> HResult: 80131505

Number of exceptions of this type:        2 Exception MethodTable: 000007fef0f99848 Exception object: 00000000035061a0 Exception type: System.Net.Sockets.SocketException Message: 既存の接続はリモート ホストに強制的に切断されました。 InnerException: <none> StackTrace (generated):     SP               IP               Function     000000001C1EE650 000007FEF0ECE486 System.Net.Sockets.Socket.Receive(Byte[], Int32, Int32, System.Net.Sockets.SocketFlags)     000000001C1EE6A0 000007FEE6B54C9D System.ServiceModel.Channels.SocketConnection.ReadCore(Byte[], Int32, Int32, System.TimeSpan, Boolean)

StackTraceString: <none> HResult: 80004005

2. 既定値ってどのくらい?

さて、ではこの現象の要因となる 「私これだけ待つことができます」値 (私の心の中では『あみん値』と呼称しているのは内緒ですが…なんで「あみん」なの? というヤングマンはお母さんに聞いてみてください。) は以下に記載がある、MaxEnumContextExpiration です。この値は [%Windir%\ADWS\](%Windir%\ADWS\) フォルダ配下にある、Microsoft.ActiveDirectory.WebServices.exe.config で設定されています。

image

image

3. 参考資料

What's New in AD DS: Active Directory Web Services
Updated: January 9, 2009
Applies To: Windows Server 2008 R2
https://msdn.microsoft.com/en-us/library/dd391908(v=ws.10).aspx

MaxEnumContextExpiration 30 minutes Specifies the maximum allowed time period during which the ADWS service processes and retrieves the results of a query request from a client computer. Caution Changing the default value of this parameter is strongly discouraged. Most of the search results are returned within 30 minutes.

ご注意ください : 期間が長いクエリを実行していて、この現象に合致するかな?と思われる場合は試しにちょっとだけ伸ばしてみるのもありですが、基本的に上記の "Caution" にあるように、既定値を変えることはおすすめできません。やはり、30 分以上検索コンテキストをオープンにしておくのはあまりよろしくないですし、確認もしくは一時的にどうしても、大容量データを取得する必要がある場合などにちょっと変えるくらいがよいでしょう。あくまでも暫定回避です。お勧めの回避方法としては、やっぱりリトライになります。
なお、上記 MSDN の日本語訳があるのですが、全く逆の内容になっています。(2011/06/28 現在) 基本的に、原文も併せて読むことをお勧めいたします。

日本語訳
https://technet.microsoft.com/ja-jp/library/dd391908(WS.10).aspx

×
注意 
このパラメーターの既定値は変更することをお勧めします。

注意 
このパラメーターの既定値を変更することは推奨いたしません。

原文

image

日本語訳

image

4. まとめ

・get-AD 系コマンドレットの検索クエリがオープン可能な時間は既定で 30 分、MaxEnumContextExpiration で設定 ・MaxEnumContextExpiration は %Windir%\ADWS\ フォルダ直下にある ・30 分以上クエリに時間を要すると発生する例外のパターンは一意ではないが、必ず 30 分が現象発生有無の分水嶺になる ・例外が発生した状態の PowerShell コンソールをタスク マネージャでダンプを取り、psscor2.dll の !dae コマンドで見ると多数の Socket 系例外や、AD のクエリ関連のタイムアウトの情報が出ている ・MaxEnumContextExpiration の値を書き換えるのはお勧めできない。が、日本語訳のページではおすすめしている。正解は「(MaxEnumContextExpiration の) 既定値を書き換えることはおすすめいたしません」

それでは皆様ごきげんよう。

ういこう@ヴァン・アレン帯デーキッス★