mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Report error when PowerShell built-in modules are missing (#16628)
This commit is contained in:
+168
-1
@@ -11,7 +11,7 @@ function Add-ModulePath
|
||||
|
||||
if ($Prepend)
|
||||
{
|
||||
$env:PSModulePAth = $Path + [System.IO.Path]::PathSeparator + $env:PSModulePath
|
||||
$env:PSModulePath = $Path + [System.IO.Path]::PathSeparator + $env:PSModulePath
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -131,6 +131,22 @@ function New-TestNestedModule
|
||||
[scriptblock]::Create($newManifestCmd).Invoke()
|
||||
}
|
||||
|
||||
function Get-DesktopModuleToUse {
|
||||
$system32Path = "$env:windir\system32\WindowsPowerShell\v1.0\Modules"
|
||||
$persistentMemoryModule = "PersistentMemory"
|
||||
$remoteDesktopModule = "RemoteDesktop"
|
||||
|
||||
if (Test-Path -PathType Container "$system32Path\$persistentMemoryModule") {
|
||||
return $persistentMemoryModule
|
||||
} elseif (Test-Path -PathType Container "$system32Path\$remoteDesktopModule") {
|
||||
return $remoteDesktopModule
|
||||
} else {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
$desktopModuleToUse = Get-DesktopModuleToUse
|
||||
|
||||
Describe "Get-Module with CompatiblePSEditions-checked paths" -Tag "CI" {
|
||||
|
||||
BeforeAll {
|
||||
@@ -643,6 +659,37 @@ Describe "Additional tests for Import-Module with WinCompat" -Tag "Feature" {
|
||||
'{"Microsoft.PowerShell:ExecutionPolicy": "RemoteSigned", "WindowsPowerShellCompatibilityNoClobberModuleList": ["' + $ModuleName2 + '"]}' | Out-File -Force $ConfigPath
|
||||
& $pwsh -NoProfile -NonInteractive -settingsFile $ConfigPath -c "[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('TestWindowsPowerShellPSHomeLocation', `'$basePath`');Test-${ModuleName2}PSEdition;Test-$ModuleName2" | Should -Be @('Desktop','Core')
|
||||
}
|
||||
|
||||
It "NoClobber WinCompat list in powershell.config is a Desktop-edition module" {
|
||||
if (-not $desktopModuleToUse) {
|
||||
throw 'Neither the "PersistentMemory" module nor the "RemoteDesktop" module is available. Please check and use a desktop-edition module that is under the System32 module path.'
|
||||
}
|
||||
|
||||
## The 'Desktop' edition module 'PersistentMemory' (available on Windows Client) or 'RemoteDesktop' (available on Windows Server) should not be imported twice.
|
||||
$ConfigPath = Join-Path $TestDrive 'powershell.config.json'
|
||||
@"
|
||||
{"Microsoft.PowerShell:ExecutionPolicy": "RemoteSigned", "WindowsPowerShellCompatibilityNoClobberModuleList": ["$desktopModuleToUse"]}
|
||||
"@ | Out-File -Force $ConfigPath
|
||||
$env:PSModulePath = ''
|
||||
|
||||
## The desktop-edition module is listed in the no-clobber list, so we will first try loading a core-edition
|
||||
## compatible version of the module before loading the remote one. The 'system32' module path will be skipped
|
||||
## in this attempt, which is by-design.
|
||||
## If we don't skip the 'system32' module path in this loading attempt, the desktop-edition module will be
|
||||
## imported twice as a remote module, and then 'Remove-Module' won't close the WinCompat session.
|
||||
$script = @"
|
||||
Import-Module $desktopModuleToUse -UseWindowsPowerShell -WarningAction Ignore
|
||||
Get-Module $desktopModuleToUse | ForEach-Object { `$_.ModuleType.ToString() }
|
||||
(Get-PSSession | Measure-Object).Count
|
||||
Remove-Module $desktopModuleToUse
|
||||
(Get-PSSession | Measure-Object).Count
|
||||
"@
|
||||
$scriptBlock = [scriptblock]::Create($script)
|
||||
$results = & $pwsh -NoProfile -NonInteractive -settingsFile $ConfigPath -c $scriptBlock
|
||||
$results[0] | Should -BeExactly 'Script'
|
||||
$results[1] | Should -BeExactly 1
|
||||
$results[2] | Should -BeExactly 0
|
||||
}
|
||||
}
|
||||
|
||||
Context "Tests around PSModulePath in WinCompat process" {
|
||||
@@ -1343,3 +1390,123 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "WinCompat importing should check availablity of built-in modules" -Tag "CI" {
|
||||
BeforeAll {
|
||||
if (-not $IsWindows ) {
|
||||
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
|
||||
$PSDefaultParameterValues["it:skip"] = $true
|
||||
return
|
||||
}
|
||||
|
||||
## Copy the current PowerShell instance to a temp location
|
||||
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "WinCompat"
|
||||
$pwshDir = Join-Path $tempDir "pwsh"
|
||||
$moduleDir = Join-Path $tempDir "Modules"
|
||||
$savedModulePath = $env:PSModulePath
|
||||
|
||||
if (Test-Path $tempDir) {
|
||||
Remove-Item $tempDir -Recurse -Force
|
||||
}
|
||||
|
||||
Write-Host "Making a copy of the running PowerShell instance ..." -ForegroundColor Yellow
|
||||
Copy-Item $PSHOME $pwshDir -Recurse -Force
|
||||
Move-Item $pwshDir\Modules $moduleDir -Force
|
||||
Write-Host "-- Done copying!" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
if (-not $IsWindows) {
|
||||
$global:PSDefaultParameterValues = $originalDefaultParameterValues
|
||||
return
|
||||
}
|
||||
|
||||
$env:PSModulePath = $savedModulePath
|
||||
Remove-Item $tempDir -Recurse -Force
|
||||
}
|
||||
|
||||
It "Missing built-in modules will trigger error instead of loading the non-compatible ones in System32 directory. Running '<Command>'" -TestCases @(
|
||||
@{
|
||||
Command = 'Start-Transcript';
|
||||
FullyQualifiedErrorId = "CouldNotAutoloadMatchingModule";
|
||||
ExceptionMessage = "*'Start-Transcript'*'Microsoft.PowerShell.Host'*'Microsoft.PowerShell.Host'*'Core'*`$PSHOME*'Import-Module Microsoft.PowerShell.Host'*"
|
||||
}
|
||||
@{
|
||||
Command = 'Import-Module Microsoft.PowerShell.Host';
|
||||
FullyQualifiedErrorId = "System.InvalidOperationException,Microsoft.PowerShell.Commands.ImportModuleCommand"
|
||||
ExceptionMessage = "*'Microsoft.PowerShell.Host'*'Core'*`$PSHOME*"
|
||||
}
|
||||
@{
|
||||
Command = 'Import-Module CimCmdlets'
|
||||
FullyQualifiedErrorId = "System.InvalidOperationException,Microsoft.PowerShell.Commands.ImportModuleCommand"
|
||||
ExceptionMessage = "*'CimCmdlets'*'Core'*`$PSHOME*"
|
||||
}
|
||||
@{
|
||||
Command = 'Import-Module Microsoft.PowerShell.Utility'
|
||||
FullyQualifiedErrorId = "System.InvalidOperationException,Microsoft.PowerShell.Commands.ImportModuleCommand"
|
||||
ExceptionMessage = "*'Microsoft.PowerShell.Utility'*'Core'*`$PSHOME*"
|
||||
}
|
||||
) {
|
||||
param(
|
||||
$Command,
|
||||
$FullyQualifiedErrorId,
|
||||
$ExceptionMessage
|
||||
)
|
||||
|
||||
$template = @'
|
||||
try {{
|
||||
{0}
|
||||
}} catch {{
|
||||
$_.FullyQualifiedErrorId
|
||||
$_.Exception.Message
|
||||
}}
|
||||
'@
|
||||
$env:PSModulePath = ''
|
||||
$script = $template -f $Command
|
||||
$scriptBlock = [scriptblock]::Create($script)
|
||||
|
||||
$result = & "$pwshDir\pwsh.exe" -NoProfile -NonInteractive -c $scriptBlock
|
||||
$result | Should -HaveCount 2
|
||||
$result[0] | Should -BeExactly $FullyQualifiedErrorId
|
||||
$result[1] | Should -BeLike $ExceptionMessage
|
||||
}
|
||||
|
||||
It "Attempt to load a 'Desktop' edition module should fail because 'Export-PSSession' cannot be found" {
|
||||
if (-not $desktopModuleToUse) {
|
||||
throw 'Neither the "PersistentMemory" module nor the "RemoteDesktop" module is available. Please check and use a desktop-edition module that is under the System32 module path.'
|
||||
}
|
||||
|
||||
$script = @"
|
||||
try {
|
||||
Import-Module $desktopModuleToUse -ErrorAction Stop
|
||||
} catch {
|
||||
`$_.FullyQualifiedErrorId
|
||||
`$_.Exception.Message
|
||||
}
|
||||
"@
|
||||
$env:PSModulePath = ''
|
||||
$scriptBlock = [scriptblock]::Create($script)
|
||||
$result = & "$pwshDir\pwsh.exe" -NoProfile -NonInteractive -c $scriptBlock
|
||||
$result | Should -HaveCount 2
|
||||
$result[0] | Should -BeExactly "CommandNotFoundException,Microsoft.PowerShell.Commands.ImportModuleCommand"
|
||||
$result[1] | Should -BeLike "*'$desktopModuleToUse'*'Export-PSSession'*'Microsoft.PowerShell.Utility'*"
|
||||
}
|
||||
|
||||
It "When built-in modules are available but not in `$PSHOME module path, things should work" {
|
||||
$env:PSModulePath = ''
|
||||
$result = & "$pwshDir\pwsh.exe" -NoProfile -NonInteractive -c @"
|
||||
`$env:PSModulePath += ';$moduleDir'
|
||||
Import-Module Microsoft.PowerShell.Utility -UseWindowsPowerShell -WarningAction Ignore
|
||||
Get-Module Microsoft.PowerShell.Utility | ForEach-Object ModuleType
|
||||
Get-Module Microsoft.PowerShell.Utility | Where-Object ModuleType -eq 'Manifest' | ForEach-Object Path
|
||||
Get-Module Microsoft.PowerShell.Utility | Where-Object ModuleType -eq 'Script' | ForEach-Object { `$_.ExportedCommands.Keys }
|
||||
"@
|
||||
$result | Should -HaveCount 6
|
||||
$result[0] | Should -BeExactly 'Manifest'
|
||||
$result[1] | Should -BeExactly 'Script'
|
||||
$result[2] | Should -BeExactly "$moduleDir\Microsoft.PowerShell.Utility\Microsoft.PowerShell.Utility.psd1"
|
||||
$result[3] | Should -BeExactly 'Convert-String'
|
||||
$result[4] | Should -BeExactly 'ConvertFrom-String'
|
||||
$result[5] | Should -BeExactly 'CFS'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user