I have a PowerShell module that targets both Windows PowerShell 5.1 and PowerShell Core 6.0. On the Windows PowerShell (Desktop edition) side, it has an additional dependency on .NET Framework 4.7.1 in order to work properly.
When I originally authored the module, I thought it was sufficient to use the DotNetFrameworkVersion
entry in the module manifest thinking that PowerShell would enforce that minimum.
DotNetFrameworkVersion key
Specifies the minimum version of the .NET Framework that is required by the module.
I have since learned there is no enforcement and I'm not even sure what the field is used by. In any case, I'm trying to reduce the support issues that get filed when people try using the module without the minimum .NET Framework version installed.
In a recent version, I added some code in the module's PSM1 file that would check the .NET Framework version when the module is loaded and throw an error if the minimum version wasn't found.
This seems to work great, but only if you manually import the module with Import-Module
. If you just try to run one of the module functions and let PowerShell auto-loading do the import, the import error is suppressed and you instead get a relatively generic error such as:
Get-Blah : The 'Get-Blah' command was found in the module 'MyModule', but the module could not be loaded. For more information, run 'Import-Module MyModule'.
I want to believe users would read the message, do what it says by importing the module manually, and then read/understand the real error message. But I've already had a user submit an issue to the contrary.
So now I'm wondering. Is there a better way to go about this? Do I move my version check code out of the PSM1 and into each public function? Is there something simpler I'm missing?
I too just ran into this, and found it frustrating. I read the documentation linked above to where Microsoft documents how to get the .NetFramework client installed version using C# and PowerShell code. I wrote this script, which you can execute on another system:
$Url = 'https://gist.githubusercontent.com/ChrisLynch
HPE/e2f276eadd5ec96c2c2e5b5835a444eb/raw/05a2d1c98839d3c253a66fb6469c840fec9ea496/Get-InstalledDotNetFramework.ps1'
iex (([System.Net.WebClient]::new()).DownloadString($Url))
This is the solution I came up with that is in the header of my PSM1:
if ($PSVersionTable.PSVersion -match '5.1')
{
$ReleaseKey = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\' | Get-ItemPropertyValue -Name Release
if ($ReleaseKey -lt 461808)
{
[System.String]$Exception = 'InvalidOperationException'
[System.String]$ErrorId = 'UnableToLoadModuleMissingDependancy'
[System.Object]$TargetObject = 'Import-Module ModuleName'
[System.Management.Automation.ErrorCategory]$ErrorCategory = 'ResourceUnavailable'
[System.String]$Message = 'The library is unable to load due to this sytem missing the required .NetFramework 4.7.2 client. Please visit https://go.microsoft.com/fwlink/?LinkId=863265 to download the .NetFramework 4.7.2 Offline Installer.'
throw [Management.Automation.ErrorRecord]::new((New-Object $Exception $Message), $ErrorID, $ErrorCategory, $TargetObject)
}
}
So when a client attempts to import the module with say an older .NetFramework installed, the following exception is thrown and the module fails to load:
Import-Module: The library is unable to load due to this sytem missing the required .NetFramework 4.7.2 client. Please visit https://go.microsoft.com/fwlink/?LinkId=863265 to download the
.NetFramework 4.7.2 Offline Installer.
At line:1 char:1
+ ipmo hponeview.500
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (Import-Module ModuleName:String) [Import-Module], InvalidOperationException
+ FullyQualifiedErrorId : UnableToLoadModuleMissingDependancy,Microsoft.PowerShell.Commands.ImportModuleCommand
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With