IT專業人員很少只在本地電腦上工作。使用PowerShell的Invoke-Command cmdlet,我們不需要這樣做!這個cmdlet允許我們無縫地編寫代碼,就好像我們在本地電腦上工作一樣。
通過使用PowerShell的遠程功能,Invoke-Command cmdlet是一個常用的PowerShell cmdlet,允許用戶在PSSession內執行代碼。這個PSSession可以是之前使用New-PSSession cmdlet創建的,也可以快速創建和取消的臨時會話。
把Invoke-Command想像成PowerShell的psexec。雖然它們的實現方式不同,但概念是相同的。拿一些代碼或命令在遠程計算機上“本地”運行。
但是,要使Invoke-Command工作,必須在遠程計算機上啟用並可用PowerShell遠程功能。默認情況下,所有的Windows Server 2012 R2或更新版本的機器都已啟用該功能,並帶有相應的防火牆例外。如果你不幸仍然有Server 2008機器,有多種方法可以設置遠程操作,但一種簡單的方法是在遠程計算機上運行winrm quickconfig或Enable-PSRemoting。
為了展示Invoke-Command如何與“即時命令”一起使用,即不需要創建新的PSSession,假設您有一台遠程的Windows Server 2012 R2或更高版本的域加入計算機。在使用工作組計算機時會有一些混亂。我將打開我的PowerShell控制台,輸入Invoke-Command並按Enter鍵。
I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.
為了證明腳本塊內的代碼在遠程計算機上執行,讓我們運行hostname命令。該命令將返回運行它的計算機的主機名。在我的本地計算機上運行hostname命令會顯示它的名稱。
現在讓我們將具有相同代碼的腳本塊作為參數傳遞給Invoke-Command。然而,在這之前,我們忘記了一個必需的參數:ComputerName。我們必須告訴Invoke-Command在哪台遠程計算機上運行此命令。
請注意,hostname的輸出現在是遠程計算機WEBSRV1的名稱。您在WEBSRV1上運行了一些代碼。在一個腳本塊中運行簡單的代碼並將其傳遞給一個遠程計算機是Invoke-Command的最簡單應用,但它還可以做更多。
將本地變量傳遞給遠程腳本塊
你不會在一個腳本中只有一個Invoke-Command參考。你的腳本可能有幾十行,有變量定義的地方,還有在模塊中定義的函數等等。儘管只是將一些代碼放在幾個大括號中看起來無害,但實際上你正在改變該代碼運行的整個作用域。畢竟,你正在將該代碼發送到遠程計算機。遠程計算機除了腳本塊中的內容外,對於你本地計算機上的所有本地代碼一無所知。
例如,也許你有一個帶有計算機名稱和文件路徑參數的函數。這個函數的目的是在遠程計算機上運行一些軟件安裝程序。你可以將計算機名稱和“本地”文件路徑傳遞給已經位於遠程計算機上的安裝程序。
下面的函數看起來合理,對吧?讓我們運行它。
由於我使用了”和”運算符,它失敗並顯示了一個晦澀的錯誤消息。代碼並沒有錯誤,但它失敗是因為$InstallerFilePath
是空的,即使你使用函數參數傳遞了一個值。我們可以通過將”和”運算符替換為Write-Host
來測試這一點。
新的函數行:Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "安裝程序路徑為:$InstallerFilePath" }
請注意,$InstallerFilePath
的值是空的。該變量未展開,因為它未傳遞給遠程計算機。要將本地定義的變量傳遞給遠程腳本塊,有兩種選擇;我們可以在腳本塊內部的變量名前加上$using:
,或者我們可以使用Invoke-Command
參數ArgumentList
。讓我們看看兩者的區別。
ArgumentList參數
將本地變量傳遞給遠程腳本塊的一種方法是使用Invoke-Command
的ArgumentList
參數。該參數允許您將本地變量傳遞給參數並將腳本塊中的本地變量引用替換為佔位符。
將本地變量傳遞給ArgumentList
參數很容易。
有些人會被困擾的部分是如何在腳本塊內部結構化變量。我們需要將{ & $InstallerPath }
更改為{ & $args[0] }
或{param($foo) & $foo }
。兩種方式都可以正常工作,但應該使用哪一種?
ArgumentList參數是一個對象集合。對象集合允許一次傳遞一個或多個對象。在這個例子中,我只傳遞了一個對象。
當執行時,Invoke-Command cmdlet將該集合注入到腳本區塊中,從而將其轉換為名為$args
的數組。請記住$args -eq ArgumentList
。此時,您可以像操作數組一樣引用集合的每個索引。在我們的例子中,集合中只有一個元素($InstallerFilePath
),它“轉換”為$args[0]
,表示該集合中的第一個索引。但是,如果有更多的元素,您可以使用$args[1]
,$args[2]
等引用它們。
此外,如果您希望為腳本區塊變量分配更好的變量名稱,您還可以像定義函數一樣為腳本區塊添加參數。畢竟,腳本區塊就是一個匿名函數。要創建腳本區塊參數,請使用參數名稱創建一個param區塊。創建後,在腳本區塊中引用該參數,如下所示。
在這種情況下,ArgumentList
集合中的元素按照順序“映射”到定義的參數。參數名稱並不重要,重要的是順序。Invoke-Command
將取出ArgumentList
集合中的第一個元素,尋找第一個參數並映射這些值,然後對第二個元素進行相同的操作,依此類推。
使用$Using構造
$using
構造是將本地變量傳遞給遠程腳本塊的另一種常用方式。這個構造允許您重用現有的本地變量,只需在變量名前加上 $using:
。不需要擔心 $args
集合,也不需要添加參數塊。
PowerShell 的 $using
構造更加簡單,但如果您開始 學習 Pester,您會發現 ArgumentList
是您的好朋友。
Invoke-Command 和 New-PSSession
從技術上講,這篇文章只涉及 Invoke-Command,但為了展示它的用途,我們需要簡要介紹一下 New-PSSession
命令。之前我提到過 Invoke-Command
可以使用“即席”命令或使用現有的會話。
在本文中,我們一直在遠程計算機上運行“即席”命令。我們一直在啟動新的會話,運行代碼,然後關閉它。這對於一次性情況很好,但對於在同一台計算機上執行數十個命令的情況來說,這樣做效果不佳。在這種情況下,最好提前使用 New-PSSession
創建一個現有的 PSSession 來重用。
在運行任何命令之前,首先需要使用New-PSSession
創建一個PSSession。我們可以通過運行$session = New-PSSession -ComputerName WEBSRV1
來實現這一點。這將在服務器上創建一個遠程會話,並在本地機器上引用該會話。此時,我可以將ComputerName
引用替換為Session
,並將Session
指向保存的$session
變量。
運行後,您會注意到性能更快,因為會話已經建立。但完成後,重要的是使用Remove-PSSession
移除打開的會話。
總結
Invoke-Command PowerShell cmdlet是最常見和強大的cmdlet之一。這是我個人使用最多的命令之一。它的易用性和在遠程計算機上運行任何代碼的能力非常強大,我建議從頭到尾學習這個命令!