web-dev-qa-db-fra.com

Comment inclure une fonction définie localement lorsque j'utilise Invoke-Command de PowerShell pour la communication à distance?

Je sens que je manque quelque chose qui devrait être évident, mais je ne peux pas comprendre comment faire cela.

J'ai un script ps1 qui a une fonction définie dans celui-ci. Il appelle la fonction et 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 [email protected]

Ce court exemple de script affiche "Bonjour!" puis se bloque en disant "Le terme 'foo' n'est pas reconnu comme le nom d'une applet de commande, d'une fonction, d'un fichier de script ou d'un programme utilisable."

Je comprends que la fonction n'est pas définie sur le serveur distant, car ce n'est pas dans le ScriptBlock. Je pourrais le redéfinir ici, mais je préférerais ne pas le faire. Je voudrais définir la fonction une fois et l'utiliser soit localement ou à distance. Y at-il un bon moyen de le faire?

26
David Hogue

Vous devez transmettre la fonction elle-même (pas un appel à la fonction dans la variable ScriptBlock).

J'avais le même besoin la semaine dernière et j'ai trouvé cette SO discussion

Donc, votre code deviendra:

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

Notez qu'en utilisant cette méthode, vous ne pouvez transmettre des paramètres à votre fonction que par position; vous ne pouvez pas utiliser les paramètres nommés comme vous le pouvez lorsque vous exécutez la fonction localement.

30
alroc

Vous pouvez passer la définition de la fonction en tant que paramètre, puis redéfinir la fonction sur le serveur distant en créant un scriptblock puis en le dotant de source:

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

Cela élimine le besoin d'avoir une copie en double de votre fonction. Vous pouvez également transmettre plus d’une fonction de cette façon, si vous le souhaitez:

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

Vous pouvez également placer les fonctions ainsi que le script dans un fichier (foo.ps1) et le transmettre à Invoke-Command à l'aide du paramètre FilePath:

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

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

4
Keith Hill

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

Assez drôle, la liste de paramètres du scriptblock dans la fonction test ne prend pas d'argument de type [scriptblock] et nécessite par conséquent une 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 possibilité est de passer une table de hachage à notre script qui contient toutes les méthodes que vous aimeriez avoir disponibles dans la session distante: 

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