As noted by commenter, this is not recommended. The module function should be refactored by adding an optional parameter that allows callers to provide a choice programmatically.
If you really can't modify the source of the module, then here are two ways to temporarily override Read-Host.
For testing both solutions I have created the following module file:
module.psm1
Function ModuleFun {
$choice = Read-Host 'Enter (Y)es or (N)o'
"Output from module: $choice"
}
1st solution
The easiest solution is possible when you have Pester installed. I've tested it with Pester 5 but I believe it should work with older Pester versions too. It uses the InModuleScope command of Pester to "hack" a module.
main.ps1
Import-Module $PSScriptRoot\module.psm1
# InModuleScope is provided by Pester.
# Override Read-Host within module scope only.
InModuleScope -ModuleName module -ScriptBlock {
Function Script:Read-Host { $global:ReadHostOverride }
}
# Use Read-Host normally
Read-Host 'Enter something'
# Call module function with overridden Read-Host
$ReadHostOverride = 'Y'
ModuleFun
# Use Read-Host normally again
Read-Host 'Enter something'
# Call module function with different Read-Host override value
$ReadHostOverride = 'N'
ModuleFun
Output:
Enter something: 23
23
Output from module: Y
Enter something: 42
42
Output from module: N
2nd solution
If you don't have Pester available, you may use my function Invoke-WithReadHostOverride to override Read-Host during the execution of a script block that calls a module function.
main.ps1
Import-Module $PSScriptRoot\module.psm1
#------------------------------------------------------------------------------------
# Create an in-memory module which has the advantage that the dot sourcing operator "."
# allows us to run a scriptblock in the caller's scope (outside of the module).
# You may put this code into a separate module file instead.
$null = New-Module {
# Helper function to call the given script block while Read-Host is overridden
Function Invoke-WithReadHostOverride {
[CmdletBinding()]
param (
[Parameter(Mandatory)] [string] $ReadHostOutput,
[Parameter(Mandatory)] [scriptblock] $ScriptBlock
)
# Temporary override of Read-Host command, returns value given by parameter.
Function Global:Read-Host { $ReadHostOutput }
try {
# Everything in ScriptBlock calls our Read-Host override
. $ScriptBlock
}
finally {
# Remove our Read-Host override again
Remove-Item Function:Read-Host
}
}
}
#------------ DEMO CODE --------------------------------------------------------------
# Use Read-Host normally
Read-Host 'Enter something'
# Call module function with overridden Read-Host
Invoke-WithReadHostOverride -ReadHostOutput Y -ScriptBlock {
$choice = ModuleFun
}
$choice # Thanks to the in-memory module trick, $choice has been set in current scope!
# Use Read-Host normally again
Read-Host 'Enter something'
# Call module function with different Read-Host override value
Invoke-WithReadHostOverride -ReadHostOutput N -ScriptBlock {
ModuleFun
}
Output:
Enter something: 23
23
Output from module: Y
Enter something: 42
42
Output from module: N
As usual with such temporary things, it's a good idea to wrap them in try/finally blocks to ensure the cleanup runs even in case of an exception.
The override works, because functions have higher precedence than cmdlets defined in the same session. From the docs:
If you do not specify a path, PowerShell uses the following precedence
order when it runs commands for all items loaded in the current
session:
Alias Function Cmdlet External executable files (programs and
non-PowerShell scripts)