IT-Profis arbeiten selten nur an unserem lokalen Computer. Mit dem PowerShell-Befehl Invoke-Command müssen wir das auch nicht! Dieser Befehl ermöglicht es uns, Code nahtlos zu schreiben, als ob wir an unserem lokalen Computer arbeiten würden.
Durch die Verwendung der PowerShell-Remoting-Funktion ist der Invoke-Command-Befehl ein häufig verwendeter PowerShell-Befehl, mit dem der Benutzer Code in einer PSSession ausführen kann. Diese PSSession kann entweder zuvor mit dem New-PSSession-Befehl erstellt worden sein oder es kann auch schnell eine temporäre Sitzung erstellt und abgebaut werden.
Verwandt: PowerShell Remoting: Der ultimative Leitfaden
Denken Sie an Invoke-Command als das PowerShell-psexec. Obwohl sie unterschiedlich implementiert sind, ist das Konzept dasselbe. Nehmen Sie ein bisschen Code oder einen Befehl und führen Sie ihn „lokal“ auf dem Remote-Computer aus.
Damit Invoke-Command jedoch funktioniert, müssen Sie PowerShell-Remoting auf dem Remote-Computer aktiviert und verfügbar haben. Standardmäßig ist dies auf allen Windows Server 2012 R2 oder neueren Maschinen aktiviert, zusammen mit den entsprechenden Firewall-Ausnahmen. Wenn Sie das Pech haben, immer noch Server 2008-Maschinen zu haben, gibt es mehrere Möglichkeiten, Remoting einzurichten, aber eine einfache Möglichkeit besteht darin, den Befehl winrm quickconfig oder Enable-PSRemoting auf der Remote-Maschine auszuführen.
Um zu demonstrieren, wie Invoke-Command mit einem „ad-hoc Befehl“ funktioniert, der bedeutet, dass kein neues PSSession erstellt werden muss, nehmen wir an, Sie haben einen entfernten Windows Server 2012 R2 oder späteren domänenbeitretenden Computer. Die Dinge werden ein wenig kompliziert, wenn Sie an Workgroup-Computern arbeiten. Ich öffne meine PowerShell-Konsole, gebe Invoke-Command
ein und drücke Enter.
I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.
Damit wir beweisen können, dass der Code innerhalb des Skriptblocks auf dem entfernten Computer ausgeführt wird, führen wir einfach den Befehl hostname
aus. Dieser Befehl gibt den Hostnamen des Computers zurück, auf dem er ausgeführt wird. Wenn ich hostname
auf meinem lokalen Computer ausführe, erhalte ich seinen Namen.
Jetzt übergeben wir einen Skriptblock mit demselben Code an Invoke-Command
. Bevor wir das tun, vergessen wir jedoch einen erforderlichen Parameter: ComputerName
. Wir müssen Invoke-Command
mitteilen, auf welchem Remote-Computer dieser Befehl ausgeführt werden soll.
Beachten Sie, dass die Ausgabe von hostname
jetzt der Name des Remote-Computers WEBSRV1
ist. Sie haben Code auf WEBSRV1 ausgeführt. Das Ausführen von einfachem Code innerhalb eines Skriptblocks und das Übergeben an eine einzelne Remote-Maschine ist die einfachste Anwendung von Invoke-Command
, aber es kann noch viel mehr tun.
Übergeben von lokalen Variablen an Remote-Skriptblocks
Sie werden keine einzelne Invoke-Command-Referenz in einem Skript haben. Ihr Skript wird wahrscheinlich dutzende Zeilen lang sein, Variablen an verschiedenen Stellen definieren, Funktionen in Modulen definieren usw. Obwohl es unschuldig aussieht, einige Codezeilen in geschweifte Klammern zu setzen, ändern Sie tatsächlich den gesamten Gültigkeitsbereich, in dem dieser Code ausgeführt wird. Schließlich senden Sie diesen Code an einen Remote-Computer. Dieser Remote-Computer hat keine Ahnung von all dem lokalen Code auf Ihrem Computer, außer dem, was im Skriptblock enthalten ist.
Zum Beispiel haben Sie möglicherweise eine Funktion mit Computernamen und einem Dateipfadparameter. Der Zweck dieser Funktion besteht darin, einen Software-Installer auf dem Remote-Computer auszuführen. Sie können den Computernamen und den „lokalen“ Dateipfad an den Installer übergeben, der sich bereits auf dem Remote-Computer befindet.
Die folgende Funktion scheint vernünftig zu sein, oder? Lassen Sie uns sie ausführen.
Es schlägt mit einer obskuren Fehlermeldung fehl, aufgrund meiner Verwendung des Kaufmanns-Operators. Der Code war nicht falsch, aber er ist fehlgeschlagen, weil $InstallerFilePath
leer war, obwohl Sie einen Wert mit dem Funktionsparameter übergeben haben. Wir können dies testen, indem wir den Kaufmanns-Operator durch Write-Host
ersetzen.
Neue Funktionszeile: Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Installer-Pfad ist: $InstallerFilePath" }
Beachten Sie, dass der Wert von $InstallerFilePath
nichts ist. Die Variable wurde nicht erweitert, weil sie nicht an die Remote-Maschine übergeben wurde. Um lokal definierte Variablen an das Remote-Skriptblock zu übergeben, haben wir zwei Möglichkeiten: Wir können den Variablennamen im Skriptblock mit $using:
voranstellen oder wir können den ArgumentList
-Parameter von Invoke-Command
verwenden. Lassen Sie uns beide Optionen betrachten.
Der ArgumentList-Parameter
Eine Möglichkeit, lokale Variablen an einen Remote-Skriptblock zu übergeben, besteht darin, den ArgumentList
-Parameter von Invoke-Command
zu verwenden. Mit diesem Parameter können Sie lokale Variablen an den Parameter übergeben und lokale Variablenreferenzen im Skriptblock durch Platzhalter ersetzen.
Das Übergeben der lokalen Variablen an den ArgumentList
-Parameter ist einfach.
Das, was einige Leute verwirrt, ist die Strukturierung der Variablen im Skriptblock. Anstatt { & $InstallerPath }
zu verwenden, müssen wir es entweder in { & $args[0] }
oder {param($foo) & $foo }
ändern. Beide Varianten funktionieren gleich, aber welche sollte man verwenden?
Der ArgumentList
-Parameter ist eine Objektsammlung. Objektsammlungen ermöglichen es Ihnen, ein oder mehrere Objekte auf einmal zu übergeben. In diesem Fall übergebe ich nur eins.
Wenn sie ausgeführt wird, nimmt das Invoke-Command-Cmdlet diese Sammlung und injiziert sie in das Skriptblock, was es im Wesentlichen in ein Array namens $args
verwandelt. Denken Sie daran, dass $args -eq ArgumentList
. An diesem Punkt würden Sie jeden Index der Sammlung genau wie bei einem Array referenzieren. In unserem Fall hatten wir nur ein Element in der Sammlung ($InstallerFilePath
), das „übersetzt“ wurde in $args[0]
, was den ersten Index in dieser Sammlung bedeutet. Wenn Sie jedoch mehrere hätten, würden Sie sie mit $args[1]
, $args[2]
und so weiter referenzieren.
Zusätzlich können Sie, wenn Sie bessere Variablennamen für die Variablen im Skriptblock zuweisen möchten, auch Parameter zum Skriptblock hinzufügen, genau wie bei einer Funktion. Schließlich ist ein Skriptblock nur eine anonyme Funktion. Um Skriptblock-Parameter zu erstellen, erstellen Sie einen param-Block mit dem Namen des Parameters. Sobald erstellt, referenzieren Sie diesen Parameter im Skriptblock wie unten gezeigt.
In diesem Fall werden die Elemente in der ArgumentList
-Sammlung in der angegebenen Reihenfolge den definierten Parametern „zugeordnet“. Die Parameterbezeichnungen spielen keine Rolle; es ist die Reihenfolge, die wichtig ist. Invoke-Command
nimmt das erste Element in der ArgumentList
-Sammlung, sucht nach dem ersten Parameter und ordnet diese Werte zu, tut dasselbe für das zweite, das dritte usw.
Das $Using-Konstrukt
Die $using
-Konstruktion ist eine weitere beliebte Möglichkeit, lokale Variablen an einen Remote-Skriptblock zu übergeben. Mit dieser Konstruktion können Sie die vorhandenen lokalen Variablen wiederverwenden, indem Sie einfach den Variablennamen mit $using:
versehen. Sie müssen sich keine Gedanken über eine $args
-Sammlung oder das Hinzufügen eines Parameterblocks machen.
Die PowerShell $using
-Konstruktion ist viel einfacher, aber wenn Sie sich jemals mit Pester beschäftigen, werden Sie sehen, dass ArgumentList
Ihr Freund sein wird.
Invoke-Command und New-PSSession
Technisch gesehen handelt es sich bei diesem Beitrag nur um Invoke-Command, aber um seine Nützlichkeit zu demonstrieren, müssen wir kurz auf den Befehl New-PSSession
eingehen. Erinnern Sie sich daran, dass ich zuvor erwähnt habe, dass Invoke-Command
„ad-hoc“ -Befehle verwenden oder bestehende Sitzungen verwenden kann.
In diesem Beitrag haben wir bisher nur „ad-hoc“ -Befehle auf Remote-Computern ausgeführt. Wir haben eine neue Sitzung gestartet, Code ausgeführt und sie dann beendet. Dies ist für einmalige Situationen in Ordnung, aber nicht so gut, wenn Sie Dutzende von Befehlen auf demselben Computer ausführen. In diesem Fall ist es besser, eine vorhandene PSSession wiederzuverwenden, indem Sie diese mit New-PSSession
im Voraus erstellen.
Bevor Sie Befehle ausführen, müssen Sie zuerst eine PSSession mit New-PSSession
erstellen. Dies können Sie einfach tun, indem Sie $session = New-PSSession -ComputerName WEBSRV1
ausführen. Dadurch wird eine Remote-Sitzung auf dem Server erstellt sowie eine Referenz zu dieser Sitzung auf meinem lokalen Computer. An diesem Punkt kann ich meine ComputerName
-Verweise durch Session
ersetzen und Session
auf meine gespeicherte $session
-Variable verweisen.
Wenn Sie es ausführen, werden Sie feststellen, dass die Leistung schneller ist, da die Sitzung bereits erstellt wurde. Wenn Sie jedoch fertig sind, ist es wichtig, die offene Sitzung mit Remove-PSSession
zu entfernen.
Zusammenfassung
Das Invoke-Command PowerShell-Cmdlet ist eines der häufigsten und leistungsstärksten Cmdlets überhaupt. Es ist dasjenige, das ich persönlich am häufigsten benutze. Die Benutzerfreundlichkeit und die Möglichkeit, beliebigen Code auf Remote-Computern auszuführen, sind äußerst mächtig, und es ist ein Befehl, den ich empfehle, von oben bis unten zu erlernen!