我正在开发一个自定义PowerShell模块,我想在与另一台计算机的远程会话的上下文中使用它。以下代码(显然不起作用)解释了我想要实现的目标:
import-module .\MyCustomModule.psm1
$session = new-pssession -computerName server01
invoke-command -session $session -scriptblock {
<# use function defined in MyCustomModule here #>
}
第一个问题是,是否完全可以实现这种情况?我的意思是我只希望我的自定义模块实际存在于我的机器上,而不是远程服务器上。
我找到this thread,但我没有管理它 - 它不允许从远程机器创建一个会话回到本地会话。可能,我面对在该帖子的评论中提到的配置限制......此外,作者提到了对我的解决方案至关重要的性能影响......
如果那可能,那怎么样?
PowerShell的版本目前不是约束 - 如果解决方案仅在PS 3.0中可用 - 我可以忍受这个。
答案 0 :(得分:41)
对这个问题有一些很好的评论,我花了一些时间研究解决问题的各种方法。
首先,我最初要求的是不可能的。我的意思是,如果你采用模块方式,那么模块应该实际存在于目标机器上,以便能够Import-Module
进入远程会话。
为了进一步抽象我的问题,我正在尝试为产品部署创建一个可重用的基于PowerShell的框架。这将是一种推送方式的部署,这意味着我们鼓励人们在本地计算机上运行一些脚本以部署到某个远程服务器。就我所研究的地区而言,有两种可能的方式对常识很友好。
要遵循的流程:
*.psm1
)PSModulePath
变量以包含新模块位置Invoke-Command -Session $s -ScriptBlock {...}
Import-Module CustomModule
开始 - 它将搜索远程计算机上的CustomModule
,显然会找到它以下是喜欢这种方法的理由:
以下内容非常重要:
xcopy
。此外,交付机制应该支持升级/降级和(最好)多实例安装,但这与我的任务比一般问题更相关要遵循的流程:
Invoke-Command -Session $s -FilePath .\myscript.ps1
将脚本中定义的函数加载到远程会话Invoke-Command -Session $s -ScriptBlock {...}
并参考您的自定义函数 - 它们将在会话中以下是这种方法的优点:
当然,这并不理想:
最后,我应该说远程机器仍然需要为远程处理做好准备。这就是我的意思:
Set-ExecutionPolicy Unrestricted
Enable-PSRemoting
答案 1 :(得分:7)
这是另一种方法:在远程会话中重新创建模块,而不复制任何文件。
我没有尝试处理模块之间的依赖关系,但这似乎适用于简单的自包含模块。它依赖于本地会话中可用的模块,因为这使得确定导出更容易,但是通过一些额外的工作,它也可以用于模块文件。
+--------------++--------------+
| msisdn || id |
+--------------++--------------+
| 919999999999 || 2323232 |
| 918888888888 || 2323231 |
| 917777777777 || 2323233 |
+--------------++--------------+
答案 2 :(得分:1)
我不相信这个盒子没有任何“黑客”的支持。聪明的举动可能是将模块放在像文件服务器这样的公共位置,并在需要时将其导入服务器。例如:
$session = new-pssession -computerName server01
invoke-command -session $session -scriptblock {
#Set executionpolicy to bypass warnings IN THIS SESSION ONLY
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
#Import module from public location
Import-Module \\fileserver\folders\modulelocation...
<# use function defined in MyCustomModule here #>
}
答案 3 :(得分:0)
如何使用Invoke-command
Import-module YourModule
$s = [scriptblock]::Create($(get-item Function:\Your-ModuleFunction).Definition)
Invoke-Command -ScriptBlock $s -Computername s1,s2,sn
答案 4 :(得分:0)
感谢此主题,它很有帮助……。
但是我实际上重写了该功能。
请注意,本文中的原始功能或该重写的功能都包含模块清单数据。因此,您不能依赖模块上的版本检查。
function Import-ModuleRemotely {
Param (
[string] $moduleName,
[System.Management.Automation.Runspaces.PSSession] $session
)
Import-Module $moduleName
$Script = @"
if (get-module $moduleName)
{
remove-module $moduleName;
}
New-Module -Name $moduleName { $($(Get-Module $moduleName).Definition) } | Import-Module
"@
Invoke-Command -Session $Session -ScriptBlock {
Param($Script)
. ([ScriptBlock]::Create($Script))
Get-Module
} -ArgumentList $Script
}
答案 5 :(得分:0)
从 PS 5.0 开始,我认为现在有另一种更简洁的方法:
利用 Copy-Item 的 ToSession 参数将本地模块复制到远程机器上。
这不涉及以前解决方案的缺点:
示例用法:
$s = New-PSSession MyTargetMachine
Get-Module MyLocalModule | Import-LocalModuleToRemoteSession -Session $s -Force
# Show module is loaded
Invoke-Command $s -ScriptBlock { Get-Module }
Import-LocalModuleToRemoteSession 函数
注意它不会加载模块依赖
<#
.SYNOPSIS
Imports a loaded local module into a remote session
.DESCRIPTION
This script copies a module's files loaded on the local machine to a remote session's temporary folder and imports it, before removing the temporary files.
It does not require any shared folders to be exposed as it uses the default Copy-To -ToSession paramter (added in PS 5.0).
#>
function Import-LocalModuleToRemoteSession
{
[CmdletBinding()]
param(
# Module to import
[Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName,Mandatory)]
[System.Management.Automation.PSModuleInfo]$ModuleInfo,
# PSSession to import module to
[Parameter(Mandatory)]
[System.Management.Automation.Runspaces.PSSession]
$Session,
# Override temporary folder location for module to be copied to on remote machine
[string]
$SessionModuleFolder=$null,
[switch]
$Force,
[switch]
$SkipDeleteModuleAfterImport
)
begin{
function New-TemporaryDirectory {
$parent = [System.IO.Path]::GetTempPath()
[string] $name = [System.Guid]::NewGuid()
New-Item -ItemType Directory -Path (Join-Path $parent $name)
}
}
process{
if( [string]::IsNullOrWhiteSpace($SessionModuleFolder) ){
Write-Verbose "Creating temporary module folder"
$item = Invoke-Command -Session $Session -ScriptBlock ${function:New-TemporaryDirectory} -ErrorAction Stop
$SessionModuleFolder = $item.FullName
Write-Verbose "Created temporary folder $SessionModuleFolder"
}
$directory = (Join-Path -Path $SessionModuleFolder -ChildPath $ModuleInfo.Name)
Write-Verbose "Copying module $($ModuleInfo.Name) to remote folder: $directory"
Copy-Item `
-ToSession $Session `
-Recurse `
-Path $ModuleInfo.ModuleBase `
-Destination $directory
Write-Verbose "Importing module on remote session @ $directory "
try{
Invoke-Command -Session $Session -ErrorAction Stop -ScriptBlock `
{
Get-ChildItem (Join-Path -Path ${Using:directory} -ChildPath "*.psd1") `
| ForEach-Object{
Write-Debug "Importing module $_"
Import-Module -Name $_ #-Force:${Using:Force}
}
if( -not ${Using:SkipDeleteModuleAfterImport} ){
Write-Debug "Deleting temporary module files: $(${Using:directory})"
Remove-Item -Force -Recurse ${Using:directory}
}
}
}
catch
{
Write-Error "Failed to import module on $Session with error: $_"
}
}
}