Gli specialisti IT lavorano raramente solo sul nostro computer locale. Utilizzando il cmdlet Invoke-Command di PowerShell, non dobbiamo farlo! Questo cmdlet ci consente di scrivere codice in modo trasparente come se stessimo lavorando sul nostro computer locale.
Utilizzando la funzionalità di PowerShell Remoting, il cmdlet Invoke-Command è un cmdlet di PowerShell comunemente utilizzato che consente all’utente di eseguire il codice all’interno di una sessione PSSession. Questa PSSession può essere creata in precedenza con il cmdlet New-PSSession o può essere creata e distrutta rapidamente anche una sessione temporanea.
Correlato: PowerShell Remoting: La guida definitiva
Pensa a Invoke-Command come al PowerShell psexec. Anche se sono implementati in modo diverso, il concetto è lo stesso. Prendi un po’ di codice o un comando e eseguilo “localmente” sul computer remoto.
Perché Invoke-Command funzioni, tuttavia, devi avere PowerShell Remoting abilitato e disponibile sul computer remoto. Per impostazione predefinita, tutte le macchine Windows Server 2012 R2 o successive lo hanno abilitato insieme alle appropriate eccezioni del firewall. Se hai ancora macchine Server 2008, ci sono più modi per configurare Remoting, ma un modo semplice è eseguire winrm quickconfig o Enable-PSRemoting sulla macchina remota.
Per dimostrare come funziona Invoke-Command con un “comando ad hoc”, ovvero uno che non richiede la creazione di una nuova PSSession, diciamo che hai un computer remoto Windows Server 2012 R2 o successivo associato a un dominio. Le cose diventano un po’ disordinate quando si lavora su computer in un gruppo di lavoro. Aprirò la mia console PowerShell, scriverò Invoke-Command
e premerò Invio.
I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.
Per dimostrare che il codice all’interno dello scriptblock viene eseguito sul computer remoto, eseguiamo il comando hostname
. Questo comando restituirà il nome host del computer su cui viene eseguito. Eseguendo hostname
sul mio computer locale, ottengo il suo nome.
Passiamo ora uno scriptblock con lo stesso codice all’interno di uno scriptblock a Invoke-Command
. Prima di farlo, però, stiamo dimenticando un parametro obbligatorio: ComputerName
. Dobbiamo dire a Invoke-Command
su quale computer remoto eseguire questo comando.
Osserva che l’output di hostname
è ora il nome del computer remoto WEBSRV1
. Hai eseguito del codice su WEBSRV1. Eseguire un codice semplice all’interno di uno scriptblock e passarlo a una singola macchina remota è l’applicazione più semplice di Invoke-Command
, ma può fare molto di più.
Passaggio di Variabili Locali a Scriptblock Remoti
Non avrai un singolo riferimento a Invoke-Command all’interno di uno script. Lo script sarà probabilmente composto da diverse decine di righe, con variabili definite in vari punti, funzioni definite in moduli e così via. Anche se racchiudere del codice tra un paio di parentesi graffe sembra innocuo, in realtà stai cambiando l’intero contesto in cui quel codice viene eseguito. Dopotutto, stai inviando quel codice a un computer remoto. Questo computer remoto non ha idea di tutto il codice locale sulla tua macchina a meno che non sia presente nello scriptblock.
Ad esempio, supponiamo che tu abbia una funzione con un parametro per il nome del computer e per il percorso del file. Lo scopo di questa funzione è eseguire un’installazione di un software sul computer remoto. Puoi passare il nome del computer e il percorso del file “locale” all’installatore che è già presente sul computer remoto.
La seguente funzione sembra ragionevole, giusto? Proviamola.
Si verifica un errore oscuro a causa dell’uso dell’operatore ampersand. Il codice non era sbagliato ma è fallito perché $InstallerFilePath
era vuoto anche se hai passato un valore tramite il parametro della funzione. Possiamo testarlo sostituendo l’ampersand con Write-Host
.
Nuova linea di funzione: Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Il percorso dell'installatore è: $InstallerFilePath" }
Si noti che il valore di $InstallerFilePath
è nullo. La variabile non è stata espansa perché non è stata passata alla macchina remota. Per passare le variabili definite localmente allo scriptblock remoto, abbiamo due opzioni; possiamo precedere il nome della variabile con $using:
all’interno dello scriptblock o possiamo utilizzare il parametro ArgumentList
di Invoke-Command
. Vediamo entrambi i casi.
Il parametro ArgumentList
Un modo per passare le variabili locali a uno scriptblock remoto è utilizzare il parametro ArgumentList
di Invoke-Command
. Questo parametro consente di passare le variabili locali al parametro e sostituire i riferimenti alle variabili locali nello scriptblock con segnaposto.
Passare le variabili locali al parametro ArgumentList
è semplice.
La parte che confonde alcune persone è come strutturare le variabili all’interno dello scriptblock. Invece di utilizzare { & $InstallerPath }
, dobbiamo cambiarlo in { & $args[0] }
o {param($foo) & $foo }
. Entrambi i metodi funzionano allo stesso modo, ma quale dovresti utilizzare?
Il parametro ArgumentList
è una collezione di oggetti. Le collezioni di oggetti consentono di passare uno o più oggetti contemporaneamente. In questo caso, sto passando solo uno.
Quando eseguito, il cmdlet Invoke-Command prende tale raccolta e la inietta nel blocco di script, trasformandola essenzialmente in un array chiamato $args
. Ricorda che $args -eq ArgumentList
. A questo punto, referenzi ogni indice della raccolta proprio come faresti con un array. Nel nostro caso, avevamo solo un elemento nella raccolta ($InstallerFilePath
), che si “traduceva” in $args[0]
, significando il primo indice in quella raccolta. Tuttavia, se ne avessi di più, li referenzieresti con $args[1]
, $args[2]
e così via.
Inoltre, se preferisci assegnare nomi di variabili migliori alle variabili del blocco di script, puoi anche aggiungere parametri al blocco di script, proprio come una funzione. Dopotutto, un blocco di script è solo una funzione anonima. Per creare parametri di blocco di script, crea un blocco param con il nome del parametro. Una volta creato, fai riferimento a quel parametro nel blocco di script come mostrato di seguito.
In questo caso, gli elementi nella raccolta ArgumentList
sono “mappati” ai parametri definiti nell’ordine. I nomi dei parametri non importano; è l’ordine che conta. Invoke-Command
prenderà il primo elemento nella raccolta ArgumentList
, cercherà il primo parametro e mapparà quei valori, farà lo stesso per il secondo, il terzo e così via.
La costruzione $Using
Il costrutto $using
è un altro modo popolare per passare variabili locali a un blocco di script remoto. Questo costrutto ti permette di riutilizzare le variabili locali esistenti semplicemente aggiungendo il prefisso $using:
al nome della variabile. Non c’è bisogno di preoccuparsi di una collezione $args
né di aggiungere un blocco di parametri.
Il costrutto $using
di PowerShell è molto più semplice, ma se mai ti avvicini al learning di Pester, vedrai che ArgumentList
sarà tuo amico.
Invoke-Command e New-PSSession
Tecnicamente, questo post riguarda solo Invoke-Command, ma per dimostrarne l’utilità, è necessario accennare brevemente al comando New-PSSession
anche. Ricorda prima ho menzionato che Invoke-Command
può utilizzare comandi “ad-hoc” o utilizzare sessioni esistenti.
In tutto questo post, abbiamo solo eseguito comandi “ad-hoc” su computer remoti. Abbiamo avviato una nuova sessione, eseguito il codice e terminato la sessione. Questo va bene per situazioni occasionali ma non è ideale quando stai eseguendo dozzine di comandi sullo stesso computer. In questo caso, è meglio riutilizzare una sessione PSSession esistente creandone una con New-PSSession
in anticipo.
Prima di eseguire qualsiasi comando, dovrai prima creare una PSSession con New-PSSession
. Possiamo farlo semplicemente eseguendo $session = New-PSSession -ComputerName WEBSRV1
. Questo crea una sessione remota sul server e un riferimento a quella sessione sul mio computer locale. A questo punto, posso sostituire i riferimenti al mio ComputerName
con Session
e puntare Session
alla mia variabile salvata $session
.
Quando viene eseguito, noterai che le prestazioni sono più veloci perché la sessione è già stata creata. Tuttavia, è importante rimuovere la sessione aperta con Remove-PSSession
una volta completato.
Riepilogo
Il cmdlet PowerShell Invoke-Command è uno dei cmdlet più comuni e potenti. È quello che personalmente uso di più tra tutti. La sua facilità d’uso e la capacità di eseguire qualsiasi codice su computer remoti è estremamente potente ed è un comando che consiglio di imparare a fondo!