web-dev-qa-db-ja.com

リモート処理にPowerShellのInvoke-Commandを使用する場合、ローカルで定義された関数を含めるにはどうすればよいですか?

明らかなはずの何かを見逃しているように感じますが、どうすればよいかわかりません。

関数が定義されているps1スクリプトがあります。関数を呼び出し、リモートで使用を試みます。

function foo
{
    Param([string]$x)

    Write-Output $x
}

foo "Hi!"

Invoke-Command -ScriptBlock { foo "Bye!" } -ComputerName someserver.example.com -Credential [email protected]

この短いスクリプト例は「Hi!」を出力しますそして、「「foo」という用語は、コマンドレット、関数、スクリプトファイル、または操作可能なプログラムの名前として認識されません」とクラッシュします。

ScriptBlockにないため、関数がリモートサーバーで定義されていないことを理解しています。そこで再定義できましたが、そうではありません。関数を一度定義して、ローカルまたはリモートで使用したいと思います。これを行う良い方法はありますか?

29
David Hogue

関数自体を渡す必要があります(ScriptBlockの関数の呼び出しではありません)。

先週と同じニーズがあり、 this SO Discussion

したがって、コードは次のようになります。

Invoke-Command -ScriptBlock ${function:foo} -argumentlist "Bye!" -ComputerName someserver.example.com -Credential [email protected]

このメソッドを使用すると、関数に位置的にのみパラメーターを渡すことができます。関数をローカルで実行するときのように、名前付きパラメーターを使用することはできません。

34
alroc

関数の定義をパラメーターとして渡し、スクリプトブロックを作成してからドットソースを取得することで、リモートサーバーで関数を再定義できます。

$fooDef = "function foo { ${function:foo} }"

Invoke-Command -ArgumentList $fooDef -ComputerName someserver.example.com -ScriptBlock {
    Param( $fooDef )

    . ([ScriptBlock]::Create($fooDef))

    Write-Host "You can call the function as often as you like:"
    foo "Bye"
    foo "Adieu!"
}

これにより、関数のコピーを複製する必要がなくなります。また、この方法で複数の関数を渡すこともできます。

$allFunctionDefs = "function foo { ${function:foo} }; function bar { ${function:bar} }"
26
JamesQMurphy

また、関数とスクリプトをファイル(foo.ps1)に入れ、FilePathパラメーターを使用してInvoke-Commandに渡すこともできます。

Invoke-Command –ComputerName server –FilePath .\foo.ps1

ファイルがリモートコンピューターにコピーされ、実行されます。

5
Keith Hill

それは古い質問ですが、解決策を追加したいと思います。

面白いことに、関数test内のスクリプトブロックのパラメーターリストは、[scriptblock]型の引数をとらないため、変換が必要です。

Function Write-Log 
{
    param(
        [string]$Message
    )

    Write-Host -ForegroundColor Yellow "$($env:computername): $Message"
}

Function Test
{
    $sb = {
        param(
            [String]$FunctionCall
        )

        [Scriptblock]$WriteLog = [Scriptblock]::Create($FunctionCall) 
        $WriteLog.Invoke("There goes my message...")               
    }

    # Get function stack and convert to type scriptblock 
    [scriptblock]$writelog = (Get-Item "Function:Write-Log").ScriptBlock 

    # Invoke command and pass function in scriptblock form as argument 
    Invoke-Command -ComputerName SomeHost -ScriptBlock $sb -ArgumentList $writelog
}

Test

もう1つの可能性は、リモートセッションで使用できるようにするすべてのメソッドを含むハッシュテーブルをスクリプトブロックに渡すことです。

Function Build-FunctionStack 
{
    param([ref]$dict, [string]$FunctionName)

    ($dict.Value).Add((Get-Item "Function:${FunctionName}").Name, (Get-Item "Function:${FunctionName}").Scriptblock)
}

Function MyFunctionA 
{
    param([string]$SomeValue)

    Write-Host $SomeValue
}

Function MyFunctionB
{
    param([int]$Foo)

    Write-Host $Foo
}

$functionStack = @{}

Build-FunctionStack -dict ([ref]$functionStack) -FunctionName "MyFunctionA"
Build-FunctionStack -dict ([ref]$functionStack) -FunctionName "MyFunctionB" 

Function ExecuteSomethingRemote
{
    $sb = {
        param([Hashtable]$FunctionStack)

        ([Scriptblock]::Create($functionStack["MyFunctionA"])).Invoke("Here goes my message");
        ([Scriptblock]::Create($functionStack["MyFunctionB"])).Invoke(1234);

    }

    Invoke-Command -ComputerName SomeHost -ScriptBlock $sb -ArgumentList $functionStack
}

ExecuteSomethingRemote
0
Matze