A Module Measurement Scripting Solution
At the end of last month I left you with a PowerShell scripting challenge. This is a great way to flex your scripting muscles and hopefully learn something new. If nothing else, the challenges re-enforces your understanding of PowerShell syntax, language, and concepts that you can apply to anything.
Let me share my solutions for the most recent challenge. My solution is not the only way to achieve the end result, and don't consider this a competition to see how close you can get to my solution. Compare your work against mine and try to understand the differences.
The Basic Challenge
I offered two challenges last month. For the basic challenge I wanted you to find how many modules are installed in each location defined in %PSMODULEPATH%
. This environment variable contains the paths where PowerShell searches for modules. Some of the paths are hard-coded. You'll see different locations depending on your PowerShell version.
PS C:\> $env:PSModulePath
C:\Users\jeff\Documents\PowerShell\Modules;C:\Program Files\PowerShell\Modules;c:\program files\powershell\7\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules
You might have additional locations. Some modules might add locations. And this is a operating system variable so you can easily modify it outside of PowerShell to add other locations.
The challenge is to check each location in the path. Each location is separated by a semi-colon so we can split the string.
PS C:\> $env:PSModulePath -split ";"
C:\Users\jeff\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\Windows\system32\WindowsPowerShell\v1.0\Modules
However, the path separator on non-Windows platforms may be something else. If you wanted to write code that would work cross-platform, you need to discover the path separator character. Fortunately, this is a property you can find in the System.IO.Path
.NET class.
$pathSep = [System.IO.Path]::PathSeparator
Using this gives me the same results.
PS C:\> $pathSep = [System.IO.Path]::PathSeparator
PS C:\> $env:PSModulePath -split $pathSep
C:\Users\jeff\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\7\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\Windows\system32\WindowsPowerShell\v1.0\Modules
And it works cross-platform.
PS /home/jeff> $pathSep = [System.IO.Path]::PathSeparator
PS /home/jeff> $env:PSModulePath -split $pathSep
/home/jeff/.local/share/powershell/Modules
/usr/local/share/powershell/Modules
/opt/microsoft/powershell/Modules
I'll save the locations to a variable.
$locations = $env:PSModulePath -split $pathSep
For this challenge, all you needed to do was count the modules in each location. Each module is stored as a folder in the location.
PS C:\> dir $locations[1] -Directory | Select -first 3
Directory: C:\Program Files\PowerShell\Modules
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 3/20/2025 8:47 AM Microsoft.PowerShell.ConsoleGuiTools
d---- 3/20/2025 8:47 AM Microsoft.PowerShell.Crescendo
d---- 3/20/2025 8:47 AM Microsoft.PowerShell.PSResourceGet
For each location, I can count the number of top-level folders and that will give me the number of modules installed in each location.
$out = Foreach ($Location in $locations) {
#count top-level directories
$count = (Get-ChildItem -Path $Location -Directory -ErrorAction SilentlyContinue).Count
#create custom object output
[PSCustomObject]@{
Location = $Location
Count = $count
PSVersion = $PSVersionTable.PSVersion
Computername = [System.Environment]::MachineName
UserName = [System.Environment]::UserName
}
}
I am setting the ErrorAction preference on Get-ChildItem
to SilentlyContinue. This will suppress any errors for locations with no top-level directories. For each location, I am creating a custom object. Note that I am using .NET references for the user and computer names so that this code will run cross-platform.
Here's my Windows result.
PS C:\> $out | Format-Table
Location Count PSVersion Computername UserName
-------- ----- --------- ------------ --------
C:\Users\jeff\Documents\PowerShell\Modules 1 7.5.1 CADENZA jeff
C:\Program Files\PowerShell\Modules 27 7.5.1 CADENZA jeff
c:\program files\powershell\7\Modules 14 7.5.1 CADENZA jeff
C:\Program Files\WindowsPowerShell\Modules 40 7.5.1 CADENZA jeff
C:\Windows\system32\WindowsPowerShell\v1.0\Modules 86 7.5.1 CADENZA jeff
And here's an Ubuntu result.
PS /home/jeff> $out | Format-Table
Location Count PSVersion Computername UserName
-------- ----- --------- ------------ --------
/home/jeff/.local/share/powershell/Modules 3 7.5.0 Cadenza jeff
/usr/local/share/powershell/Modules 0 7.5.0 Cadenza jeff
/opt/microsoft/powershell/Modules 10 7.5.0 Cadenza jeff
This challenge was only interested in the number of different modules. You might have multiple versions of the same module installed, but they will share the same top-level folder. Finding module by version is part of the advanced challenge.