IT-professionals werken zelden alleen op onze lokale computer. Met behulp van de PowerShell Invoke-Command cmdlet hoeft dat ook niet! Deze cmdlet stelt ons in staat code naadloos te schrijven alsof we op onze lokale computer aan het werk waren.
Door gebruik te maken van de PowerShell Remoting-functie, is de Invoke-Command
cmdlet een veelgebruikte PowerShell-cmdlet waarmee de gebruiker code kan uitvoeren binnen een PSSession. Deze PSSession kan ofwel eerder zijn gemaakt met de New-PSSession
cmdlet, of het kan snel een tijdelijke sessie aanmaken en afbreken.
Gerelateerd: PowerShell Remoting: De ultieme gids
Beschouw Invoke-Command als de PowerShell-variant van psexec. Hoewel ze op verschillende manieren zijn geïmplementeerd, is het concept hetzelfde. Neem een stuk code of opdracht en voer het “lokaal” uit op de externe computer.
Om Invoke-Command
te laten werken, moet PowerShell Remoting zijn ingeschakeld en beschikbaar zijn op de externe computer. Standaard hebben alle machines met Windows Server 2012 R2 of later dit ingeschakeld, samen met de juiste firewall-uitzonderingen. Als je helaas nog Server 2008-machines hebt, zijn er meerdere manieren om Remoting in te stellen, maar een eenvoudige manier is door winrm quickconfig
of Enable-PSRemoting
uit te voeren op de externe machine.
Om te laten zien hoe Invoke-Command werkt met een “ad-hoc commando”, wat betekent dat er geen nieuwe PSSession hoeft te worden gemaakt, laten we zeggen dat je een externe Windows Server 2012 R2 of latere computer hebt die is toegetreden tot het domein. Dingen worden een beetje rommelig wanneer je werkt aan workgroup-computers. Ik open mijn PowerShell-console, typ Invoke-Command
en druk op Enter.
I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.
Zodat we kunnen bewijzen dat de code binnen de scriptblok wordt uitgevoerd op de externe computer, laten we gewoon het hostname
-commando uitvoeren. Dit commando geeft de hostnaam van de computer terug waarop het wordt uitgevoerd. Het uitvoeren van hostname
op mijn lokale computer levert de naam op.
Laten we nu een scriptblok doorgeven met dezelfde code binnen een scriptblok naar Invoke-Command
. Voordat we dat doen, vergeten we een vereiste parameter: ComputerName
. We moeten Invoke-Command
vertellen op welke externe computer dit commando moet worden uitgevoerd.
Merk op dat de uitvoer van hostname
nu de naam is van de externe computer WEBSRV1
. Je hebt wat code uitgevoerd op WEBSRV1. Het uitvoeren van eenvoudige code binnen een scriptblok en deze doorgeven aan een enkele externe machine is de eenvoudigste toepassing van Invoke-Command
, maar het kan veel meer doen.
Het doorgeven van lokale variabelen naar externe scriptblokken
Je gaat waarschijnlijk geen enkele Invoke-Command referentie hebben binnen een script. Jouw script zal waarschijnlijk tientallen regels lang zijn, variabelen op verschillende plaatsen gedefinieerd hebben, functies gedefinieerd in modules, enzovoort. Ook al lijkt het misschien onschuldig om wat code tussen een paar accolades te zetten, je verandert eigenlijk de gehele scope waarin die code wordt uitgevoerd. Immers, je stuurt die code naar een externe computer. Die externe computer heeft geen idee van alle lokale code op jouw machine, behalve wat er in het scriptblock staat.
Bijvoorbeeld, misschien heb je een functie met een computernaam en een bestandspad parameter. Het doel van deze functie is om wat software-installateur op de externe computer uit te voeren. Je kunt de computernaam en het “lokale” bestandspad aan de installateur doorgeven dat al op de externe computer staat.
De onderstaande functie lijkt redelijk, toch? Laten we het uitvoeren.
Het mislukt met een obscure foutmelding als gevolg van mijn gebruik van de ampersand-operator. De code was niet fout, maar het mislukte omdat $InstallerFilePath
leeg was, ook al heb je een waarde doorgegeven met de functieparameter. We kunnen dit testen door de ampersand te vervangen door Write-Host
.
Nieuwe functieregel: Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Installatiepad is: $InstallerFilePath" }
Let op dat de waarde van $InstallerFilePath
niets is. De variabele is niet uitgebreid omdat deze niet naar de externe machine is doorgegeven. Om lokaal gedefinieerde variabelen naar het externe scriptblok door te geven, hebben we twee opties; we kunnen de variabelenaam voorafgaan met $using:
binnen het scriptblok, of we kunnen de ArgumentList
-parameter van Invoke-Command
gebruiken. Laten we beide opties bekijken.
De ArgumentList-parameter
Één manier om lokale variabelen naar een extern scriptblok door te geven, is door de ArgumentList
-parameter van Invoke-Command
te gebruiken. Deze parameter stelt je in staat om lokale variabelen naar de parameter te sturen en lokale variabelen in het scriptblok te vervangen door aanduidingen.
Het doorgeven van de lokale variabelen naar de ArgumentList
-parameter is eenvoudig.
Wat sommige mensen in de war brengt, is hoe ze de variabelen binnen het scriptblok moeten structureren. In plaats van { & $InstallerPath }
te gebruiken, moeten we het veranderen naar { & $args[0] }
of {param($foo) & $foo }
. Beide manieren werken hetzelfde, maar welke moet je gebruiken?
De ArgumentList
-parameter is een objectverzameling. Objectverzamelingen stellen je in staat om één of meer objecten tegelijk door te geven. In dit geval geef ik er slechts één door.
Wanneer uitgevoerd, neemt het Invoke-Command cmdlet die verzameling en injecteert deze vervolgens in het scriptblock, waardoor het in feite wordt omgezet in een array genaamd `$args`. Onthoud dat `$args -eq ArgumentList`. Op dit punt zou je elk index van de verzameling refereren zoals je dat zou doen met een array. In ons geval hadden we slechts één element in de verzameling (`$InstallerFilePath`) wat “vertaald” werd naar `$args[0]`, wat betekent dat het eerste index in die verzameling. Als je echter meer had, zou je ze refereren met `$args[1]`, `$args[2]` enzovoort.
Bovendien, als je liever betere variabelenamen toewijst aan scriptblock-variabelen, kun je ook parameters toevoegen aan het scriptblock, net als bij een functie. Uiteindelijk is een scriptblock gewoon een anonieme functie. Om scriptblock parameters te maken, maak je een param blok met de naam van de parameter. Zodra deze is gemaakt, kun je vervolgens naar die parameter verwijzen in het scriptblock zoals hieronder.
In dit geval worden de elementen in de verzameling `ArgumentList` “gemapt” naar de gedefinieerde parameters in volgorde. De parameter namen doen er niet toe; de volgorde is belangrijk. `Invoke-Command` zal het eerste element in de `ArgumentList` verzameling nemen, zoeken naar de eerste parameter en die waarden mappen, hetzelfde doen voor de tweede, de derde enzovoort.
De $Using Construct
De $using
constructie is een andere populaire manier om lokale variabelen door te geven aan een externe scriptblock. Met deze constructie kunt u de bestaande lokale variabelen hergebruiken door eenvoudigweg de variabelenaam vooraf te laten gaan door $using:
. U hoeft zich geen zorgen te maken over een $args
collectie noch over het toevoegen van een parameterblok.
De PowerShell $using
constructie is veel eenvoudiger, maar als je ooit bezig bent met het leren van Pester, zul je zien dat ArgumentList
je vriend zal zijn.
Invoke-Command en New-PSSession
Technisch gezien gaat deze post alleen over Invoke-Command, maar om de bruikbaarheid te demonstreren, moeten we kort ingaan op het New-PSSession
commando. Herinner je je eerder dat ik heb vermeld dat Invoke-Command
“ad-hoc” commando’s kan gebruiken of bestaande sessies kan gebruiken.
Gedurende deze post hebben we alleen “ad-hoc” commando’s uitgevoerd op externe computers. We hebben een nieuwe sessie opgezet, code uitgevoerd en afgebroken. Dit is prima voor incidentele situaties, maar niet zozeer voor wanneer je tientallen commando’s op dezelfde computer uitvoert. In dit geval is het beter om een bestaande PSSession opnieuw te gebruiken door er van tevoren een aan te maken met New-PSSession
.
Voor het uitvoeren van enige commando’s, moet je eerst een PSSession maken met New-PSSession
. Dit kunnen we doen door eenvoudigweg $session = New-PSSession -ComputerName WEBSRV1
uit te voeren. Hiermee wordt een externe sessie op de server gemaakt, evenals een referentie naar die sessie op mijn lokale machine. Op dit punt kan ik mijn ComputerName
-verwijzingen vervangen door Session
en Session
wijzen naar mijn opgeslagen $session
-variabele.
Wanneer uitgevoerd, zul je merken dat de prestaties sneller zijn omdat de sessie al is opgebouwd. Wanneer deze echter is voltooid, is het belangrijk om de open sessie te verwijderen met Remove-PSSession
.
Samenvatting
De Invoke-Command PowerShell-cmdlet is een van de meest voorkomende en krachtige cmdlets die er zijn. Het is er een die ik persoonlijk het meest gebruik van bijna allemaal. De gebruiksvriendelijkheid en het vermogen om willekeurige code op externe computers uit te voeren, zijn uiterst krachtig en het is een commando dat ik aanbeveel om van boven naar beneden te leren!