Proper installation of powershell modules for all users - powershell

I've looked at this document, and it lists $Env:ProgramFiles\WindowsPowerShell\Modules (%ProgramFiles%\WindowsPowerShell\Modules) as a good place for installing modules for all users. About naming, it says:
Use the Correct Module Directory Name
A "well-formed" module is a module that is stored in a directory that
has the same name as the base name of at least one file in the module
directory. If a module is not well-formed, Windows PowerShell does not
recognize it as a module.
The "base name" of a file is the name without the file name extension.
In a well-formed module, the name of the directory that contains the
module files must match the base name of at least one file in the
module.
For example, in the sample Fabrikam module, the directory that
contains the module files is named "Fabrikam" and at least one file
has the "Fabrikam" base name. In this case, both Fabrikam.psd1 and
Fabrikam.dll have the "Fabrikam" base name.
C:\Program Files Fabrikam Technologies
Fabrikam Manager
Modules
Fabrikam
Fabrikam.psd1 (module manifest)
Fabrikam.dll (module assembly)
However, I'm still unclear; suppose I have a module composed ABC.psd1 and ABC.psm1. Am I understanding correctly that it should be installed in $Env:ProgramFiles\WindowsPowerShell\Modules\ABC, with the .psd1 and .psm1 directly under that directory?
Looking at some of the microsoft modules, I see, for example:
C:\Program Files
WindowsPowerShell
Modules
AzureRM
5.7.0
AzureRM[.psd1,psm1]
So at least based on the referenced document, it breaks the rule of "well-formed module" since the statement does not seem to allow for that 5.7.0 directory, or does not talk about a recursive search. Is that a hierarchy specifically intended for versioning, is it of the developer's choice? Even for versioning, it doesn't match the recommendations in the above document, for "Using Multiple Versions of a Module" which suggest it is added to the directory name, not as a subdirectory.
The gist of my question is this:
Is there a better explanation on how powershell searches for modules in this directory, with respect to nesting?
Suppose as my product grows, I have a second module DEF.psd1. Should I group under a company or product folder, and will all versions of powershell be happy with this (that is, without me altering the PSModulePath).
e.g.:
C:\Program Files
WindowsPowerShell
Modules
My Company
ABC
ABC[.psd1,psm1]
DEF
DEF[.psd1,psm1]
I am looking to support Powershell 3.0 and later. Also, I am noticing that the powershell ISE module browser uses different rules for searching modules, than powershell itself. For example, using my proposed hierarchy with a company name works fine in powershell, ISE, and user scripts, but breaks the ISE module browser which gives a "cannot find module" error when clicking on "Show details".

Looks like you pretty much have this answered. Not sure why/how the AzureRM module has a versioning folder...but the module was created by Microsoft so maybe they know a loop hole.
In my experience and in the documentation I've read, the proper file structure is what you have in your example, except WITHOUT the 'My Company' folder. How you named the folders and the .psm1/psd1 files (with the same base name) is correct. In fact, for a minimal script based module, all you need is the .psm1 file and it will work (load whatever functions are in the file), although generating a module file with New-ModuleManifest doesn't hurt. As mentioned in the comments, versioning can then be tracked and recognized by PowerShell in the .psd1 file.
Now, I've never put a module in the ProgramFiles folder for all users but I imagine it uses the same rules as the user based folders (they are just paths in an enviroment variable that powershell looks at).
I've also used the link below as a method or template for building PowerShell modules that are easy to maintain:
http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/
You can ignore the parts you aren't interested in or not using (folders for tests or views or using a Git repo)...the base structure works as well as the technique for loading individual functions with the .psm1 file.

Related

PowerShell Module Deployment Duplication

I am using Azure DevOps to deploy PowerShell modules to a server. This release task deploys the modules to the directory C:\Windows\System32\WindowsPowerShell\v1.0\Modules\. I am able to use the modules once they are deployed to this folder successfully.
If I modify one of the modules and re-release it the file in C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ gets updated, however the old version of the module is still used when running from a batch file using pwsh.
I discovered that the module file also exists in the following paths:
C:\Program Files\PowerShell\Modules\
C:\Program Files\PowerShell\6\Modules\
When deploying the new version using Azure DevOps the old version in the above two directories are not updated. Manually updating the module in those locations fixes the problem.
Why is the module file being copied into those two additional paths?
Should those copies be overwritten when a new version of the module is deployed?
What is the correct way of deploying a module in this scenario?
Powershell uses different paths to load modules. Use $env:PSModulePath -split ";" to know which are the paths being used.
The difference between each path is user scope and usage scope (e.g. made for custom modules or windows official modules).
Now, by default, PS looks for the latest version of each module across all the paths. So maybe the old version is being run because at the time you re-deploy. You are not updating the module version in the Module Manifest, so if PS see they are the "same" version it gets the last one loaded on the PSModulePath.
Take a look at this awesome post for more details: Everything you wanted to know about PowerShell's Module Path
Now to your questions.
Why is the module file being copied into those two additional paths?
This could be a server configuration or the script that you are using to deploy.
Should those copies be overwritten when a new version of the module is deployed?
Not necessarily, if the versions are maintained correctly. On the post shared says how to check the versions of each module.

Can the Powershell "using" statement with dynamic module paths?

I have some powershell modules which contain classes I need to instantiate from various powershell scripts. To access the classes from the powershell scripts I am have statements like:
using module "..\..\Library\Mymodule.psm1"
But I dont know ahead of time where in the folder hierarchy my library will be relative to the script.
We dont want to use the standard powershell modules folders because these classes are under source control and deploying to user's folders would be nightmarish.
This is an extremely hard topic to google because, well "using" is used everywhere!
Tried provide multiple possible locations for the same file but of course we get an error for the alternate locations that do not exist:
The specified module 'C:...Mymodule.psm1' was not loaded because no valid module file was found in any module directory.
At line:0 char:0
using module "..\..\Library\Mymodule.psm1"
using module "..\Library\Mymodule.psm1"
using module ".\Library\Mymodule.psm1"
But I'd rather run a function to first determine the correct module path, and then use something like this
using module "$foundModulePath"
Is there any way to dynamically set the path of a module and then "using" it?
I would version my modules in a separate repository and write a function the clones that repository inside the workspace at runtime.
Using tags in that repo - would allow me to control the version of the modules I am loading for each script.
This will make the development of the modules possible, even when a script still has to use the old versions and I don't want to "invest" time in re-writing it , just because I've changed a dependency.
idea no 2.
Gradle is very good in handling messy dependencies lists in big projects.
You can build a gradle script that prepares the workspace and then, from inside of it, execute your powershell script.
See Link here

How do I shim executables with the same names in different directories?

I am creating a Chocolatey package for internal team usage. (In this case, the package is for Microsoft's windows debuggers.)
Windows Debuggers contains two folders, one for 32-bit x86 executables and an x64 folder for 64-bit executables.
The executable names are identical.
x86\adplus.exe
x64\adplus.exe
After installation it looks like the shim created by Chocolatey is indeed starting one of the adplus instances successfully. But sometimes I need the 32-bit version and sometimes I need the 64-bit version.
So here is the question: When there are two identically named executables in different directories, how do I tell Chocolately to create different shims for the executables in each directory?
The short answer is that you can't have two identically named shims in the Chocolatey shim folder ($env:ChocolateyInstall\bin).
A limitation of Windows for a directory is that each file/folder must be a unique name. This is what you are running into. Shims get dropped into the $env:ChocolateyInstall\bin folder, which puts them on the PATH automatically because $env:ChocolateyInstall\bin is on the PATH (it allows folks to install all kinds of things without overloading the PATH environment variables).
You can create an empty file ending in .ignore (e.g x86\adplus.exe.ignore) file next to the one you don't want to be shimmed. This is documented on the wiki. You can even do it programmatically during install based on something like OS architecture.
It sounds like you have a need for one of them sometimes and the other at other times on the SAME machine. I would suggest .ignore files for both files, and likely using Get-BinRoot to push the files to a tools folder (you get to define where the location of this is). Then you can set the process PATH temporarily for whichever one you need and it doesn't persist to the actual path. You can even set one on the path and then override it when you want the other.
Since the automation scripts are just PowerShell, you have all kinds of options here.

Where to put non-essential .pl files in a distribution?

I would like to include a few additional .pl files in my CPAN module. These files are not essential to use the module, but are provide useful functionality/glue when the module is used in some common frameworks and applications.
Currently, I just include the .pl files in a "extras" directory of the distribution. This has the drawback that the files are not installed on make install. Is there a way to include them in the installation and where should they go? (They aren't executables and don't belong in "bin".) Would "share" make sense? Or are these kinds of files usually just not installed and it is left to the user to get them out of the .tgz archive and use as needed?
I use Dist::Zilla to manage my distribution.
I would suggest the following:
If they're actual complete programs or are almost complete, polish then up and make them standalone items that could go into /bin with POD of their own.
If they're utility glue, make a ::Utils module for them to live in and document their usage.
If these are useful code snippets but not something you can install somewhere or are sample usages or handy idioms, create a ::Cookbook all-POD module and include them there with the appropriate illuminating explanation for each one.
I don't know exactly how Dist::Zilla works, but the resulting archive has to be compatible with what ExtUtils::MakeMaker creates.
When you create a module with module-starter, it creates a module template using ExtUtils::MakeMaker. It creates several files and directories like the lib directory where your module lives and the t directories where your tests live.
One thing it doesn't create is a bin directory. However, if you create a bin directory, and you put files under this directory (such as Perl scripts), these files will be installed under the bin directory in your Perl's distribution and linked to /usr/local/bin or /usr/bin. Would this be a good place for your scripts?
I liked #Joe's answer, except that in my case the files were WebWork macros -- individual .pl files that make my module callable from WebWork end-user's code. So they don't fit under any of the categories discussed here, and as .pl files can't be made a module.
This is what I ended up doing:
put all .pl macro files into 'extras/WebWork' in the distribution.
add to "dist.ini" file a [ShareDir] stanza with dir = extras property.
now the WebWork admin can install my distribution from CPAN and then use perl -MFile::ShareDir -e 'print File::ShareDir::dist_dir("Statistics-R-IO")' to find the macros and make them available in WebWork.

Must Powershell modules be placed in separate folders?

Using PowerShell 2.0
According to what I've read online, user-created powershell modules must each reside in their own directory. For example if I create a module called MyModule.psm1 it must reside in a folder called MyModule and reside in any directory listed in $env:PSModulePath.
If I have many modules for a project, it seems silly to me to have to create a separate folder for each one. Is this really necessary? Why? Any elegant way around it?
If you want to just have the .psm1 file, you can import it by giving the path to the file itself rather than just the name of the module.
For example:
import-module c:\mymodules\folder\themodule.psm1
With this technique you can have as many modules as you want in the same folder. I don't know that I'd recommend this, but it does work.
PowerShell modules can consist of more than one file, for instance for providing multi-language help or by splitting the functionality into several files. So yes, it's necessary.
It's not ideal, but you could create junctions (MSDN) using Sysinternals junction.exe. That would allow you to store all of the actual data in one folder, but reference it using different paths.
[MSDN] Hard Links and Junctions
Assume that you had three (3) stand-alone .psm1 module files in a folder named Modules in your $env:UserProfile\Documents\WindowsPowerShell\Modules directory.
You can use several junction.exe commands to create junctions (links) that point to the "real" Modules folder on the filesystem.
$ModulePath = '{0}\Modules' -f (Split-Path -Path $Profile.CurrentUserCurrentHost -Parent);
junction.exe $ModulePath\Foo $ModulePath\Modules;
junction.exe $ModulePath\Bar $ModulePath\Modules;
junction.exe $ModulePath\Trevor $ModulePath\Modules;
Here is what it looks like when you navigate into one of the junctions, for example, Foo.
As far as Windows PowerShell is concerned, you are in the Foo directory, which matches the Foo.psm1 file name. It should ignore the rest of the files in that folder.
Now, you can run Get-Module -ListAvailable, and you should see a list of the modules in your user module directory.