Comment puis-je inclure une fonction définie localement lorsque J'utilise la commande Invoke-Command for remoting de PowerShell?

j'ai l'impression de rater quelque chose qui devrait être évident, mais je ne sais pas comment faire.

j'ai un script ps1 qui a une fonction définie. Il appelle la fonction, puis essaie de l'utiliser à distance:

function foo
{
    Param([string]$x)

    Write-Output $x
}

foo "Hi!"

Invoke-Command -ScriptBlock { foo "Bye!" } -ComputerName someserver.example.com -Credential someuser@example.com

ce court exemple de script imprime " Salut!"et puis se bloque en disant: "Le terme 'foo' n'est pas reconnu comme nom d'applet de commande, fonction, fichier de script ou d'un programme exécutable."

I comprendre que la fonction n'est pas définie sur le serveur distant, car il n'est pas dans le ScriptBlock. Je pourrais le redéfinir là - bas, mais je ne préférerais pas. J'aimerais définir la fonction une fois et l'utiliser localement ou à distance. Est-il une bonne façon de le faire?

24
demandé sur David Hogue 2012-07-06 21:58:21

4 réponses

vous devez passer la fonction elle-même (pas un appel à la fonction dans le ScriptBlock ).

j'ai eu le même besoin la semaine dernière et j'ai trouvé cette discussion ainsi

ainsi votre code deviendra:

Invoke-Command -ScriptBlock ${function:foo} -argumentlist "Bye!" -ComputerName someserver.example.com -Credential someuser@example.com

notez qu'en utilisant cette méthode, vous ne pouvez passer les paramètres dans votre fonction qu'en position; vous ne pouvez pas utiliser les paramètres nommés comme vous le pouvez en exécutant la fonction localement.

29
répondu alroc 2017-05-23 12:34:41

vous pouvez passer la définition de la fonction en paramètre, puis redéfinir la fonction sur le serveur distant en créant un scriptblock puis dot-sourcing it:

$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!"
}

Ceci élimine le besoin d'avoir une copie de votre fonction. Vous pouvez également passer plus d'une fonction de cette manière, si vous êtes si incliné:

$allFunctionDefs = "function foo { ${function:foo} }; function bar { ${function:bar} }"
23
répondu JamesQMurphy 2014-05-30 06:44:47

vous pouvez aussi mettre la ou les fonctions ainsi que le script dans un fichier(foo.ps1) et passer que pour invoquer-commande en utilisant le paramètre FilePath:

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

Le fichier sera copié sur les ordinateurs distants et exécuté.

4
répondu Keith Hill 2015-07-01 01:12:10

bien que ce soit une vieille question, je voudrais ajouter ma solution.

assez drôle la liste param du scriptblock dans le test de fonction, ne prend pas un argument de type [scriptblock] et a donc besoin de conversion.

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

une autre posibilité est de passer un hashtable à notre scriptblock contenant toutes les méthodes que vous aimeriez avoir disponibles dans la session à distance:

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
répondu Matze 2018-04-11 07:38:19