使用PowerShell模块是PowerShell自动化的重要组成部分。当您开始学习PowerShell时,通常首先使用单个命令。然后逐步构建脚本,最后构建函数。
通过使用函数,您可以使脚本更加模块化。这样可以在许多地方重复使用相同的代码,而无需复制粘贴。使用函数可以节省时间,避免在每个使用它的地方都进行相同的修改。相反,您可以在一个地方改进代码。
要将函数提升到更高的级别,可以将这些函数组合成一个模块。
A module is a collection of functions in a text file with a psm1 extension. There are some optional additions, such as a module manifest and comment-based or external help that may also be included. These will be covered later on.
先决条件
I’ll be using Windows PowerShell 5.1 in this article. If you’re using an older version or PowerShell Core, your mileage may vary as to results you see.
与模块交互
首次打开PowerShell会话后,您将开始使用两个模块。第一个是Microsoft.PowerShell.Utility,其中包含许多您已经使用过的基本PowerShell函数。另一个模块是PSReadline。您可以使用Get-Module
命令查看这些起始模块。

也就是说,这不是所有可用模块的完整列表。自PowerShell 3以来,安装的模块将根据需要导入。如果您使用的是旧版本的PowerShell,则需要在使用任何命令之前使用Import-Module
命令导入模块。
在后续版本中,仍然有时需要使用Import-Module
。如果您想在模块已安装之后导入模块,可以像这样使用Import-Module
:

當使用Get-Module
命令顯示所有已導入的模組時,您將無法看到尚未導入的模組。您可以使用ListAvailable
參數來顯示所有其他可用的模組。

Get-Module -ListAvailable
預設情況下,並非所有命令都會顯示
ExportedCommands
屬性包含了從模組導出的所有可用命令的列表。您可能會在此列表和模組文件中看到一些差異。導出的命令是模組清單中內置的一個功能,它允許作者將某個函數設為隱藏。模組作者還可以使用Export-ModuleMember
命令,但這超出了本文的範圍。
模組作者可能希望將某個函數設為隱藏,因為它是用來支援其他函數而非面向用戶。為了將函數設為隱藏,作者會將它從清單的FunctionsToExport
陣列中排除。在這裡,您可以看到ExportedCommands
屬性的擴展視圖。

導入模組
使用模块有很多种方法。您可以使用模块文件的路径手动导入模块。这样可以使您能够在不进行太多工作的情况下测试和更新模块。但这样做并不便于移植性,因为您必须使用确切的模块路径。PowerShell也不会自动导入不在$env:PSModulePath
变量中的模块。
选择性导入命令
您可以使用Import-Module
仅导入特定函数,而不是整个模块,方法是使用Function
参数。这在从远程系统导入模块时可以节省时间,例如Office 365模块。
所有用户模块
为所有用户安装的模块将放置在C:\Program Files\WindowsPowerShell\Modules中。此目录包含许多预添加的模块,包括使用默认范围AllUsers
使用Install-Module
安装的任何模块。
当前用户模块
如果您要安装一个模块,但只想让单个用户使用它,则可以使用CurrentUser
范围。这将在您的文档文件夹中的C:\Users\<username>\Documents\WindowsPowerShell\Modules中放置模块文件。这在使用文档文件夹的文件夹重定向的环境中非常有用。
在这种情况下,您可以在一台计算机上安装一个模块,并在另一台计算机上使用它,因为它们都共享同一个文档文件夹。
系统模块
為了完整性,還有一個模組目錄位於C:\Windows\System32\WindowsPowerShell\1.0\Modules。雖然從技術上講,放在此路徑的模組會像其他路徑一樣被導入,但不建議這樣做,因為這個路徑是保留給微軟的系統模組使用的。
命名很重要
您可以手動將模組放在其中一個路徑中,以使它在新會話中默認可用,但您必須確保遵循模組所需的命名規則。模組文件所在的文件夾必須與 psm1 模組文件和(如果有的話)psd1 模組清單文件具有相同的名稱。
使用我們之前提到的Get-Module -ListAvailable
來引用這些路徑。您可以使用$env:PSModulePath -Split ';'
查看所有模組路徑。您可能會注意到列表中有其他路徑,這些路徑是在安裝其他程序時添加的。其中一個例子是 SQL,它在其自己的模組路徑中包含了自己的模組。

$env:PSModulePath
還有一些模組需要使用不同的安裝程序來安裝。其中一個最重要的例子是ActiveDirectory模組。從 Windows 7 到 Windows 10 1803,您需要使用遠程伺服器管理工具 (RSAT) 安裝程式來安裝此模組。
在更新的 Windows 10 版本(1809+)中,這只能通過功能隨需求獲得。安裝 RSAT 會安裝ActiveDirectory模組和許多其他用於管理其他 Windows 角色的模組。在 Windows 伺服器作業系統上,這些模組是通過伺服器管理員進行安裝的。
導入遠程模塊(隱式遠程)
有些情況下,在本地運行模塊並不實用。相反,最好連接到遠程設備並導入安裝在其上的模塊。這樣做時,命令實際上是在遠程計算機上執行的。這在使用Microsoft的Office 365模塊時經常使用。其中許多模塊連接到Office 365服務器,然後導入一個模塊。當您運行任何命令時,它們在遠程服務器上運行,然後將輸出發送回您的會話。
導入遠程模塊的另一個用途是當您在本地未安裝該模塊時。這就是如果您沒有安裝ActiveDirectory模塊,但您嘗試導入它時會發生的情況。

要導入遠程模塊,首先必須創建一個PSSession。您可以使用New-PSSession
創建會話。然後,您可以使用Import-Module
的PSSession參數導入可在遠程設備上使用的模塊。
使用此方法導入遠程模塊可以在分佈式環境中實現更快的代碼執行。例如,如果您在自己的計算機上工作,但您要處理的服務器位於美國的另一端,使用本地對服務器運行某些命令可能需要更長的時間。相反,在服務器上運行命令並將輸出返回到您的本地會話則快得多。
添加模塊前綴
您还可以在从远程机器导入的函数上添加前缀。此选项可在导入本地模块时使用,但在测试不同版本的模块并行运行时很少使用。
如果您运行了上述导入命令,并在查看命令时看到了以下内容:

在这种情况下,您可以使用前缀来显示它不是本地模块。这可以在导入可在本地使用的模块的情况下使用。添加前缀可以减少代码执行位置的混淆。
移除模块
您还可以在当前会话中移除模块,而不使用Remove-Module
命令。这将从本地会话中移除模块,但不会删除模块文件。您可能希望在使用远程会话使用模块的情况下使用此选项。您可以使用Remove-Module
清理会话,然后断开远程会话。

Remove-Module
的另一个用途是在更改模块时不想启动新的PowerShell会话。在这种情况下,您将使用Remove-Module
,然后使用Import-Module
重新加载它到您的会话。或者,您可以在Import-Module
中使用Force
参数。这将为您完成模块的卸载和重新加载。
PowerShell模块的组成
A module can consist of one or more files. To meet the minimum requirements for a module, you must have a module file. This can be a PSM1 file or any other module file such as a binary module file. To build upon that, your psm1 should have functions defined in it, or it will not be much use to anyone.
雖然對於函數的外觀或功能沒有具體要求,但有一些指南。通常情況下,建議將所有函數放在圍繞同一概念的模塊中。
模塊包含相關的函數
例如,ActiveDirectory模塊只包括與Active Directory有關的功能。通常函數名也包含前綴。以ActiveDirectory模塊為例,函數名中的名詞都以AD開頭。
使用這些指南有助於發現函數。想像一下,你剛剛導入了這個新模塊,想要通過Tab鍵查看函數。如果所有函數都有相似的名稱結構,這樣做就容易得多。雖然你可能經常看到模塊以PS開頭,但這個前綴只正式保留給Microsoft模塊。如果你在模塊開頭使用PS,可能不會引起問題,但可能會與其他模塊名稱產生衝突。
根據這些指南,如果你有一堆與註冊表交互有關的函數,你可以這樣做:
模塊清單
要擴充文本模塊文件,還可以包含一個模塊清單。這些文件的擴展名為PSD1,包含有關該模塊的元數據。這是你會包含作者信息、模塊描述、其他所需模塊和許多其他屬性的地方。要發布到存儲庫,需要填寫Author
和Description
字段。
這是我們可能為註冊模組擁有的清單範例:
雖然一開始可能會讓人感到有些困惑,但 Microsoft 提供了一個方便的 cmdlet,可以用來生成模組清單。包含的命令是 New-ModuleManifest
。要生成上述清單,可以使用:
外部說明檔案
某些模組中還可能會有外部說明檔案。你可以根據檔案名稱末尾的 <ModuleName>-Help.xml 來識別這些外部說明檔案。這些外部說明檔案包含的是與函數定義中通常包含的命令說明相同的信息。
如果要在導入模組後使用 Get-Help
命令,還需要在函數中添加 # .ExternalHelp <ModulePath>-Help.xml
,以便正確運作。通常,只有在非常大的模組中才會看到外部說明檔案,且這些檔案超出了本範圍。
相關檔案
儘管這些是模組中最常見的檔案類型,但並不是唯一的檔案類型。有時,除了文本模組外,還會看到二進位檔案,因為還有其他相依性。通過探索模組路徑,你可以找到許多其他檔案類型的範例。
如果要正確發佈非標準模組檔案,可以在模組清單的 FileList
參數中包含其他檔案。
在模組清單中,您會注意到許多其他目前為空的參數。您可以使用這些參數來定義使用您的模組的其他要求。例如,您可以定義模組可以使用的 PowerShell 版本。如果您嘗試在不支援的 PowerShell 版本上導入模組,您將看到以下訊息:

PSRepositories
模組的一個重要分發選項是 PSRepository。在1000英尺的視圖中,PSRepository是多人或多設備可以訪問模組檔案的本地位置。這些通常是可以發布檔案的網頁伺服器。
您還可以使用目錄作為存儲庫,但這會限制存儲庫的功能。您可以自行託管 PSRepository,或者您可以利用互聯網上提供的許多選項,如 PowerShell Gallery。您可以使用 Get-PSRepository
命令查看您的 PSRepositories。

默認情況下,您只會有一個條目,並且它將是 PowerShell Gallery。您可能會注意到它會顯示為不受信任。這是因為 PowerShell 告知您,通過使用 PowerShell Gallery,您可能在使用未經 Microsoft 審核和批准的程式碼。這意味著在從其中安裝任何模組之前,您必須明確給予權限。
添加 PSRepositories
您還可以添加自己的存儲庫。要信任 PowerShell Gallery,您可以運行 Get-PSRepository -Name PSGallery | Set-PSRepository -InstallationPolicy Trusted
,或者您可以在首次從 PowerShell Gallery 安裝模組時接受警告。
所有與這些 PSRepositories 互動的命令都可以在 PowerShellGet 模組中找到。你可以在這裡看到這些函數:

在與特定的儲存庫互動之前,可能需要更新 PowerShellGet 模組。
尋找模組
使用 PSRepository 的另一個關鍵功能是能夠搜尋模組。可以使用 Find-Module 命令來實現這一點。有多種方式可以過濾以找到你所尋找的模組,但現在你可以像這樣搜索 VMware 的模組:

這將顯示所有以 VMware 開頭的模組。雖然大部分都來自於 VMware,但你需要查看作者屬性以查看誰發佈了該模組。
由於任何人都可以上傳到 PowerShell Gallery,因此有成千上萬的模組可用。這意味著你可能會找到在你的用例中無法正常工作的模組。你將會發現大多數模組都是開源的,因此你可以對其進行貢獻,以改進模組的功能。
安裝模組
要使用 Install-Module 命令,你必須擁有一個可信任的 PSRepository,該儲存庫用於托管模組。這可以是 PowerShell Gallery、其他互聯網 PSRepository 或自託管站點。你可以從 Find-Module 命令中進行管道連接,以便在安裝模組之前輕鬆確認模組。

你還可以使用 MinimumVersion、MaximumVersion 或 RequiredVersion 參數來定義模組的版本。
要查看使用 Install-Module
安裝的所有模組,您可以使用 Get-InstalledModule
。這將列出安裝到 AllUsers
範圍或您的 CurrentUser
範圍的所有模組。
卸載模組
就像您可以安裝模組一樣,您也可以卸載模組。如果模組不是通過 Install-Module
命令安裝的,則無法使用 Uninstall-Module
命令卸載它。

Uninstall-Module
如您在此處所見,我們正在嘗試卸載 ActiveDirectory 模組。由於此模組未使用 Install-Module
安裝,因此在嘗試使用 Uninstall-Module
時會收到錯誤。要卸載此模組,我們需要通過反轉安裝模組的步驟來卸載它。
要查看模組的成功卸載,您可以卸載先前安裝的 VMware.PowerCLI 模組。

即使您已卸載了 VMware.PowerCLI,您仍然可以看到仍安裝了許多相依性。如果您想卸載所有模組,我們可以使用 Get-InstalledModule VMware.* | Uninstall-Module -Force
。
您會遇到完全卸載此模組的困難,是因為它具有如此多的相依性。此外,其中一些模組是彼此的相依性,這就是為什麼需要使用 Force
參數的原因。
更新模組
現在你已經知道如何安裝和卸載模組,你可能想知道如何更新已安裝的模組。
就像其他操作一樣,如果模組不是使用Install-Module
安裝的,你就無法使用PowerShell命令更新。你可以使用Update-Module
將模組更新到最新版本,或者更新到較新的特定版本。
還有一個AllowPreRelease
的開關,它允許你更新到尚未正式發布的版本。有時這可能有所幫助,因為可能修復了你遇到的錯誤或添加了你想使用的新功能。

Update-Module
檢查/保存模組
在使用模組之前進行審查時,很少使用的一個非常有用的命令是Save-Module
。使用此命令,你可以將模組下載到指定路徑而無需安裝它。
然後,你可以檢查文件,如果模組不是二進制模組,可以打開並查看其代碼。這不僅可以確保模組不會執行任何惡意操作,還可以學習他人如何結構化模組。

Save-Module
在這個例子中,下載了VMware.PowerCLI模組以及所有相依性。以下是顯示在VMware.PowerCLI文件夾中的內容:

這是一個很好的例子,顯示有時模組中會包含非標準的模組文件,例如最終用戶許可協議。
撰寫自己的模組
現在您已經學會了如何與他人的模組進行互動。現在您想要學習如何創建自己的模組,以便能夠開始優化代碼以實現可擴展性。
創建模板文件
首先,您需要創建一個包含所有模組文件的文件夾。在創建容器後,您需要創建模組文件。您必須確保模組文件的名稱與文件夾的名稱相同,否則在嘗試發布模組時,PowerShell將無法正確發現模組。
現在,您還希望使用一個清單,您還需要將其命名為容器和模組文件相同的名稱。
有了容器、模組文件和清單文件,您就有了一個完整的功能模組。您可以將此模組發布到PSRepository並在任何您想要的地方安裝它。但是,由於模組文件是空的,它可能對您沒有太多用處。您仍然可以使用這些文件進行測試發布,以確保您的資源庫正常工作。
註冊一個PSRepository
在發布模組之前,您需要將另一個PSRepository添加到您的會話中。為了測試,您可以使用本地路徑作為您的PSRepository,因為它容易設置和拆除。
通常,如果您要設置一個具有目錄的PSRepository,您需要確保多台計算機可以訪問它。您可以像這樣創建一個本地資源庫:
如果您只從PSRepository下載而不發布,則可以排除PublishLocation
參數。
發布您的模組
由於您已將安裝策略設定為受信任,您將不會收到允許從存儲庫安裝模塊的確認提示。現在您有一個新的PSRepository可用,您可以使用Publish-Module -Name .\Scripts\ATARegistry -Repository LocalRepo
來發布您的模塊。
發布模塊後,您可以使用上面的命令來查找模塊並安裝它。
現在您已安裝了該模塊,您可以使用Get-Module
來查看模塊是否已導入到本地會話中。由於您在清單中未將任何函數添加到FunctionsToExport
數組中,因此ExportedCommands
屬性為空。

添加到模塊中
既然您知道可以發布和安裝模塊,您可以開始為其添加一些功能。您可以添加一個返回註冊表鍵的函數,使其看起來像這樣:
如果您保留清單不變並嘗試上傳新模塊,您將遇到兩個問題。首先,您將收到一個錯誤,指出模塊的版本已存在於存儲庫中。這是因為您尚未在清單文件中更改模塊版本。
導出模塊函數
另一個問題是,在導入模塊後,您仍然看不到ExportedCommands
屬性中的任何函數,因為您尚未將新函數添加到清單中。
儘管您的函數可以在不列在FunctionsToExport
列表中的情況下使用,但這將使其更難以定位。
只要您不定义一个空数组
@()
作为FunctionsToExport
,所有的函数、变量和别名都会默认导出。
要解决这两个问题,您可以像这样更新您的模块文件:
现在您已经向您的模块添加了一个函数,并且更新了清单以反映这些更改,您可以使用与之前相同的命令发布新版本的模块。
在函数导出和Export-ModuleMember之间做决策
PowerShell在导出模块成员时有两个类似的特性。选择其中一个可能更适合您,具体取决于您的需求。
当您希望动态控制导出的函数时,请使用Export-ModuleMember
,因为您可以传递一个要导出的函数列表。通常,在点操作多个单独的函数PS1文件时使用此选项。通过将内部函数分为私有文件夹和可导出函数分为公共文件夹,您可以通过将所有公共函数传递给Export-ModuleMember
函数来轻松导出这些函数。
A few notes about Export-ModuleMember:
- 它会覆盖
FunctionsToExport
的行为,因此如果使用Export-ModuleMember
命令,则FunctionsToExport
不会起作用。 Export-ModuleMember
不会自动导出变量和别名,除非明确定义它们,而FunctionsToExport
会导出这些值。- 可以使用多个
Export-ModuleMember
命令,并且它们会叠加而不是优先。
如果您不希望在功能列表中进行更改,可以使用模块清单中的FunctionsToExport
配置,这样就可以正常工作,而无需显式导出变量和别名。
更新模块
最后一步是在会话中更新模块,以便能够使用更新后的文件。使用Update-Module ATARegistry
下载刚刚发布到存储库的更新。

现在您可以看到您有了模块的新版本,并且可以看到您在清单中定义的函数。
构建帮助内容
之前提到的选项之一是内置于PowerShell的帮助系统。您可能曾经在某个函数上使用过Get-Help
。这些信息可以通过两种主要方式添加。
第一种方式是在函数定义中添加基于注释的帮助。这通常是许多模块编写者实现的方式。另一种方式是使用外部帮助文件。您可以使用Full
参数显示帮助所提供的所有内容。

Get-Help
如您所见,实际上没有太多信息,您所获得的少量信息可能对任何人都没有帮助。
您可以向模块文件添加一些基于注释的帮助,以填充帮助系统中的这些字段。您可以使用Get-Help about_Comment_Based_Help
查阅有关基于注释的帮助的所有选项的信息。
現在,您可以將您的函數更新為以下內容。這是一個常用的幫助參數列表,但這些都是可選的,還可以添加其他參數。
現在您的函數如下所示:
還有一些特殊的幫助參數,例如 .FORWARDHELPTARGETNAME。此選項將所有傳入的幫助請求轉發到其他命令。這可以用於在幫助中顯示多個命令的相同信息的情況下使用。
現在您已經添加了幫助,可以更新模塊清單中的版本,發布新版本,並像之前一樣更新您會話中安裝的版本。
如果您現在查看該函數的幫助,將會看到有更多的信息可用。這是一種很好的方式來包含有關如何使用函數的文檔,尤其是對於對代碼不熟悉且無法快速理解模塊功能的人來說。

Get-Help
在外部的幫助檔案中,新增的資訊是相同的,但該資訊會被放在一個單獨的檔案中,並在函數內部連結。
如果你在AllUsers
模組路徑中查看,你可以看到模組的版本以及你所安裝的所有模組檔案。

如果你回到之前創建的PSRepository路徑C:\Repo,你可以看到一堆NUPKG檔案。每個發布的版本都會有一個檔案。這些是使用Publish-Module
時所發布的壓縮版本。
摘要
一旦你熟悉了PowerShell控制台、PowerShell語言和撰寫腳本,建立自己的模組就是最後一步。模組讓你能夠開始在PowerShell中開發有用的工具。如果以單一目的建立模組,並確保設計和建立正確,隨著時間的推移,你會發現自己寫的程式碼越來越少。你將開始在更多的程式碼中引用你的模組函數,並從那裡建立。
模組函數讓你能夠將你在腳本中重複的程式碼抽象化。它們代表了以後在程式碼中可以隨時呼叫的「標籤」,而不是重新發明輪子,試圖弄清楚以前如何實現你的目標。模組是PowerShell程式碼的最後「封裝」,將相關的程式碼分組在一起,避免浪費時間在已經解決過的問題上。