ITのプロフェッショナルは、ほとんどの場合、ローカルコンピューターだけで作業することはありません。PowerShellのInvoke-Commandコマンドレットを使用すると、その必要はありません!このコマンドレットを使用すると、ローカルコンピューターで作業しているかのようにコードをシームレスに書くことができます。
PowerShellのリモート機能を使用することで、Invoke-Commandコマンドレットは一般的に使用されるPowerShellコマンドレットであり、ユーザーはPSSession内でコードを実行することができます。このPSSessionは、以前にNew-PSSessionコマンドレットで作成されたものであるか、一時的なセッションを迅速に作成して解除することもできます。
関連記事: PowerShellリモート操作:究極のガイド
Invoke-CommandをPowerShellのpsexecと考えてみてください。実装方法は異なりますが、コンセプトは同じです。少しのコードやコマンドを取り、それをリモートコンピューター上で「ローカル」に実行します。
ただし、Invoke-Commandを使用するには、リモートコンピューターでPowerShellリモートが有効になっている必要があります。デフォルトでは、すべてのWindows Server 2012 R2以降のマシンには、それと適切なファイアウォールの例外が有効になっています。Server 2008のマシンを使用している場合は、複数の方法でリモート操作を設定することができますが、簡単な方法は、リモートマシンで「winrm quickconfig」または「Enable-PSRemoting」を実行することです。
「Invoke-Command」が「ad-hocコマンド」と一緒に動作する方法を示すために、リモートのWindows Server 2012 R2以降のドメイン参加コンピューターがあるとしましょう。ワークグループのコンピューターで作業する場合は少し複雑になります。 PowerShellコンソールを開き、「Invoke-Command」と入力してエンターキーを押します。
I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.
スクリプトブロック内のコードがリモートコンピューターで実行されることを証明するために、「hostname」コマンドを実行してみましょう。このコマンドは、実行されているコンピューターのホスト名を返します。ローカルコンピューターで「hostname」を実行すると、その名前が表示されます。
次に、同じコードを含むスクリプトブロックを「Invoke-Command」に渡してみましょう。ただし、忘れている必要なパラメーターがあります:「ComputerName」です。「Invoke-Command」に、このコマンドを実行するリモートコンピューターを指定する必要があります。
hostname
の出力が、リモートコンピューターの名前WEBSRV1
になっていることに注意してください。あなたはWEBSRV1でコードを実行しました。スクリプトブロック内に単純なコードを実行し、単一のリモートマシンに渡すことは、Invoke-Command
の最も簡単な応用ですが、それ以上のこともできます。
ローカル変数をリモートスクリプトブロックに渡す
スクリプト内には、Invoke-Commandの参照は1つだけではありません。スクリプトはおそらく数十行にわたり、変数が定義されたり、モジュールで関数が定義されたりするでしょう。ただいくつかのコードを中括弧で括るだけのように見えるかもしれませんが、実際にはそのコードが実行されるスコープ全体が変わってしまいます。なぜなら、そのコードはリモートコンピュータに送信されるからです。リモートコンピュータは、スクリプトブロック内のローカルのコードに関して、スクリプト内に含まれるもの以外の情報を持っていません。
例えば、コンピュータ名とファイルパスのパラメータを持つ関数があるかもしれません。この関数の目的は、リモートコンピュータ上でいくつかのソフトウェアインストーラを実行することです。リモートコンピュータ上にすでに存在する「ローカル」ファイルパスをインストーラに渡すことができます。
以下の関数は合理的なように見えますね。実行してみましょう。
しかし、エラーメッセージが表示され、エラーが発生します。アンパサンド演算子の使用によるものです。コード自体は間違っていませんが、関数のパラメータで値を渡したにもかかわらず、$InstallerFilePath
が空であったため失敗しました。アンパサンドをWrite-Host
と置き換えてテストできます。
新しい関数の行: Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Installer path is: $InstallerFilePath" }
$InstallerFilePath
の値が空です。変数はリモートマシンに渡されなかったため、展開されていません。ローカルで定義された変数をリモートのスクリプトブロックに渡すには、2つのオプションがあります。スクリプトブロック内で変数名の前に$using:
を付けるか、Invoke-Command
のArgumentList
パラメーターを使用することができます。両方を見てみましょう。
ArgumentListパラメーター
ローカル変数をリモートのスクリプトブロックに渡す方法の1つは、Invoke-Command
のArgumentList
パラメーターを使用することです。このパラメーターを使用すると、ローカル変数をパラメーターに渡し、スクリプトブロック内のローカル変数の参照をプレースホルダーで置き換えることができます。
ArgumentList
パラメーターにローカル変数を渡すのは簡単です。
問題になるのは、スクリプトブロック内の変数の構造です。 { & $InstallerPath }
の代わりに、{ & $args[0] }
または{param($foo) & $foo }
に変更する必要があります。どちらを使用すべきでしょうか?
ArgumentList
パラメーターはオブジェクトのコレクションです。オブジェクトのコレクションを使用すると、一度に1つ以上のオブジェクトを渡すことができます。この場合、私は単に1つのオブジェクトを渡しています。
実行されると、Invoke-Commandコマンドレットはそのコレクションを取得し、それをスクリプトブロックに注入して、実質的に$args
という配列に変換します。ここで、$args -eq ArgumentList
であることを覚えておいてください。この時点で、配列と同様にコレクションの各インデックスを参照します。私たちの場合、コレクションには1つの要素($InstallerFilePath
)しかありませんでしたが、これは$args[0]
、つまりそのコレクションの最初のインデックスを意味します。ただし、もしそれ以上の要素がある場合は、$args[1]
、$args[2]
などで参照します。
さらに、スクリプトブロックの変数により良い変数名を割り当てる場合は、関数のようにスクリプトブロックにパラメータを追加することもできます。結局のところ、スクリプトブロックは匿名関数です。スクリプトブロックのパラメータを作成するには、paramブロックを作成し、パラメータの名前を指定します。作成した後、スクリプトブロック内でそのパラメータを以下のように参照します。
この場合、ArgumentList
コレクションの要素は、定義されたパラメータに順番に「マッピング」されます。パラメータ名は重要ではありません。重要なのは順序です。Invoke-Command
は、ArgumentList
コレクションの最初の要素を取り、最初のパラメータを検索してその値をマッピングし、同様に2番目、3番目などを行います。
$Using構文
$using
構文は、ローカル変数をリモートのスクリプトブロックに渡すためのもう一つの一般的な方法です。この構文では、既存のローカル変数を再利用することができますが、変数名の前に$using:
を付けるだけです。 $args
コレクションを心配する必要はありませんし、パラメータブロックを追加する必要もありません。
PowerShellの$using
構文は非常にシンプルですが、Pesterを学ぶと、ArgumentList
が便利なことがわかるでしょう。
Invoke-CommandとNew-PSSession
技術的には、この投稿はInvoke-Commandに関するものですが、その有用性を示すために、New-PSSession
コマンドにも簡単に触れる必要があります。以前に、Invoke-Command
は「アドホック」コマンドまたは既存のセッションを使用できると述べました。
この投稿では、リモートコンピュータで単発のコマンドを実行していました。新しいセッションを立ち上げ、コードを実行し、セッションを終了させていました。これは一度限りの状況では問題ありませんが、同じコンピュータで何十ものコマンドを実行する場合にはあまり適していません。この場合、事前にNew-PSSession
を使用して既存のPSSessionを再利用する方が良いです。
コマンドを実行する前に、New-PSSession
でPSSessionを作成する必要があります。これは、単純に$session = New-PSSession -ComputerName WEBSRV1
を実行することで行うことができます。これにより、サーバー上でリモートセッションが作成され、ローカルマシン上でそのセッションへの参照が作成されます。この時点で、ComputerName
の参照をSession
に置き換え、Session
を保存された$session
変数に指定します。
実行すると、セッションが既に構築されているため、パフォーマンスが向上することに気付くでしょう。ただし、完了したら、Remove-PSSession
でオープンセッションを削除することが重要です。
概要
Invoke-Command PowerShellのcmdletは、最も一般的で強力なcmdletの1つです。私自身、ほとんどのコマンドの中で最もよく使用するものです。使いやすさとリモートコンピュータで任意のコードを実行できる能力は非常に強力であり、このコマンドを上から下まで学ぶことをお勧めします!