Getting PowerShell Core to run on Linux was easy enough. As I’m running Kubuntu 18.10 it’s a simple case of using snapd.

However, I didn’t expect running PowerShell Core on Linux to be problem free. And it wasn’t. After installing my own PsTodoTxt module from the PowerShell Gallery, PowerShell couldn’t find it.

I installed the module as normal, trusting the PowerShell Gallery first to stop the prompts:

PS> Set-PSRepository -Name PSGallery -InstallationPolicy Trusted

PS> Install-Module -Name PsTodoTxt -Scope CurrentUser -Verbose

VERBOSE: Using the provider 'PowerShellGet' for searching packages.
VERBOSE: The -Repository parameter was not specified.  PowerShellGet will use all of the registered repositories.
VERBOSE: Getting the provider object for the PackageManagement Provider 'NuGet'.
VERBOSE: The specified Location is 'https://www.powershellgallery.com/api/v2' and PackageManagementProvider is 'NuGet'.
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='pstodotxt'' for ''.
VERBOSE: Total package yield:'1' for the specified package 'pstodotxt'.
VERBOSE: Performing the operation "Install-Module" on target "Version '1.2.0' of module 'PsTodoTxt'".
VERBOSE: The installation scope is specified to be 'currentuser'.
VERBOSE: The specified module will be installed in '/home/paul/.local/share/powershell/Modules'.
VERBOSE: The specified Location is 'NuGet' and PackageManagementProvider is 'NuGet'.
VERBOSE: Downloading module 'PsTodoTxt' with version '1.2.0' from the repository 'https://www.powershellgallery.com/api/v2'.
VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PsTodoTxt'' for ''.
VERBOSE: InstallPackage' - name='PsTodoTxt', version='1.2.0',destination='/tmp/997376539'
VERBOSE: DownloadPackage' - name='PsTodoTxt', version='1.2.0',destination='/tmp/997376539/PsTodoTxt.1.2.0/PsTodoTxt.1.2.0.nupkg', uri='https://www.powershellgallery.com/api/v2/package/PsTodoTxt/1.2.0'
VERBOSE: Downloading 'https://www.powershellgallery.com/api/v2/package/PsTodoTxt/1.2.0'.
VERBOSE: Completed downloading 'https://www.powershellgallery.com/api/v2/package/PsTodoTxt/1.2.0'.
VERBOSE: Completed downloading 'PsTodoTxt'.
VERBOSE: InstallPackageLocal' - name='PsTodoTxt', version='1.2.0',destination='/tmp/997376539'
VERBOSE: Validating the 'PsTodoTxt' module contents under '/tmp/997376539/PsTodoTxt.1.2.0' path.
VERBOSE: Test-ModuleManifest successfully validated the module manifest file '/tmp/997376539/PsTodoTxt.1.2.0'.
VERBOSE: Module 'PsTodoTxt' was installed successfully to path '/home/paul/.local/share/powershell/Modules/PsTodoTxt/1.2.0'.

Note the very last line that contains the path to where the module is installed: /home/paul/.local/share/powershell/Modules/PsTodoTxt/1.2.0. This will be useful later.

I then try to import the module:

PS> Import-Module pstodotxt

import-module : The specified module 'pstodotxt' was not loaded because no valid module file was found in any module directory.
At line:1 char:1
+ import-module pstodotxt
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ResourceUnavailable: (pstodotxt:String) [Import-Module], FileNotFoundException
+ FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand

But we just installed it. So why can’t PowerShell see it? Let’s make sure that the install path is one of the paths that PowerShell searches (stored in $env:PSModulePath):

PS> $env:PSModulePath -split ':'

/home/paul/.local/share/powershell/Modules
/usr/local/share/powershell/Modules
/snap/powershell/17/opt/powershell/Modules

The install path of the module is the first in the list, so PowerShell is looking in the right place. Let’s try getting a list of all installed modules:

# ExportedCommands column removed for brevity
> Get-Module -ListAvailable

    Directory: /home/paul/.local/share/powershell/Modules

ModuleType Version    Name                                PSEdition
---------- -------    ----                                ---------
Script     5.4.2      InvokeBuild                         Desk
Script     4.6.0      Pester                              Desk


    Directory: /snap/powershell/17/opt/powershell/Modules


ModuleType Version    Name                                PSEdition
---------- -------    ----                                ---------
Manifest   1.2.2.0    Microsoft.PowerShell.Archive        Desk
Manifest   6.1.0.0    Microsoft.PowerShell.Host           Core
Manifest   6.1.0.0    Microsoft.PowerShell.Management     Core
Manifest   6.1.0.0    Microsoft.PowerShell.Security       Core
...
# deleted rest for brevity

The module is not appearing in the list.

I had a thought. My module does not declare, in the manifest file, which CompatiblePSEditions it supports. Maybe for PowerShell Core you need to explicitly state it? But then, Pester and InvokeBuild do not specify it, and they appear. So I was a bit stumped at this stage.

So I went back into the path where the module is installed:

PS> ls ~/.local/share/powershell/Modules/PsTodoTxt/1.2.0

en-US  PSGetModuleInfo.xml  PSTodoTxt.Format.ps1xml  PSTodoTxt.psd1  pstodotxt.psm1

After staring at this for a while I noticed that the folder the module is installed in, PsTodoTxt is different to the manifest file (which is what Get-Module would be looking for).

But it’s not different in the Windows sense.

NTFS through Windows is case insensitive (although with the Windows Subsystem For Linux you can force case sensitivity). So PsTodoTxt is the same as PSTodoTxt or any other combination of letter case.

But Linux file systems are case sensitive so PsTodoTxt is not the same as PSTodoTxt

So assuming that PowerShell is looking for a manifest file that is named exactly the same as the module folder, one of them needs to be renamed. So I renamed the manifest file to PsTodoTxt.psd1 and check for the module again:

> Rename-Item /home/paul/.local/share/powershell/Modules/PsTodoTxt/1.2.0/PSTodoTxt.psd1 `
    PsTodoTxt.psd1

# ExportedCommands column removed for brevity
> Get-Module -ListAvailable

    Directory: /home/paul/.local/share/powershell/Modules

ModuleType Version    Name                                PSEdition
---------- -------    ----                                ---------
Script     5.4.2      InvokeBuild                         Desk
Script     4.6.0      Pester                              Desk
Script     1.2.0      PsTodoTxt                           Desk

    Directory: /snap/powershell/17/opt/powershell/Modules

ModuleType Version    Name                                PSEdition
---------- -------    ----                                ---------
Manifest   1.2.2.0    Microsoft.PowerShell.Archive        Desk
Manifest   6.1.0.0    Microsoft.PowerShell.Host           Core
Manifest   6.1.0.0    Microsoft.PowerShell.Management     Core
Manifest   6.1.0.0    Microsoft.PowerShell.Security       Core
...
...

And the module now imports without issue!

So while I’ve fixed the issue here, it will reoccur the next time I install any version of this module in the future.

I’d prefer to have the module itself be renamed to PSTodoTxt but it is stored in the PowerShell Gallery as PsTodoTxt and I’m not sure if I can even change the case of it in there. So the easiest solution is to change the filename format for the manifest file.

But that’s not all I need to change:

  • Because the build script uses the project folder name for the name of the script module, it usually ends up in lower case (as that’s how I clone it to the local file system). I need to manually set that with the correct case using $PSBPreference.General.ModuleName = 'PsTodoTxt';

  • And to keep things consistent, I want to rename the case for the .ps1xml file. But the value in the manifest FormatsToProcess field needs to match the filename exactly so I need to update that too.

None of this is strictly a PowerShell problem. Case insensitivity is a thing in the Linux world whereas it’s not in the Windows world so you need to pay attention to the case of filenames and paths. So while as a Linux guru you may be sitting there and thinking ‘pfft I would have known that’, remember it’s not immediately obvious to techies from the Linux world.

Paul Broadwith avatar
Paul Broadwith

Paul is a Senior Technology Engineer with 25 years experience in finance, government, manufacturing and services industries. He is a father, Cloud and Datacenter MVP, coder and loves to automate everything.

comments powered by Disqus