Reputation: 91
A few years ago I put together this function, that can take another Powershell function, and after specifying which of it's parameters is the "loop by" parameter can launch parallel jobs based on the array/collection of that parameter's value, effectively running the function in parallel using start-job. At the time I built it to be used for any kind of function, so I came up with using the dynamic parameter as described below, so that it will take the function's parameter as its own, and then pass those down to the start-job commandlet. (I stripped a lot of function down, with just the bits that matter for my question)
Function Invoke-FunctionAsJob
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$FunctionName,
# name or path of module to load inside worker jobs (.psm1 path)
[Parameter(Mandatory=$false)]
[string]
$CustomModulePath
)
DynamicParam {
if (-not $FunctionName) {
return
}
Write-Verbose "Preparing dynamic parameters"
$Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
if ($CustomModulePath) {
$FullQualifiedModule = $CustomModulePath
Import-module $FullQualifiedModule
$childfunctionParams = (Get-Command -All $FunctionName -FullyQualifiedModule $FullQualifiedModule).Parameters
} else {
Import-module $FullQualifiedModule
$childfunctionParams = (Get-Command -All $FunctionName).Parameters
}
$commonparams = [System.Management.Automation.PSCmdlet]::CommonParameters
foreach ($p in $childfunctionParams.GetEnumerator()) {
#if the variable is not in the common parameter set we add it to this function's parameter dictionary
if ($p.Key -notin $commonparams) {
$paramdata = [ordered]@{
Name = $p.key
Type = $p.Value.ParameterType
Alias = $p.Value.Aliases
ValidateSet = $p.Value.Attributes.GetEnumerator().validvalues
Mandatory = $p.Value.Attributes.GetEnumerator().Mandatory
ParameterSetName = $p.Value.ParameterSets
Position = $p.Value.Attributes.GetEnumerator().Position
ValueFromPipelineByPropertyName = $p.Value.Attributes.GetEnumerator().ValueFromPipelineByPropertyName
DPDictionary = $Dictionary
}
New-DynamicParam @paramdata
}
}
$Dictionary
}
begin {
write-Information "begin block"
}
process {
write-Information "begin block"
}
end {}
Now it's all been working fine as long as the module of the function i wanted to run in parallel, had a single version installed. Now I have this module installed in my personal profile (my documents), and an older version of the module in the system folder.
What happens, and I've narrowed it down to the dynamic parameter block and the "Get-Command" commandlet I'm using, is that when invoke-functionasjob is run, the dynamic parameter block somehow feels the need to load the module in the other system location (in my case the older module). I have no dependency in that module that would explain why the older module loads. Also i've tried loading the old module first. For some reason the newer module gets loaded also.
As you see in code above i've tried using a specific module path, and added that full path to the get-command, which when i run it plain and simple, works, but not in this case. I've also tried to load the module in the dynamic param scriptblock (as you can see I have import-module in there) but still it does not stop powershell for looking for the other folder.
This is on both the VS Code host, and the Powershell core window: Name : Visual Studio Code Host Version : 2024.2.2
Name : ConsoleHost Version : 7.4.2
Any ideas on this would be appreciated.
Upvotes: 0
Views: 13