Les professionnels de l’informatique travaillent rarement uniquement sur leur ordinateur local. Grâce à la commande Invoke-Command de PowerShell, ce n’est plus nécessaire ! Cette commande nous permet d’écrire du code de manière transparente comme si nous travaillions sur notre ordinateur local.
En utilisant la fonctionnalité de PowerShell Remoting, la commande Invoke-Command est une commande PowerShell couramment utilisée qui permet à l’utilisateur d’exécuter du code à l’intérieur d’une session PSSession. Cette PSSession peut être créée préalablement avec la commande New-PSSession ou elle peut également créer et détruire rapidement une session temporaire.
Articles connexes : PowerShell Remoting : Le guide ultime
Pensez à Invoke-Command comme au psexec de PowerShell. Bien qu’ils soient implémentés différemment, le concept est le même. Prenez un peu de code ou de commande et exécutez-le « localement » sur l’ordinateur distant.
Cependant, pour que Invoke-Command fonctionne, vous devez avoir l’accès à distance PowerShell activé et disponible sur l’ordinateur distant. Par défaut, toutes les machines Windows Server 2012 R2 ou ultérieures l’ont activé, ainsi que les exceptions appropriées du pare-feu. Si vous avez encore des machines Server 2008 malheureusement, il existe plusieurs façons de configurer l’accès à distance, mais une méthode simple consiste à exécuter la commande « winrm quickconfig » ou « Enable-PSRemoting » sur la machine distante.
Pour démontrer comment Invoke-Command fonctionne avec une « commande ad hoc », c’est-à-dire une commande qui ne nécessite pas de créer une nouvelle PSSession, disons que vous avez un ordinateur distant appartenant au domaine, sous Windows Server 2012 R2 ou une version ultérieure. Les choses se compliquent un peu lorsqu’on travaille sur des ordinateurs en groupe de travail. J’ouvrirai ma console PowerShell, taperai Invoke-Command
et appuierai sur Entrée.
I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.
Pour prouver que le code à l’intérieur du bloc de script est exécuté sur l’ordinateur distant, exécutons simplement la commande hostname
. Cette commande renverra le nom d’hôte de l’ordinateur sur lequel elle est exécutée. Exécuter hostname
sur mon ordinateur local donne son nom.
Passons maintenant un bloc de script contenant ce même code à Invoke-Command
. Avant cela, cependant, nous oublions un paramètre requis : ComputerName
. Nous devons dire à Invoke-Command
sur quel ordinateur distant exécuter cette commande.
Remarquez que la sortie de hostname
est maintenant le nom de l’ordinateur distant WEBSRV1
. Vous avez exécuté du code sur WEBSRV1. L’exécution d’un code simple à l’intérieur d’un bloc de script et son passage à une simple machine distante est l’application la plus facile de Invoke-Command
mais elle peut faire beaucoup plus.
Passer des variables locales à des blocs de script distants
Vous n’allez pas avoir une seule référence Invoke-Command à l’intérieur d’un script. Votre script va probablement faire des dizaines de lignes, avoir des variables définies à certains endroits, des fonctions définies dans des modules, et ainsi de suite. Même si envelopper du code entre quelques accolades peut sembler innocent, vous changez en réalité l’ensemble de la portée dans laquelle ce code s’exécute. Après tout, vous envoyez ce code à un ordinateur distant. Cet ordinateur distant n’a aucune idée de tout le code local sur votre machine, à part ce qui se trouve dans le scriptblock.
Par exemple, peut-être avez-vous une fonction avec un nom d’ordinateur et un paramètre de chemin de fichier. Le but de cette fonction est d’exécuter un programme d’installation de logiciel sur l’ordinateur distant. Vous pouvez passer le nom de l’ordinateur et le chemin d’accès au fichier « local » à l’installateur qui est déjà situé sur l’ordinateur distant.
La fonction ci-dessous semble raisonnable, n’est-ce pas ? Exécutons-la.
Elle échoue avec un message d’erreur obscur en raison de mon utilisation de l’opérateur ampersand. Le code n’était pas faux mais il a échoué car $InstallerFilePath
était vide même si vous avez passé une valeur avec le paramètre de la fonction. Nous pouvons tester cela en remplaçant l’ampersand par Write-Host
.
Nouvelle ligne de fonction : Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Le chemin d'accès de l'installateur est : $InstallerFilePath" }
Remarquez que la valeur de $InstallerFilePath
est nulle. La variable n’a pas été étendue car elle n’a pas été transmise à la machine distante. Pour transmettre des variables définies localement au scriptblock distant, nous avons deux options ; nous pouvons préfixer le nom de la variable avec $using:
à l’intérieur du scriptblock ou utiliser le paramètre ArgumentList
de Invoke-Command
. Regardons les deux.
Le Paramètre ArgumentList
Une façon de transmettre des variables locales à un scriptblock distant est d’utiliser le paramètre ArgumentList
de Invoke-Command
. Ce paramètre vous permet de transmettre des variables locales au paramètre et de remplacer les références de variables locales dans le scriptblock par des espaces réservés.
Passer les variables locales au paramètre ArgumentList
est facile.
La partie qui embrouille certaines personnes est la manière de structurer les variables à l’intérieur du scriptblock. Au lieu d’utiliser { & $InstallerPath }
, nous devons le changer pour être soit { & $args[0] }
soit {param($foo) & $foo }
. Les deux fonctionnent de la même manière, mais lequel devriez-vous utiliser?
Le paramètre ArgumentList
est une collection d’objets. Les collections d’objets vous permettent de transmettre un ou plusieurs objets à la fois. Dans ce cas, je transmets simplement un objet.
Lorsqu’il est exécuté, la commande Invoke-Command prend cette collection et l’injecte dans le scriptblock, la transformant essentiellement en un tableau appelé $args
. Rappelez-vous que $args -eq ArgumentList
. À ce stade, vous feriez référence à chaque index de la collection comme vous le feriez avec un tableau. Dans notre cas, nous n’avions qu’un seul élément dans la collection ($InstallerFilePath
) qui se « traduisait » en $args[0]
, ce qui signifie le premier index dans cette collection. Cependant, si vous en aviez plus, vous les référenceriez avec $args[1]
, $args[2]
, et ainsi de suite.
De plus, si vous préférez attribuer de meilleurs noms de variables aux variables du scriptblock, vous pouvez également ajouter des paramètres au scriptblock, tout comme une fonction. Après tout, un scriptblock est simplement une fonction anonyme. Pour créer des paramètres de scriptblock, créez un bloc param avec le nom du paramètre. Une fois créé, faites référence à ce paramètre dans le scriptblock comme indiqué ci-dessous.
Dans ce cas, les éléments de la collection ArgumentList
sont « mappés » aux paramètres définis dans l’ordre. Les noms des paramètres n’ont pas d’importance ; c’est l’ordre qui compte. Invoke-Command
prendra le premier élément de la collection ArgumentList
, cherchera le premier paramètre et mappera ces valeurs, fera de même pour le deuxième, le troisième, et ainsi de suite.
La construction $Using
Le constructeur $using
est une autre façon populaire de passer des variables locales à un bloc de script distant. Ce constructeur vous permet de réutiliser les variables locales existantes en préfixant simplement le nom de la variable avec $using:
. Pas besoin de se soucier d’une collection $args
ni d’ajouter un bloc de paramètres.
Le constructeur $using
de PowerShell est beaucoup plus simple, mais si jamais vous apprenez à utiliser Pester, vous verrez que ArgumentList
sera votre ami.
Invoke-Command et New-PSSession
Techniquement, cet article ne concerne que Invoke-Command, mais pour démontrer son utilité, nous devons également toucher brièvement à la commande New-PSSession
. Rappelez-vous plus tôt que j’ai mentionné que Invoke-Command
peut utiliser des commandes « ad-hoc » ou utiliser des sessions existantes.
Tout au long de cet article, nous avons simplement exécuté des commandes « ad-hoc » sur des ordinateurs distants. Nous avons démarré une nouvelle session, exécuté du code et l’avons arrêtée. C’est bien pour des situations ponctuelles, mais pas tant pour une situation où vous exécutez des dizaines de commandes sur le même ordinateur. Dans ce cas, il est préférable de réutiliser une session PSSession existante en en créant une avec New-PSSession
au préalable.
Avant d’exécuter des commandes, vous devrez d’abord créer une PSSession avec New-PSSession
. Vous pouvez le faire en exécutant simplement $session = New-PSSession -ComputerName WEBSRV1
. Cela crée une session à distance sur le serveur ainsi qu’une référence à cette session sur ma machine locale. À ce stade, je peux remplacer mes références ComputerName
par Session
et pointer Session
vers ma variable $session
sauvegardée.
Lors de l’exécution, vous remarquerez que les performances sont meilleures car la session a déjà été établie. Cependant, il est important de supprimer la session ouverte avec Remove-PSSession
une fois terminé.
Résumé
La cmdlet PowerShell Invoke-Command est l’une des cmdlets les plus courantes et puissantes. C’est celle que j’utilise personnellement le plus souvent parmi presque toutes. Sa facilité d’utilisation et sa capacité à exécuter n’importe quel code sur des ordinateurs distants sont extrêmement puissantes, c’est une commande que je recommande d’apprendre de A à Z !