如何将自定义PowerShell模块导入远程会话?

Yan*_*nko 35 powershell powershell-2.0 powershell-remoting powershell-3.0

我正在开发一个自定义PowerShell模块,我想在与另一台计算机的远程会话的上下文中使用它.以下代码(显然不起作用)解释了我想要实现的目标:

import-module .\MyCustomModule.psm1
$session = new-pssession -computerName server01
invoke-command -session $session -scriptblock { 
  <# use function defined in MyCustomModule here #> 
}
Run Code Online (Sandbox Code Playgroud)

第一个问题是,是否完全可以实现这种情况?我的意思是我只希望我的自定义模块实际存在于我的机器上,而不是远程服务器上.

我找到了这个帖子,但我没有管理它 - 它不允许从远程机器创建一个会话回到本地.可能,我面对在该线程的评论中提到的配置限制......此外,作者提到了对我的解决方案至关重要的性能影响......

如果那可能,那怎么样?

PowerShell的版本目前不是一个约束 - 如果解决方案仅在PS 3.0中可用 - 我可以忍受这个.

Yan*_*nko 43

这个问题有一些很好的评论,我花了一些时间研究解决问题的各种方法.

首先,我最初要求的是不可能的.我的意思是,如果你采用模块方式,那么模块应该物理地存在于目标机器上以便能够Import-Module进入远程会话.

为了进一步抽象我的问题,我正在尝试为产品部署创建一个可重用的基于PowerShell的框架.这将是一种推送方式的部署,这意味着我们鼓励人们在本地计算机上运行一些脚本以部署到某个远程服务器.据我调查这个区域,有两种可能的方式对常识很友好.

模块方法

要遵循的流程:

  • 将每个逻辑上不同的功能放入PowerShell模块(*.psm1)
  • 将模块分发到远程计算机并扩展PSModulePath变量以包括新模块位置
  • 在客户端计算机上,创建到远程服务器的新会话,并使用 Invoke-Command -Session $s -ScriptBlock {...}
  • 在脚本块开始Import-Module CustomModule- 它将搜索CustomModule远程计算机,显然会找到它

好处

以下是喜欢这种方法的原因:

  • 传统模块角色的结果 - 促进可重用库的创建
  • 根据Windows PowerShell in Action这本伟大的书,"模块可用于创建特定于域的应用程序".据我所知,它可以通过组合模块嵌套和混合脚本/二进制模块来实现,以暴露特定于某个域的直观界面.基本上,对于基于PowerShell的部署框架的目标,这是我最重视的

缺点

考虑以下因素很重要:

  • 您必须找到将自定义模块交付到远程计算机的方法.我玩过NuGet,我不确定它是否适合任务,但还有其他选项,例如,MSI安装程序或xcopy来自共享文件夹的plain .此外,交付机制应该支持升级/降级和(最好)多实例安装,但这与我的任务相关而不是一般的问题.

脚本方法

要遵循的流程:

  • 将每个逻辑上不同的功能放在单独的PowerShell脚本中(*.ps1)
  • 在客户端计算机上,创建到远程服务器的新会话,并用于Invoke-Command -Session $s -FilePath .\myscript.ps1将脚本中定义的函数加载到远程会话
  • 使用另一个Invoke-Command -Session $s -ScriptBlock {...}并参考您的自定义函数 - 它们将在会话中存在

好处

以下是这种方法的优点:

  • 它很简单 - 您不必了解模块的特性.只需编写简单的PowerShell脚本就可以了
  • 您无需向远程计算机提供任何内容 - 这使得解决方案更简单,维护更不容易出错

缺点

当然,这并不理想:

  • 对解决方案的控制较少:例如,如果您将一组功能"导入"到会话中,所有这些功能都被"导入"并且对用户可见,因此没有"封装"等等.我确信很多解决方案可以忍受这个,所以不要只基于这一点来决定
  • 每个文件中的功能必须是自包含的 - 任何点源或模块从那里导入将搜索远程机器,而不是本地

最后,我应该说远程机器仍然需要为远程处理做好准备.这就是我的意思:

  • 执行策略应该更改为某些内容,因为默认情况下它受到限制: Set-ExecutionPolicy Unrestricted
  • 应启用PowerShell远程处理: Enable-PSRemoting
  • 脚本运行的帐户应添加到远程服务器的本地管理员
  • 如果您计划访问远程会话中的文件共享,请确保您了解多跳身份验证并采取适当的操作
  • 确保您的防病毒软件是您的朋友,并且不会将您发送到PowerShell地狱

  • 严,这是一个非常详细和通过回应.谢谢(你的)信息. (2认同)

Rob*_*Rob 7

这是另一种方法:在远程会话中重新创建模块,而不复制任何文件.

我没有尝试处理模块之间的依赖关系,但这似乎适用于简单的自包含模块.它依赖于本地会话中可用的模块,因为这使得确定导出更容易,但是通过一些额外的工作,它也可以用于模块文件.

function Import-ModuleRemotely([string] $moduleName,[System.Management.Automation.Runspaces.PSSession] $session)
{
    $localModule = get-module $moduleName;
    if (! $localModule) 
    { 
        write-warning "No local module by that name exists"; 
        return; 
    }
    function Exports([string] $paramName, $dictionary) 
    { 
        if ($dictionary.Keys.Count -gt 0)
        {
            $keys = $dictionary.Keys -join ",";
            return " -$paramName $keys"
        }
    }
    $fns = Exports "Function" $localModule.ExportedFunctions;
    $aliases = Exports "Alias" $localModule.ExportedAliases;
    $cmdlets = Exports "Cmdlet" $localModule.ExportedCmdlets;
    $vars = Exports "Variable" $localModule.ExportedVariables;
    $exports = "Export-ModuleMember $fns $aliases $cmdlets $vars;";

    $moduleString= @"
if (get-module $moduleName)
{
    remove-module $moduleName;
}
New-Module -name $moduleName {
$($localModule.Definition)
$exports;
}  | import-module
"@
    $script = [ScriptBlock]::Create($moduleString);
    invoke-command -session $session -scriptblock $script;
}
Run Code Online (Sandbox Code Playgroud)