From 55612f7a4e2a3e7b7427cda75452ea12b8427ccc Mon Sep 17 00:00:00 2001 From: Bryan Eargle Date: Mon, 5 Mar 2018 22:31:51 -0600 Subject: [PATCH 1/4] Add support to import module(s) from custom path. Add parameter `ModulesFromPathToImport` that accepts a single path, from which module(s) will be imported into background runspace jobs. Modules in this path are not required to be listed by name, and the path does not have to exist in `$env:PSModulePath`. --- PoshRSJob/Public/Start-RSJob.ps1 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/PoshRSJob/Public/Start-RSJob.ps1 b/PoshRSJob/Public/Start-RSJob.ps1 index 336a1e6..c401ae9 100644 --- a/PoshRSJob/Public/Start-RSJob.ps1 +++ b/PoshRSJob/Public/Start-RSJob.ps1 @@ -36,6 +36,11 @@ Function Start-RSJob { .PARAMETER ModulesToImport A collection of modules that will be imported into the background runspace job. + .PARAMETER ModulesFromPathToImport + This is the path containing modules that will be imported into the background runspace job. Must be a single + path (i.e. not a collection of paths) containing one or more modules organized in subdirectories. This is + available on PowerShell V3 and above. + .PARAMETER PSSnapinsToImport A collection of PSSnapins that will be imported into the background runspace job. @@ -180,6 +185,8 @@ Function Start-RSJob { [parameter()] [Alias('ModulesToLoad')] [string[]]$ModulesToImport, + [Alias('ModulesFromPathToLoad')] + [string]$ModulesFromPathToImport, [parameter()] [Alias('PSSnapinsToLoad')] [string[]]$PSSnapinsToImport, @@ -227,6 +234,10 @@ Function Start-RSJob { [void]$InitialSessionState.ImportPSModule($ModulesToImport) } + If ($PSBoundParameters['ModulesFromPathToImport']) { + [void]$InitialSessionState.ImportPSModulesFromPath($ModulesFromPathToImport) + } + If ($PSBoundParameters['PSSnapinsToImport']) { ForEach ($PSSnapin in $PSSnapinsToImport) { [void]$InitialSessionState.ImportPSSnapIn($PSSnapin, [ref]$Null) From 59e238565aad8020b91d320351364fab975ec736 Mon Sep 17 00:00:00 2001 From: Bryan Eargle Date: Tue, 6 Mar 2018 23:00:20 -0600 Subject: [PATCH 2/4] Validation of custom module(s) path to import. When the '-ModulesFromPathToImport' parameter is passed for 'Start-RSJob', it should handle either a full and relative path name. Also raise an exception if the module(s) path is not found, which means none of the module(s) are imported in the runspace job. An exception prevents the runspace jobs from starting, as any dependent functions referenced inside the 'ScriptBlock' parameter would not be found. --- PoshRSJob/Public/Start-RSJob.ps1 | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/PoshRSJob/Public/Start-RSJob.ps1 b/PoshRSJob/Public/Start-RSJob.ps1 index c401ae9..7c53af9 100644 --- a/PoshRSJob/Public/Start-RSJob.ps1 +++ b/PoshRSJob/Public/Start-RSJob.ps1 @@ -41,6 +41,8 @@ Function Start-RSJob { path (i.e. not a collection of paths) containing one or more modules organized in subdirectories. This is available on PowerShell V3 and above. + Either a full or relative path name is supported. + .PARAMETER PSSnapinsToImport A collection of PSSnapins that will be imported into the background runspace job. @@ -235,7 +237,22 @@ Function Start-RSJob { } If ($PSBoundParameters['ModulesFromPathToImport']) { - [void]$InitialSessionState.ImportPSModulesFromPath($ModulesFromPathToImport) + # InitialSessionState.ImportPSModulesFromPath() expects full name + # of the modules path, but we should handle both full and relative + # names + $ModulePath = $null; + Write-Verbose "Resolving path of modules to import: $ModulesFromPathToImport" + Try { + # Handle potential relative name and verify existence + $ModulePath = Resolve-Path -Path $ModulesFromPathToImport; + Write-Verbose "Found full name for path of modules to import: $ModulePath" + } + Catch { + Throw "Cannot find module path: $($_.Exception.Message)" + } + Write-Verbose "Adding modules from path to initial session state" + Write-Debug "Calling InitialSessionState.ImportPSModulesFromPath($ModulePath)" + [void]$InitialSessionState.ImportPSModulesFromPath($ModulePath) } If ($PSBoundParameters['PSSnapinsToImport']) { From 2ddda3e270fd0fbb91e12b68a476d2126de54615 Mon Sep 17 00:00:00 2001 From: Bryan Eargle Date: Tue, 6 Mar 2018 23:33:57 -0600 Subject: [PATCH 3/4] Fix exception handling in module path validation. `Resolve-Path` fires a non-terminating error when it can't find a specified path, which means the error isn't caught for handling in a Catch block. Make `Resolve-Path` raise a terminating error, so we can re-throw to add context to the original exception message. --- PoshRSJob/Public/Start-RSJob.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PoshRSJob/Public/Start-RSJob.ps1 b/PoshRSJob/Public/Start-RSJob.ps1 index 7c53af9..28bb6e5 100644 --- a/PoshRSJob/Public/Start-RSJob.ps1 +++ b/PoshRSJob/Public/Start-RSJob.ps1 @@ -244,7 +244,7 @@ Function Start-RSJob { Write-Verbose "Resolving path of modules to import: $ModulesFromPathToImport" Try { # Handle potential relative name and verify existence - $ModulePath = Resolve-Path -Path $ModulesFromPathToImport; + $ModulePath = Resolve-Path -Path $ModulesFromPathToImport -ErrorAction 'Stop'; Write-Verbose "Found full name for path of modules to import: $ModulePath" } Catch { From 08c3641adb7209ae15d245820f881a673a12d6d9 Mon Sep 17 00:00:00 2001 From: Bryan Eargle Date: Sat, 24 Mar 2018 16:33:43 -0500 Subject: [PATCH 4/4] Consolidate module import into single parameter. Remove separate '-ModulesFromPathToImport' parameter, adding this functionality to existing '-ModulesToImport' parameters. This means '-ModulesToImport' can accept a collection containing: - Installed module names - Path (full or relative) containing one or more modules - Path (full or relative) to a module manifest, script module, or binary module file Purpose of consolidation is to simplify module import for the caller; They should have the flexibility to pass a root module path for recursive import, or be able to specify modules similar to `Import-Module`, with minimal preprocessing of the parameter collection. --- PoshRSJob/Public/Start-RSJob.ps1 | 64 ++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/PoshRSJob/Public/Start-RSJob.ps1 b/PoshRSJob/Public/Start-RSJob.ps1 index 28bb6e5..8d1bc03 100644 --- a/PoshRSJob/Public/Start-RSJob.ps1 +++ b/PoshRSJob/Public/Start-RSJob.ps1 @@ -34,14 +34,11 @@ Function Start-RSJob { Number of concurrent running runspace jobs which are allowed at a time. .PARAMETER ModulesToImport - A collection of modules that will be imported into the background runspace job. + A collection of modules that will be imported into the background runspace job. Collection can include: - .PARAMETER ModulesFromPathToImport - This is the path containing modules that will be imported into the background runspace job. Must be a single - path (i.e. not a collection of paths) containing one or more modules organized in subdirectories. This is - available on PowerShell V3 and above. - - Either a full or relative path name is supported. + - Name of an installed module + - Path (either full or relative) containing one or more modules + - Path (either full or relative) to a module manifest, script module, or binary module file .PARAMETER PSSnapinsToImport A collection of PSSnapins that will be imported into the background runspace job. @@ -187,8 +184,6 @@ Function Start-RSJob { [parameter()] [Alias('ModulesToLoad')] [string[]]$ModulesToImport, - [Alias('ModulesFromPathToLoad')] - [string]$ModulesFromPathToImport, [parameter()] [Alias('PSSnapinsToLoad')] [string[]]$PSSnapinsToImport, @@ -233,26 +228,39 @@ Function Start-RSJob { $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() If ($PSBoundParameters['ModulesToImport']) { - [void]$InitialSessionState.ImportPSModule($ModulesToImport) - } - - If ($PSBoundParameters['ModulesFromPathToImport']) { - # InitialSessionState.ImportPSModulesFromPath() expects full name - # of the modules path, but we should handle both full and relative - # names - $ModulePath = $null; - Write-Verbose "Resolving path of modules to import: $ModulesFromPathToImport" - Try { - # Handle potential relative name and verify existence - $ModulePath = Resolve-Path -Path $ModulesFromPathToImport -ErrorAction 'Stop'; - Write-Verbose "Found full name for path of modules to import: $ModulePath" - } - Catch { - Throw "Cannot find module path: $($_.Exception.Message)" + # Import module(s) by name, path, or single path from which all + # modules need to be imported + Write-Verbose "Importing modules: $($ModulesToImport -join '; ')" + ForEach ($Module in $ModulesToImport) { + Write-Verbose "Resolving module: $Module" + Try { + # This helps determine how to import (by path or name) + # and handles potentially relative path + $ModulePath = Resolve-Path -Path $Module -ErrorAction Stop + # Strip trailing slash for ImportPSModulesFromPath + # compatibility + $ModulePath = $ModulePath.Path.TrimEnd('\') + Write-Verbose "Found module(s) path to import: $ModulePath" + } + Catch { + # Assume it's the name of an installed module + Write-Verbose "Importing module by name: $Module" + Write-Debug "Calling InitialSessionState.ImportPSModule($Module)" + [void]$InitialSessionState.ImportPSModule($Module); + } + If (Test-Path -Path $ModulePath -PathType Container) { + # It's a path containing one or more modules + Write-Verbose "Importing module(s) by path: $ModulePath" + Write-Debug "Calling InitialSessionState.ImportPSModulesFromPath($ModulePath)" + [void]$InitialSessionState.ImportPSModulesFromPath($ModulePath); + } + Else { + # It's a module manifest, script, or binary file + Write-Verbose "Found module file to import: $ModulePath" + Write-Verbose "Calling InitialSessionState.ImportPSModule($ModulePath)" + [void]$InitialSessionState.ImportPSModule($ModulePath); + } } - Write-Verbose "Adding modules from path to initial session state" - Write-Debug "Calling InitialSessionState.ImportPSModulesFromPath($ModulePath)" - [void]$InitialSessionState.ImportPSModulesFromPath($ModulePath) } If ($PSBoundParameters['PSSnapinsToImport']) {