{"id":1194,"date":"2011-08-24T00:00:00","date_gmt":"2011-08-24T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/further-down-the-rabbit-hole-powershell-modules-and-encapsulation\/"},"modified":"2021-05-11T15:56:25","modified_gmt":"2021-05-11T15:56:25","slug":"further-down-the-rabbit-hole-powershell-modules-and-encapsulation","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/further-down-the-rabbit-hole-powershell-modules-and-encapsulation\/","title":{"rendered":"Further Down the Rabbit Hole: PowerShell Modules and Encapsulation"},"content":{"rendered":"<div id=\"pretty\">\n<h2>Contents<\/h2>\n<p><img decoding=\"async\" class=\"float-right\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1346-alice_cardssmall.jpg\" alt=\"1346-alice_cardssmall.jpg\" \/><\/p>\n<ul>\n<li><a href=\"#first\">Encapsulation<\/a>\n<ul>\n<li><a href=\"#second\">Refactor Inline Code into Functions<\/a><\/li>\n<li><a href=\"#third\">Refactor Functions into Files<\/a><\/li>\n<li><a href=\"#fourth\">Refactor Functions into Modules<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#fifth\">Best Practices for Module Design<\/a>\n<ul>\n<li><a href=\"#sixth\">Extracting Information about Modules<\/a><\/li>\n<li><a href=\"#seventh\">Installing Modules<\/a><\/li>\n<li><a href=\"#eighth\">Associating a Manifest to a Module<\/a><\/li>\n<li><a href=\"#ninth\">Unapproved Verbs<\/a><\/li>\n<li><a href=\"#tenth\">Documenting a Module<\/a><\/li>\n<li><a href=\"#eleventh\">Enhancing Robustness<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#twelfth\">Name Collisions &#8211; Which One to Run?<\/a><\/li>\n<li><a href=\"#thirteenth\">Conclusion<\/a><\/li>\n<\/ul>\n<p class=\"start\">In my previous PowerShell exploration (<a href=\"http:\/\/www.simple-talk.com\/dotnet\/.net-tools\/down-the-rabbit-hole--a-study-in-powershell-pipelines,-functions,-and-parameters\/\">A Study in PowerShell Pipelines, Functions, and Parameters<\/a>) I concentrated on describing how parameters were passed to functions, explaining the bewildering intricacies on both sides of the function interface (the code doing the calling and the code inside the function doing the <em>receiving<\/em>). I didn&#8217;t mention how to go about actually creating a function because it was so simple to do that it could safely be left as an extracurricular exercise. With modules, by contrast, the complexity reverses; it is more intricate to create a module than to use a module, so that is where you are heading now. The first half of this article guides you along the twisted path from raw code to tidy module; the second half introduces a set of best practices for module design.<\/p>\n<h2 id=\"first\">Encapsulation<\/h2>\n<p>As you likely know, <a href=\"http:\/\/en.wikipedia.org\/wiki\/Encapsulation_%28object-oriented_programming%29\">encapsulation<\/a> makes your code more manageable. Encapsulation is the process of separating an interface from its implementation by bundling data and code together and exposing only a well-defined portion of it. The following sections walk you along the road to encapsulation in PowerShell.<\/p>\n<aside>\n<p><em>&#8220;Would you tell me, please, which way I ought to go from here?&#8221; &#8220;That depends a good deal on where you want to get to,&#8221; said the Cat. &#8220;I don&#8217;t much care where &#8211; &#8221; said Alice. &#8220;Then it doesn&#8217;t matter which way you go,&#8221; said the Cat. &#8221; &#8211; so long as I get somewhere,&#8221; Alice added as an explanation. &#8220;Oh, you&#8217;re sure to do that,&#8221; said the Cat, &#8220;if you only walk long enough.&#8221; <\/em><\/p>\n<p>&#8212; Chapter 6, Alice&#8217;s Adventures in Wonderland (Lewis Carroll)<\/p>\n<\/aside>\n<p>\n<img decoding=\"async\" class=\"float-left\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1346-cheshirecatsmall.png\" alt=\"1346-cheshirecatsmall.png\" width=\"210\" \/> <\/p>\n<h3 id=\"second\">Refactor Inline Code into Functions<\/h3>\n<p>Encapsulation encourages you to convert a single code sequence with inordinate detail into a more digestible and simpler piece of code (Figure 1).<\/p>\n<p class=\"illustration\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1346-image001.png\" alt=\"1346-image001.png\" \/><\/p>\n<p class=\"caption\">Figure 1: Refactoring inline code to a function<\/p>\n<p>Refactoring the first example into the second ended up only moving one or two lines of code (depending on how you count it) into the separate Match-Expression function. But look at how much easier it is to comprehend the code! The main program lets a reader of your code observe that Match-Expression uses the given regular expression to find several values from a given string. It does not reveal how-the Match-Expression function hides the details of how the <strong>match<\/strong> operator works. And that&#8217;s great, because your reader does not care. Before you argue the point, consider a different context such as some .NET-supplied function, e.g. <strong>String.Join<\/strong>. Except in rare circumstances you simply do not care about the implementation of <strong>String.Join<\/strong>; you just need to know what it does.<\/p>\n<p>Refactoring to functions is useful and important to do, of course, but there is one cautionary note: if instead of the simple Match-Expression function you have a more complex function that includes several support functions and variables, all of those support objects are polluting your current scope. There is nothing to prevent another part of your script from using one of these support functions that was specifically designed to be used only by Match-Expression (or rather its complex cousin). Or worse, in your zeal to refactor into smaller and smaller functions you might create a function with the same name as a built-in cmdlet; your function would supersede the built-in one. The next section returns to this consideration after a fashion.<\/p>\n<h3 id=\"third\">Refactor Functions into Files<\/h3>\n<p>Now you have this Match-Expression function that came in quite handy in your script. You find it so useful, in fact, that you want to use it in other scripts. Good design practice dictates the DRY principle: <a href=\"http:\/\/en.wikipedia.org\/wiki\/Don%27t_repeat_yourself\">Don&#8217;t Repeat Yourself<\/a>. So rather than copying this function into several other script files, move it into its own file (Expressions.ps1) and reference it from each script. Modify the above example to use dot-sourcing (explained in the <strong>Using Dot Source Notation with Scope<\/strong> section of the help topic <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd315289.aspx\">about_Scopes<\/a>) to incorporate the contents of Expressions.ps1 (Figure 2).<\/p>\n<p class=\"illustration\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1346-image002.png\" alt=\"1346-image002.png\" \/><\/p>\n<p class=\"caption\">Figure 2: Refactoring an inline function to a separate file<\/p>\n<p>The code on the right is exactly equivalent to the code on the left. The elegance of this is that if you want to change the function you have only one piece of code to modify and the changes are automatically propagated everywhere you have referenced the file.<\/p>\n<p><em>Dot-sourcing reads in the specified file just as if it was in the file. <\/em><\/p>\n<h3>Dot-Sourcing Pitfall<\/h3>\n<p>There is, however, a potential problem. As you have just seen, dot-sourcing syntax includes just two pieces: a dot (hence the name!) and a file path. In the example above I show the file path as a dot as well, but there it means <em>current directory<\/em>. The current directory is where you happen to be when you invoke the script; it is <em>not<\/em> tied to the script&#8217;s location at all! Thus, the above only works because I specifically executed the script<em> from the script directory<\/em>. What you need then is a way to tell PowerShell to look for the Expressions.ps1 file in the same directory as your main script-regardless of what your current directory is.<\/p>\n<p>A web search on this question leads you to the seemingly ubiquitous script that originated with <a href=\"http:\/\/blogs.msdn.com\/powershell\/archive\/2007\/06\/19\/get-scriptdirectory.aspx\">this post<\/a> by Jeffrey Snover of the PowerShell team:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">function Get-ScriptDirectory\r\n{\r\n\u00a0\u00a0\u00a0 $Invocation = (Get-Variable MyInvocation -Scope 1).Value\r\n\u00a0\u00a0\u00a0 Split-Path $Invocation.MyCommand.Path\r\n}\r\n<\/pre>\n<p>If you include the above in your script (or in a separate file and dot-source it!) then add this line to your script:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Write-Host (Get-ScriptDirectory)\r\n<\/pre>\n<p>&#8230;it will properly display the directory where your script resides rather than your current directory. <strong>Maybe<\/strong>. <em>The results you get from this function depend on where you call it from!<\/em><\/p>\n<aside>\n<p><em>It is a very inconvenient habit of kittens (Alice had once made the remark) that, whatever you say to them, they always purr. &#8220;If they would only purr for &#8216;yes,&#8217; and mew for &#8216;no,&#8217; or any rule of that sort,&#8221; she had said, &#8220;so that one could keep up a conversation! But how can you talk with a person if they always say the same thing?&#8221; <\/em><\/p>\n<p>&#8211;Alice. Chapter 12, Through the Looking Glass (Lewis Carroll)<\/p>\n<\/aside>\n<p>It failed immediately when I tried it! I was surprised, because I found this code example proliferated far and wide on the web. I soon discovered that it was because I used it differently to Snover&#8217;s example: Instead of calling it at the top-level in my script, I&#8217;d called it from inside another function in a way I refer to as &#8220;nested twice&#8221; in the following table. It took just a simple tweak to make Get-ScriptDirectory more robust: You just need to change from <em>parent<\/em> scope to <em>script<\/em> scope;<strong> -scope 1<\/strong> in the original function definition indicates parent scope and <strong>$script<\/strong> in the modified one indicates script scope.<\/p>\n<pre class=\"lang:powershell theme:vs2012\">function Get-ScriptDirectory\r\n{\r\n\u00a0\u00a0\u00a0 Split-Path $script:MyInvocation.MyCommand.Path\r\n}\r\n<\/pre>\n<p>To illustrate the difference between the two implementations, I created a test vehicle that evaluates the target expression in four different ways (bracketed terms are keys in the table that follows):<\/p>\n<ul>\n<li>Inline code [inline]<\/li>\n<li>Inline function, i.e. function in the main program [inline function]<\/li>\n<li>Dot-sourced function, i.e. the same function moved to a separate .ps1 file [dot source]<\/li>\n<li>Module function, i.e. the same function moved to a separate .psm1 file [module]<\/li>\n<\/ul>\n<p>The first two columns in the table define the scenario; the last two columns display the results of the two candidate implementations of Get-ScriptDirectory. A result of <strong>script<\/strong> means that the invocation correctly reported the location of the script. A result of <strong>module<\/strong> means the invocation reported the location of the module (see next section) containing the function rather than the script that called the function; this indicates a drawback of both implementations such that you cannot put this function in a module to find the location of the calling script. Setting this module issue aside, the remarkable observation from the table is that using the parent scope approach fails most of the time (in fact, twice as often as it succeeds)!<\/p>\n<div>\n<table class=\"table--striped\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>Where Called<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>What Called<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Script Scope<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Parent Scope<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Top Level<\/p>\n<\/td>\n<td valign=\"top\">\n<p>inline<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>error<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\u00a0<\/td>\n<td valign=\"top\">\n<p>inline function<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\u00a0<\/td>\n<td valign=\"top\">\n<p>dot source<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\u00a0<\/td>\n<td valign=\"top\">\n<p>module<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>module<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>module<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Nested once<\/p>\n<\/td>\n<td valign=\"top\">\n<p>inline<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\u00a0<\/td>\n<td valign=\"top\">\n<p>inline function<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>error<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\u00a0<\/td>\n<td valign=\"top\">\n<p>dot source<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>error<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\u00a0<\/td>\n<td valign=\"top\">\n<p>module<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>module<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>module<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Nested twice<\/p>\n<\/td>\n<td valign=\"top\">\n<p>inline<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>error<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\u00a0<\/td>\n<td valign=\"top\">\n<p>inline function<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>error<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\u00a0<\/td>\n<td valign=\"top\">\n<p>dot source<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>script<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>error<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\u00a0<\/td>\n<td valign=\"top\">\n<p>module<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>module<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>module<\/b><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>(You can find my test vehicle code for this in <a href=\"http:\/\/stackoverflow.com\/questions\/801967\/how-can-i-find-the-source-path-of-an-executing-script\/6985381#6985381\">my post<\/a> on StackOverflow.)<\/p>\n<h3>Dot-Sourcing: The Dark Side<\/h3>\n<p>Dot-sourcing has a dark side, too, however. Consider again if instead of the simple Match-Expression function you have a more complex function that includes several support functions and variables. Moving those support functions out of the main file and hiding them (i.e. encapsulating them) in the file you will include with dot-sourcing is clearly a good thing to do. But the problem of dot-sourcing, then, is precisely the same as the benefit:<\/p>\n<p><em>Dot-sourcing reads in the specified file just as if it was in the file.<\/em><\/p>\n<p>That means dot-sourcing pollutes your main file with all of its support functions and variables-it is not actually hiding anything. In fact, the situation is far worse with dot-sourcing than it was with just refactoring in the same file: here the detritus is hidden from <em>you<\/em> (because you no longer see it in your main file) yet it is present and polluting your current scope all the same. But do not despair! The next section provides a way out of this quagmire.<\/p>\n<h3 id=\"fourth\">Refactor Functions into Modules<\/h3>\n<p>A module is nothing more than a PowerShell script with a .psm1 extension instead of a .ps1 extension. But that small change also addresses both of the issues just discussed for dot-sourcing a script. Figure 3 returns to the familiar example again. The contents of Expressions.ps1 and Expressions.psm1 are identical for this simple example. The main program uses the Import-Module cmdlet instead of the dot-sourcing operator.<\/p>\n<p class=\"illustration\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1346-image003.png\" alt=\"1346-image003.png\" \/><\/p>\n<p class=\"caption\">Figure 3: Refactoring code from dot-sourcing to module importation<\/p>\n<p>Notice that the <strong>Import-Module<\/strong> cmdlet is not referencing a file at all; it references a module named <strong>Expressions<\/strong>, which corresponds to the file Expressions.psm1 when it is located under one of these two system-defined locations (<em>See Storing Modules on Disk<\/em> under <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd878324%28v=vs.85%29.aspx\">Windows PowerShell Modules<\/a>):<\/p>\n<div>\n<table class=\"auto-style1\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>Machine-specific<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>User-specific<\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>C:\\Users\\<i>username<\/i>\\Documents\\WindowsPowerShell\\Modules<\/b><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>Thus, the whole issue of current directory and script directory, a problem for dot-sourcing, becomes moot for modules. To use modules you must copy them into one or the other of these system repositories to be recognized by PowerShell. Once deposited you then use the <strong>Import-Module<\/strong> cmdlet to expose its interface to your script. (Caveat: you cannot just put Expressions.psm1 in either repository as an immediate child; you must put it in a subdirectory called Expressions. See the next section for the rules on this interesting topic.)<\/p>\n<p>The second issue with dot-sourcing and inline code was pollution due to &#8220;faux encapsulation&#8221;. A module truly does encapsulate its contents. Thus, you can have as much support code as you want in your module; your main script that imports the module will be able to see only what you want exposed. By default, all functions are exposed. So if you do have some functions that you want to remain private, you have to use explicit exporting instead of the default. Also, if you want to export aliases, variables, or cmdlets, you must use explicit exporting. To explicitly specify what you want to export (and thus what a script using the module can see from an import) use the Export-ModuleMember cmdlet. Thus, to make Expressions.psm1 use explicit exporting, add this line to the file:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Export-ModuleMember \u00a0Match-Expression\r\n<\/pre>\n<h2 id=\"fifth\">Best Practices for Module Design<\/h2>\n<p>Before you launch into creating modules willy-nilly, there are a few more practical things you should know, discussed next.<\/p>\n<h3 id=\"sixth\">Extracting Information about Modules<\/h3>\n<p>Before you can use modules you have to know what you already have and what you can get. <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd819449.aspx\">Get-Module<\/a> is the gatekeeper you need. With no arguments, <strong>Get-Module<\/strong> lists the <em>loaded<\/em> modules. (Once you load a module with <strong>Import-Module<\/strong> you then can use its exported members.) Here is an example:<\/p>\n<pre>ModuleType Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ExportedCommands\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n---------- ----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ----------------\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\nManifest\u00a0\u00a0 Assertions\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0{Set-AbortOnError, Assert-Expression,Set-MaxExpressionDisplayLe...\r\nManifest\u00a0\u00a0 IniFile \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Get-IniFile\u00a0\u00a0\r\nManifest\u00a0\u00a0 Pscx\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {}\r\nScript\u00a0\u00a0\u00a0\u00a0 Test-PSVersion\u00a0\u00a0\u00a0\u00a0 {}\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\nScript\u00a0\u00a0\u00a0\u00a0 TestParamFunctions {}\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\nManifest\u00a0\u00a0 BitsTransfer\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {}\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n<\/pre>\n<p>The module type may be<em> manifest, script,<\/em> or <em>binary<\/em> (more on those later). The exported commands list identifies all the objects that the module writer exported with explicit exports. An empty list indicates default or implicit export mode, i.e. all functions in the module.<\/p>\n<div class=\"note\">\n<p class=\"note\"><strong>Guideline #1: Use explicit exports so Get-Module can let your user know what you are providing<\/strong><\/p>\n<\/div>\n<p><strong>Get-Module<\/strong> has a <strong>ListAvailable<\/strong> parameter to show you what is available to load, i.e. what you have correctly installed into one of the two system repository locations provided earlier. The output format is identical to that shown just above.<\/p>\n<p>The default output of <strong>Get-Module<\/strong> shows just the three properties above, but there are other ones that are important as well. To see what other interesting properties you could extract from <strong>Get-Module<\/strong>, pipe it into the handy <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd315351.aspx\">Get-Member<\/a> cmdlet:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Get-Module | Get-Member\r\n<\/pre>\n<p>Notable properties you find in the output include <strong>Path<\/strong> (the path to the module file), <strong>Description<\/strong> (a brief summary of the module), and <strong>Version<\/strong>. To display these properties with <strong>Get-Module<\/strong>, switch from its implicit use of <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd315255.aspx\">Format-Table<\/a> to explicit use, where you can enumerate the fields <em>you<\/em> want:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Get-Module -ListAvailable | Format-Table Name, Path, Description, Version\r\n<\/pre>\n<p>&nbsp;<\/p>\n<pre>Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Path\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Description\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Version\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\n----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 -----------\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 -------\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\nAssertion\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 C:\\Users\\ms\\Documents\\Wi... Aborting and non-abortin... 1.0\r\nEnhancedChildItem\u00a0\u00a0\u00a0 C:\\Users\\ms\\Documents\\Wi... Enhanced version of Get-... 1.0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\ninifile\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 C:\\Users\\ms\\Documents\\Wi... INI file reader\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 1.0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\nSvnKeywords\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 C:\\Users\\ms\\Documents\\Wi...\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 0.0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\nMetaProgramming\u00a0\u00a0\u00a0\u00a0\u00a0 C:\\Users\\ms\\Documents\\Wi... MetaProgramming Module\u00a0\u00a0\u00a0\u00a0\u00a0 0.0.0.1 \r\nTestParamFunctions\u00a0\u00a0 C:\\Users\\ms\\Documents\\Wi...\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a00.0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\nAppLocker\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 C:\\Windows\\system32\\Wind... PowerShell AppLocker Module 1.0.0.0\r\nBitsTransfer\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 C:\\Windows\\system32\\Wind...\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 1.0.0.0\r\nPSDiagnostics\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 C:\\Windows\\system32\\Wind...\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 1.0.0.0\r\nTroubleshootingPack\u00a0 C:\\Windows\\system32\\Wind... Microsoft Windows Troubl... 1.0.0.0\r\n\r\n<\/pre>\n<p>If you actually want to see the value of some fields, though, particularly longer fields like <strong>Path <\/strong>or <strong>Description<\/strong>, it might behoove you to use <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd347700.aspx\">Format-List<\/a> rather than <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd315255.aspx\">Format-Table<\/a>:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Get-Module -ListAvailable | Format-List Name, Path, Description, Version\r\n<\/pre>\n<p><\/p>\n<pre>Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : Assertion\r\nPath\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : C:\\Users\\ms\\Documents\\WindowsPowerShell\\Modules\\CleanCode\\Assertion\\Assertion.psm1\r\nDescription : Aborting and non-aborting validation functions for testing.\r\nVersion\u00a0\u00a0\u00a0\u00a0 : 1.0\r\n\u00a0\r\nName\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : EnhancedChildItem\r\nPath\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : C:\\Users\\ms\\Documents\\WindowsPowerShell\\Modules\\CleanCode\\EnhancedChildItem\\\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 EnhancedChildItem.psd1\r\nDescription : Enhanced version of Get-ChildItem providing -ExcludeTree, -FullName, -Svn,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 -ContainersOnly, and -NoContainersOnly.\r\nVersion\u00a0\u00a0\u00a0\u00a0 : 1.0\r\n\u00a0\r\netc. . .\r\n<\/pre>\n<p>The <strong>Get-Member<\/strong> cmdlet quite thoroughly tells you what you can learn about a module but if, like me, you occasionally prefer to bore down into the raw details, you can follow the object trail to its source. First, you can determine that the .NET type of an object returned by <strong>Get-Module<\/strong> is called <strong>PSModuleInfo<\/strong> via this command:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">(Get-Module)[0].GetType().Name\r\n<\/pre>\n<p>Lookup <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.management.automation.psmoduleinfo_members%28v=VS.85%29.aspx\">PSModuleInfo on MSDN<\/a> and there you can see that the list of public properties are just what <strong>Get-Member<\/strong> showed you. On MSDN, however, you can dig further. For example, if you follow the links for the ModuleType property, you can drill down to find that the possible values are Binary, Manifest, and Script, as mentioned earlier.<\/p>\n<p>Finally, for loaded modules (i.e. not just <em>installed <\/em>but actually <em>loaded<\/em>) you can explore further with the <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd347726.aspx\">Get-Command<\/a> cmdlet, specifying the module of interest:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Get-Command -Module Assertion\r\n<\/pre>\n<p><\/p>\n<pre>CommandType\u00a0\u00a0\u00a0\u00a0 Name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Definition\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\n-----------\u00a0\u00a0\u00a0\u00a0 ----\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0----------\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\nFunction\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Assert-Expression\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0param($expression, $expected)...\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\nFunction\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-AssertCounts\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0...\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\nFunction\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Set-AbortOnError\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0param([bool]$state)...\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\nFunction\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Set-MaxExpressionDisplayLength\u00a0\u00a0\u00a0\u00a0param([int]$limit = 50)...\r\n<\/pre>\n<p>Again, you can use <strong>Get-Member<\/strong> to discover what other properties <strong>Get-Command<\/strong> could display.<\/p>\n<h3 id=\"seventh\">Installing Modules<\/h3>\n<p>Now that you know how to see what you have installed here are the important points you need to know about installation. As mentioned earlier you install modules into either the system-wide repository or the user-specific repository. Whichever you pick, its leaf node is <strong>Modules<\/strong> so in this discussion I simply use &#8220;Modules&#8221; to indicate the root of your repository. The table shows what <strong>Get-Module<\/strong> and<strong> Import-Module<\/strong> can each access for various naming permutations.<\/p>\n<table class=\"MsoTableLightGridAccent3\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b>#<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Location<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Get-Module ?<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>Import-Module ?<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>1<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>name\\name.psm1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>name<\/p>\n<\/td>\n<td valign=\"top\">\n<p>name<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>2<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>name.psm1<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>X<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>X<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>3<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>namespace\\name\\name.psm1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>name<\/p>\n<\/td>\n<td valign=\"top\">\n<p>namespace\\name<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>4<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p>namespace\\folder\\name\\name.psm1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>name<\/p>\n<\/td>\n<td valign=\"top\">\n<p>namespace\\folder\\name<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p><b>5<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>name\\other-name.psm1<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>X<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b>name\\other-name<\/b><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Standard module installation (line 1 in the table) requires that you copy your module into this directory:<\/p>\n<p>Modules\/module-name\/module-name.psm1<\/p>\n<p>That is, whatever your modules base file name is, the file must be stored in a subdirectory of the same name under Modules. If instead you put it in the Modules root without the subdirectory:<\/p>\n<p>Modules\/module-name.psm1<\/p>\n<p>&#8230;PowerShell will not recognize the module (line 2 in the table)! This peculiar behavior is probably what you would try first, so it is a common source of frustration with modules not being recognized.<\/p>\n<aside>\n<p><em>Alice felt dreadfully puzzled. The Hatter&#8217;s remark seemed to her to have no sort of meaning in it, and yet it was certainly English. &#8220;I don&#8217;t quite understand you,&#8221; she said, as politely as she could.<\/em><\/p>\n<p>&#8211;Alice, Chapter 7, Alice&#8217;s Adventures in Wonderland (Lewis Carroll)<\/p>\n<\/aside>\n<p>Putting a module in the Modules directory is not good enough; only in an eponymous subfolder will it make be recognized by PowerShell.<\/p>\n<p>Line 3 illustrates that you can use namespaces rather than clutter up your Modules root with a hodgepodge of modules from different sources. When you use <strong>Get-Module<\/strong>, though, the default output shows just the name; you must look at the <strong>Path<\/strong> property of <strong>Get-Module<\/strong> if you want to see the namespace as well. If you ask Get-Module to find a particular module, you again provide only the name. However, when you use <strong>Import-Module<\/strong> you specify the path relative to the Modules root.<\/p>\n<p>Note that namespaces are purely a convention you may or may not choose to use; PowerShell has no notion of namespaces per se (at least as of version 2-Dmitry Sotnikov has made a plea via Microsoft Connect to add namespaces in future versions; see <a href=\"https:\/\/connect.microsoft.com\/feedback\/ViewFeedback.aspx?FeedbackID=301052&amp;SiteID=99\">We Need Namespaces!<\/a>).<\/p>\n<p>Line 4 extends the case of line 3, showing that you can make your namespace as nested as you like-as long as your modules end up in like-named leaf directories.<\/p>\n<p>Given the above discourse, here is the next cardinal rule for modules:<\/p>\n<div class=\"note\">\n<p class=\"note\"><strong>Guideline #2: Install a module in an eponymous subdirectory under your Modules root<\/strong><\/p>\n<\/div>\n<p>Line 5 in the table presents an interesting corner case showing what happens if you violate Guideline #2. The module is invisible to Get-Module -ListAvailable yet you can still load it by specifying the differing subdirectory name and module name. This is, of course, not advisable.<\/p>\n<h3 id=\"eighth\">Associating a Manifest to a Module<\/h3>\n<p>The first half of the article showed the progression from inline code to script file to module file. There is a further step &#8211; introducing a manifest file associated with the module file. You need to use a manifest to specify details of your module that may be accessed programmatically. Recall that when discussing <strong>Get-Module<\/strong> one example showed how to get additional properties beyond the default &#8211; including description and version. But in the example&#8217;s output, some entries showed an empty description and a 0.0 version. Both description and version come from the manifest file; a module lacking a manifest has just those default values.<\/p>\n<p>To create a manifest file, simply invoke the <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd819477.aspx\">New-ModuleManifest<\/a> command and it will prompt you to enter property values. If you do this in a standard PowerShell command-line window, you receive a series of prompts for each property. If, on the other hand, you use the <a href=\"http:\/\/powergui.org\/\">PowerGUI script editor<\/a> it presents a more flexible pop-up dialog, as shown in figure 4. I also entered a couple other common properties, author and copyright.<\/p>\n<p class=\"illustration\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1346-image004.png\" alt=\"1346-image004.png\" \/><\/p>\n<p class=\"caption\">Figure 4: New-ModuleManifest dialog from PowerGUI Script Editor<\/p>\n<p>The <strong>ModuleToProcess<\/strong> property must reference your module script file. Upon selecting OK, the dialog closes and the manifest file is created at the location you specified for the <strong>Path<\/strong> property. The path of the manifest file must also follow rule #2, this time with a .psd1 extension. Once the manifest exists, PowerShell now looks to the manifest whenever you reference the module, notably in both the <strong>Get-Module<\/strong> and <strong>Import-Module<\/strong> cmdlets. You can confirm this with <strong>Get-Module<\/strong>: recall that <strong>Get-Module<\/strong> displays the <strong>ModuleType<\/strong> property by default; now you will see it display <strong>Manifest<\/strong> instead of <strong>Script<\/strong> for the <strong>ModuleType<\/strong>.<\/p>\n<div class=\"note\">\n<p class=\"note\"><strong>Guideline #3: Use a manifest so your users can get a version and description of your module<\/strong><\/p>\n<\/div>\n<p>Once you create your manifest, or at any time later, you can use <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd819466.aspx\">Test-ModuleManifest<\/a> to validate it. This cmdlet checks for existence of the manifest and it verifies any file references in the manifest. For more on manifests, see <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd878297%28VS.85%29.aspx\">How to Write a Module Manifest<\/a> on MSDN.<\/p>\n<aside>\n<p><em>&#8220;When I use a word,&#8221; Humpty Dumpty said, in rather a scornful tone, &#8220;it means just what I choose it to mean &#8211; neither more nor less.&#8221; <\/em><\/p>\n<p>&#8212; Chapter 6, Through the Looking Glass (Lewis Carroll)<\/p>\n<p><img decoding=\"async\" class=\"float-left\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/1346-humpty_dumpty.jpg\" alt=\"1346-humpty_dumpty.jpg\" \/><\/aside>\n<h3 id=\"ninth\">Unapproved Verbs<\/h3>\n<p>If you imported the Expressions.psm1 module given earlier, you likely received this warning message:<\/p>\n<p>WARNING: Some imported command names include unapproved verbs which might make them less discoverable. Use the Verbose parameter for more detail or type Get-Verb to see the list of approved verbs.<\/p>\n<p>PowerShell wants to encourage users to use standard naming conventions so it is easier for everybody who uses external modules to know what to expect. Cmdlets and functions should use the convention action-noun (e.g. <strong>Get-Module<\/strong>). PowerShell does not make any guesses about your choice of nouns, but it is particular about your choice of actions. You can see the list of approved actions, as the warning about indicates, by executing the <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/ee407453.aspx\">Get-Verb<\/a> cmdlet.<\/p>\n<p>Note that I use the term action rather than verb in this paragraph, because PowerShell&#8217;s definition of <em>verb <\/em>is rather non-standard(!). Humpty Dumpty really had the right idea &#8211; I use this quote frequently&#8230;<\/p>\n<p>To PowerShell a verb is &#8220;a word that implies an action&#8221;, so a construct such as <strong>New-ModuleManifest<\/strong> qualifies. See <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms714428.aspx\">Cmdlet Verbs<\/a> in MSDN for more details on naming.<\/p>\n<div class=\"note\">\n<p class=\"note\"><strong>Guideline #4: Name your functions following PowerShell conventions<\/strong><\/p>\n<\/div>\n<h3 id=\"tenth\">Documenting a Module<\/h3>\n<p>The help system in PowerShell is a tremendous boon: without leaving the IDE (or PowerShell prompt) you can immediately find out almost anything you care to know about any PowerShell cmdlet (e.g. Get-Help Get-Module) or general topic (e.g. Get-Help about_modules). When you create a module you can easily provide the same level of professional support for your own functions. <em>Implementing<\/em> the help is the easy part; writing <em>your content<\/em> is what takes most of your time.<\/p>\n<p>To implement the integrated help support, you add documentation comments (&#8220;doc-comments&#8221;) to your module script file just like you would with your other favorite programming language. Some IDEs provide great support for adding doc-comments. Visual Studio, for example, with the GhostDoc add-on almost writes the doc-comments for you. Alas, PowerShell does not yet have such a ghost writer available. To do it yourself, start with <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd819489.aspx\">about_Comment_Based_Help<\/a> (which you can also access from the PowerShell prompt by feeding that to <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd347639.aspx\">Get-Help<\/a>!). Scroll down to the <strong>Syntax for Comment-Based Help in Functions<\/strong> section. Note that the page also talks about adding help for the script itself; that applies only to main scripts (ps1 files); it does not apply to modules (psm1 files). What you will see here is that you must add a special comment section that looks like this for each function:<\/p>\n<pre>\u00a0\u00a0 &lt;#\r\n\u00a0\u00a0 .&lt; help keyword&gt;\r\n\u00a0\u00a0 &lt; help content&gt;\r\n\u00a0\u00a0 . . .\r\n\u00a0\u00a0 #&gt;\r\n<\/pre>\n<p>&#8230;and that you can place that in any of three positions relative to your function body. You can then pick your relevant help keywords from the subsequent section, <strong>Comment-Based Help Keywords<\/strong>.<\/p>\n<p>One small annoyance (hard to say if it is a feature or a defect, since it documents it as both in adjoining paragraphs!): for each function parameter, <strong>Get-Hel<\/strong>p displays a small table of its attributes. But the default value is <em>never<\/em> filled in! Here is an example from <strong>Get-Module<\/strong>&#8216;s <strong>ListAvailable<\/strong> parameter:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">-ListAvailable [&lt;SwitchParameter&gt;]\r\n\u00a0\u00a0\u00a0 Gets all of the modules that can be imported into the session. Get-Module\r\n\u00a0\u00a0\u00a0\u00a0 gets the modules in the paths specified by the $env:PSModulePath \r\n\u00a0\u00a0\u00a0\u00a0 environment variable. \r\n\u00a0\u00a0\u00a0 \r\n\u00a0\u00a0\u00a0 Without this parameter, Get-Module gets only the modules that have been \r\n\u00a0\u00a0\u00a0 imported into the session.\r\n\u00a0\u00a0\u00a0 \r\n\u00a0\u00a0\u00a0 Required?\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 false\r\n\u00a0\u00a0\u00a0 Position?\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 named\r\n\u00a0\u00a0\u00a0 Default value\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\n\u00a0\u00a0\u00a0 Accept pipeline input?\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0false\r\n\u00a0\u00a0\u00a0 Accept wildcard characters?\u00a0 false\r\n<\/pre>\n<p>You can see this feature\/issue documented under <strong>Autogenerated Content &gt; Parameter Attribute Table<\/strong>. The documentation is certainly thorough on this point, though, even to the extent of providing a workaround-it suggests you mention your default in your help text. And that is just what all the standard .NET cmdlets do!<\/p>\n<p>PowerShell provides support for help on individual modules, allowing <strong>Get-Help<\/strong> to access your help text, as you have just seen. If you produce <em>libraries<\/em> rather than just<em> individual modules<\/em> you will next be looking for the way to create an API documentation tree that you can supply with your library. Wait for it&#8230; sigh. No, PowerShell does not provide any such took like javadoc for Java or Sandcastle for .NET. Well, I found that rather unsatisfactory so I undertook to create one. My API generator for PowerShell (written in PowerShell, of course!) is in my PowerShell library, scheduled for release in the fourth quarter of 2011. You can find it here on my <a href=\"http:\/\/cleancode.sourceforge.net\/wwwdoc\/APIbookshelf.html\">API bookshelf<\/a>, alongside my libraries in five other languages. As an enthusiastic library builder, I have created similar API generators for Perl (see <a href=\"http:\/\/cleancode.sourceforge.net\/wwwdoc\/software\/Pod2HtmlTree.html\">Pod2HtmlTree<\/a>) and for T-SQL (see <a href=\"http:\/\/cleancode.sourceforge.net\/wwwdoc\/software\/XmlTransform.html\">XmlTransform<\/a>). (Note that the Perl version is Perl-specific while the T-SQL one is my generic XML conversion tool configured to handle SQL documentation, described in <a href=\"http:\/\/www.devx.com\/dbzone\/Article\/36646\">Add Custom XML Documentation Capability To Your SQL Code<\/a>.)<\/p>\n<div class=\"note\">\n<p class=\"note\"><strong>Guideline #5: Add polish to your modules by documenting your functions<\/strong><\/p>\n<\/div>\n<h3 id=\"eleventh\">Enhancing Robustness<\/h3>\n<p>I would be remiss if I did not add a mention, however brief, of an important guideline for any PowerShell script, module or otherwise. Let the compiler help you-turn on strict mode with <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd347614.aspx\">Set-StrictMode<\/a>:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Set-StrictMode -Version Latest\r\n<\/pre>\n<div class=\"note\">\n<p class=\"note\"><strong>Guideline #6: Tighten up your code by enforcing strict mode<\/strong><\/p>\n<\/div>\n<p>It is regrettable that that setting is not on by default.<\/p>\n<h2 id=\"twelfth\">Name Collisions &#8211; Which One to Run?<\/h2>\n<p>If you create a function with the same name as a cmdlet, which one does PowerShell pick? To determine that you need to know the execution precedence order (from <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd347579.aspx\">about_Command_Precedence<\/a>):<\/p>\n<ol>\n<li>Alias<\/li>\n<li>Function<\/li>\n<li>Cmdlet<\/li>\n<li>Native Windows commands<\/li>\n<\/ol>\n<p>If you have two items at the <em>same<\/em> precedence level, such as two functions or two cmdlets with the same name, the most recently added one has precedence. (Hence the desire by some to have namespaces introduced in PowerShell, as mentioned earlier.)<\/p>\n<p>When you add a new item with the same name as another item it may <em>replace<\/em> the original or it may <em>hide<\/em> the original. Defining a function with the same name as an existing cmdlet, for example, hides the cmdlet, but does not replace it; the cmdlet is still accessible if you provide a fully-qualified name. To determine the name, examine the <strong>PSSnapin<\/strong> and <strong>Module<\/strong> properties of the cmdlet:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Get-Command Get-ChildItem | Format-List -property Name, PSSnapin, Module\r\n<\/pre>\n<p><\/p>\n<pre class=\"lang:powershell theme:vs2012\">Name\u00a0\u00a0\u00a0\u00a0 : Get-ChildItem\r\nPSSnapIn : Microsoft.PowerShell.Management\r\nModule\u00a0\u00a0 :\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \r\n<\/pre>\n<p>The fully qualified name, then, for the Get-ChildItem cmdlet is:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Microsoft.PowerShell.Management\\Get-ChildItem\r\n<\/pre>\n<p>To avoid naming conflicts in the first place, import a module with the <strong>Prefix <\/strong>option to the <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd819454.aspx\">Import-Module<\/a> cmdlet. If you have created, for example, a new version of <strong>Get-Date<\/strong> in a <strong>DateFunctions<\/strong> module and run this:<\/p>\n<pre class=\"lang:powershell theme:vs2012\">Import-Module -name DateFunctions -prefix Enhanced\r\n<\/pre>\n<p>&#8230;then your <strong>Get-Date<\/strong> function is now mapped to<strong> Get-EnhancedDate<\/strong>, i.e., the action in the command is affixed with the prefix you specified.<\/p>\n<h2 id=\"thirteenth\">Conclusion<\/h2>\n<p>Modules let you organize your code well and to make your code highly reusable. Now that you are aware of them, you will probably start noticing code smells that shout &#8220;Module!&#8221;. That is, be on the lookout for chunks of code that perform a useful calculation but are generic enough to deserve separating out from your main code. I have found that taking the effort to move generic functionality into a separate module forces me to think about it in isolation and often leads me to find corner cases that I missed in the logic. Also, modularizing lets you then focus more fine-grained and more specific unit tests on that code as well. For further reading, be sure to take a look at the whole section on modules on MSDN at <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd878310%28v=VS.85%29.aspx\">Writing a Windows PowerShell Module<\/a>.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Modules allow you to use standard libraries that extend PowerShell&#8217;s functionality. They are easier to use than to create, but if you get the hang of creating them, your code will be more easily-maintained and re-usable. Let Michael Sorens once more be your guide through PowerShell&#8217;s &#8216;Alice in Wonderland&#8217; world.&hellip;<\/p>\n","protected":false},"author":221868,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[4143,4883,4204,4178,4635],"coauthors":[6802],"class_list":["post-1194","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-net","tag-net-home","tag-net-tools","tag-bi","tag-powershell"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1194","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221868"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1194"}],"version-history":[{"count":20,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1194\/revisions"}],"predecessor-version":[{"id":74834,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1194\/revisions\/74834"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1194"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1194"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1194"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1194"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}