Windows PowerShell を使用して一対多のリモート処理を使用する

完了

"一対多のリモート処理" を使用すると、1 つのコマンドを複数のコンピューターに並行して送信できます。 各コンピューターでは、送信されたコマンドを実行し、結果を XML にシリアル化して、それらの結果をコンピューターに戻します。 その後、コンピューターは XML をオブジェクトに逆シリアル化し、Windows PowerShell パイプラインに置きます。 これを行うと、各オブジェクトに複数のプロパティが追加されます。これには、各結果の取得元のコンピューターを示す PSComputerName プロパティが含まれます。 このプロパティを使用すると、コンピューター名に基づいて並べ替え、グループ化、フィルター処理を行うことができます。

次の 2 つの異なる手法を使用して、一対多のリモート処理を使用できます。

  • Invoke-Command –ComputerName name1,name2 –ScriptBlock { command }。 この手法では、スクリプト ブロックに含まれる 1 つ以上のコマンドが、リストされたコンピューターに送信されます。 この手法は、1 つまたは 2 つのコマンドを送信する場合に便利です。複数のコマンドはセミコロンで区切られます。
  • Invoke-Command –ComputerName name1,name2 –FilePath filepath。 この手法では、.ps1 ファイル名拡張子を持つスクリプト ファイルの内容が、リストされたコンピューターに送信されます。 ローカル コンピューターがファイルを開き、その内容を読み取ります。 ただし、リモート コンピューターはファイルに直接アクセスする必要はありません。 この手法は、スクリプト全体など、大きいコマンド ファイルを送信する場合に便利です。

Note

どのスクリプト ブロック (–ScriptBlock パラメーターに指定されたスクリプト ブロックを含む) 内でも、セミコロンを使用して複数のコマンドを区切ることができます。 たとえば、{ Get-Service ; Get-Process }Get-Service を実行してから、Get-Process を実行します。

Throttling

ローカル コンピューター上のリソースを管理するために、PowerShell にはコマンドごとに調整する機能が含まれています。これを使用すると、各コマンドに対して確立されている同時リモート接続の数を制限することができます。 既定では、Windows PowerShell は一度に 32 台のコンピューターのみに接続します。 32 台を超えるコンピューターを指定すると、その他のコンピューターとの接続はキューに入れられます。 最初のバッチから一部のコンピューターへのセッションが完了し、その結果が返されると、次のバッチのコンピューターへの接続が開始します。

この動作は、Invoke-Command–ThrottleLimit パラメーターを使用して変更できます。 この数を増やしても、リモート コンピューターに追加の負荷はかかりません。 ただし、Invoke-Command を呼び出したコンピューターに追加の負荷がかかります。 また、利用する帯域幅も増えます。 各同時接続は、基本的に Windows PowerShell のスレッドです。 したがって、コンピューターの数を増やすと、ローカル コンピューター上のメモリとプロセッサ速度が消費されます。

値の受け渡し

スクリプト ブロックまたはファイルの内容は、リテラル テキストとしてリモート コンピューターに送信され、そのまま正確に実行されます。 コンピューターでは、Invoke-Command が実行されたスクリプト ブロックまたはファイルを解析しません。 次のコマンド例について考えてみます。

$var = 'BITS'
Invoke-Command –ScriptBlock { Get-Service –Name $var } –Computer LON-DC1

このシナリオでは、変数 $var は、LON-DC1 で実行するスクリプト ブロックに含めるのではなく、ローカル コンピューターで設定されます。 つまり、$var は PowerShell リモート処理セッションで LON-DC1 に対して定義も設定も行われません。これは、Windows PowerShell を使い慣れていない管理者によくある間違いです。

コマンドをローカルおよびリモートで実行する

リモート コンピューターに渡される、スクリプト ブロックで囲むコマンドには細心の注意を払います。 ローカル コンピューターではスクリプト ブロックの内容は処理されるのではなく、単にリモート コンピューターに渡されるだけであることに注意してください。 たとえば、次のようなコマンドを考えてみます。

Invoke-Command –ScriptBlock { Do-Something –Credential (Get-Credential) } -ComputerName LON-DC1

このコマンドは、リモート コンピューターで Get-Credential コマンドレットを実行します。 ローカル コンピューターで Get-Credential を実行しようとすると、グラフィカル ダイアログ ボックスを使用して資格情報の入力を求められます。

質問: リモート コンピューターで実行する場合、そのコマンドは機能しますか? たとえば、100 台のリモート コンピューターで上記のコマンドを実行した場合、100 個の資格情報の入力を求められますか?

次に、次の変更されたバージョンのコマンドを検討します。

Invoke-Command –ScriptBlock { Param($c) Do-Something –Credential $c }
               -ComputerName LON-DC1
               -ArgumentList (Get-Credential)

このコマンドは、ローカル コンピューターで Get-Credential を実行し、1 回だけ実行します。 その結果生成されるオブジェクトはスクリプト ブロックの $c パラメーターに渡され、各コンピューターで同じ資格情報を使用できるようになります。

これらの例は、リモート処理コマンドを慎重に作成する重要性を示しています。 コマンドをリモートとローカルで組み合わせて実行することで、さまざまな有用な目標を達成できます。

永続化

ここで説明する手法を使用して、Invoke-Command を使用するたびに、リモート コンピューターによって新しい wsmprovhost プロセスが作成され、1 つ以上のコマンドが実行されます。 その後、結果を返し、その Windows PowerShell インスタンスを閉じます。 連続する各 Invoke-Command は、同じコンピューターに対して行われる場合であっても、新しいWindows PowerShell ウィンドウを開くのに似ています。 以前のセッションで行われた作業の内容は、ディスクなどの永続的なストレージに保存しない限り存在しません。 たとえば、次のようなコマンドを考えてみます。

Invoke-Command –ComputerName LON-DC1 –ScriptBlock { $x = 'BITS' }
Invoke-Command –ComputerName LON-DC1 –ScriptBlock { Get-Service –Name $x }

この例では、Get-Service は失敗します。これは、前の wsmprovhost プロセスの一部として作成された変数の値に依存するためです。 Invoke-Command によって呼び出された最初のスクリプトが完了すると、その変数はメモリからクリアされます。 この問題に対処するには、連続するコマンドを正常に送信できるようにリモート コンピューターで wsmprovhost プロセスを作成することができます。

複数のコンピューター名

Invoke-Command–ComputerName パラメーターは、任意の文字列オブジェクトのコレクションをコンピューター名として受け入れることができます。 次の一覧では、このようなコレクションの作成に使用できるさまざまな手法について説明します。

  • -ComputerName ONE,TWO,THREE。 コンピューター名の静的なコンマ区切りリスト。
  • -ComputerName (Get-Content Names.txt)Names.txt という名前のテキスト ファイルから名前を読み取ります。ファイルに 1 行ごとに 1 つのコンピューター名が含まれていることを前提とします。
  • -ComputerName (Import-Csv Computers.csv | Select –ExpandProperty Computer)Computers.csv という名前のコンマ区切り値 (CSV) ファイルを読み取り、コンピューター名を含む Computer という名前の列を含みます。
  • -ComputerName (Get-ADComputer –Filter * | Select –ExpandProperty Name)。 AD DS 内のすべてのコンピューター オブジェクトに対してクエリを実行します。大規模なドメインではかなり時間がかかる場合があります。

コンピューター名の使用時のよくある間違い

コンピューター名を指定する場所に注意してください。 たとえば、次のようなコマンドについて考えます。

Invoke-Command –ScriptBlock { Get-Service –ComputerName ONE,TWO }

このコマンドでは、Invoke-Command–ComputerName パラメーターは指定されません。 したがって、このコマンドはローカル コンピューターで実行されます。 ローカル コンピューターは、ONETWO という名前のコンピューターを対象にして Get-Service を実行します。 Windows PowerShell リモート処理ではなく、Get-Service で使用されるプロトコルが使用されます。 これを次のコマンドと比較します。

Invoke-Command –ScriptBlock { Get-Service } –ComputerName ONE,TWO

このコマンドでは、Windows PowerShell リモート処理を使用して、ONETWO という名前のコンピューターに接続します。 これらの各コンピューターは Get-Service をローカルで実行し、リモート処理を使用して結果を返します。

より対話的な Windows PowerShell リモート処理の状況では、個々のセッションを個別のエンティティとして管理できます。 これを行うには、まず、New-PSSession コマンドを使用してセッションを作成します。 New-PSSession コマンドを使用する利点は、セッションが複数の Invoke-Command インスタンス全体で保持され、スクリプト内の他のコマンドに変数とオブジェクトを渡すことができることです。 永続的なセッションを作成するには、New-PSSession コマンドを使用し、変数に割り当てます。 Invoke-Command コマンドを使用して、後で変数を参照できます。 完了したら、Remove-PSSession コマンドを使用して永続的なセッションを閉じることができます。