使用PowerShell远程管理:在远程PSSession中使用导入的模块cmdlets

16
有没有一种方法可以在远程会话中使用在本地会话中导入的模块?我查看了import-pssession,但我不知道如何获取本地会话。以下是我想要做的样例。
有没有办法在远程会话中使用在本地会话中导入的模块?我看了 import-pssession,但是我不知道如何获取本地会话。这是我想做的一个示例。
import-module .\MyModule\MyModule.ps1
$session = new-pssession -computerName RemoteComputer
invoke-command -session $session -scriptblock { Use-CmdletFromMyModule }

另外,我不想在远程会话中导入模块,因为ps1文件不在那台服务器上。

7个回答

10

我最终通过一些技巧使它起作用。我创建了一个本地会话,在该会话中导入了模块,并使用import-pssession将创建的本地会话中的模块导入远程会话。这很慢。如果有人知道更好的方法,或者知道如何获取基础会话的实例,请告诉我!

Remoting.psm1

function Export-ModuleToSession {
 Param(
  [ValidateNotNull()]
  $session,
  [ValidateNotNull()]
  $modules
 )

 $computername = $env:computername

 $modulesToImport = get-module -name $modules

 invoke-command -session $session -argumentlist @($computername, $modulesToImport) -scriptblock {
  Param(
   $computername,
   $modules
  )

  write-host ("Creating Temp Session On: " + $computername)

  $localSession = New-psSession -computername $computername

  $modules | foreach-object {
   if($_.ModuleType -ne "Binary") {
    $path = $_.path
   }
   else {
    $path = join-path (split-path $_.Path) ("{0}.psd1" -f $_.name)
   }

   invoke-command -session $localSession -argumentList $path -scriptblock {
    Param(
     $path
    )

    $initializeDefaultBTSDrive = $false
    set-executionpolicy unrestricted

    write-host ("Importing Module To Temp Session: " + $path)
    import-module $path
   }
  }

  $initializeDefaultBTSDrive = $false

  $modules | foreach-object { 
   write-host ("Exporting Module: " + $_.name)
   import-psSession -session $localSession -Module $_.name  | out-null 
  }
 }
}

MyModule.psm1

function MyCmdlet {}

RemotingTest.ps1

import-module .\remoting.psm1
import-module .\MyModule.psm1

try
{
 $remoteSession = New-PsSession -computerName "RemoteComputer"
 Export-ModuleToSession -session $remoteSession -modules "MyModule"

 Invoke-Command -session $remoteSession -scriptblock { MyCmdlet } -verbose -ea Stop
}
finally
{
 Remove-PsSession $remoteSession -ea Continue
 Remove-Module "Remoting" -ea Continue
 Remove-Module "MyModule" -ea Continue
}

我改正了。:-) 从远程计算机导入命令的方法非常好,而将其反过来从本地计算机导入则更好。顺便问一下,你是基于路径(而不是模块名称)进行导入的,因为你遇到了问题吗? - Keith Hill
如果模块文件不在默认的模块位置之一,则只能按名称导入模块。如果不是,则必须提供文件路径。我遇到了一个问题,即从模块清单 psd1 导入的程序集模块。二进制模块的 PSModuleInfo.Path 属性指向 dll 而不是 psd1。如果直接从 dll 导入,则 import-pssession 将失败。因此需要进行所有神秘操作以获取程序集模块的 psd1 文件。 - Jonathan Matheus
你还在使用这种方法吗?还是找到了更好的方法来完成这个任务了? - icnivad
这并不是一种不合理的方法 - 但它需要两端的电缆都有WinRM监听,这可能会根据您的环境难以配置/允许。对于内部环境来说,这没什么大不了的,但如果您的主机在EC2上,而您的内部机器在防火墙后面,那么这将很难设置。不是贬低其有用性 - 这是一个不错的技巧,但要注意问题。我还有另一种方法,也有限制,下面发布。在我看来,这是PowerShell远程操作中的一个痛点(以及没有真正等效于SFTP)。 - Ethan J. Brown

3
作为Jonathan提到的另一种选择,如果您有源模块想要通过网络传输,那么您可以很容易地完成此操作。如果您有二进制文件,您可能能够做类似的事情,但我认为这种方法并不保险。基本上,您将文件作为哈希参数推送,并写入临时文件,然后导入即可。
function Export-SourceModulesToSession
{
    Param(
     [Management.Automation.Runspaces.PSSession]
     [ValidateNotNull()]
     $Session,

    [IO.FileInfo[]]
    [ValidateNotNull()]
    [ValidateScript(
    {
      (Test-Path $_) -and (!$_.PSIsContainer) -and ($_.Extension -eq '.psm1')
    })]
   $ModulePaths
  )

   $remoteModuleImportScript = {
     Param($Modules)

     Write-Host "Writing $($Modules.Count) modules to temporary disk location"

     $Modules |
       % {
         $path = ([IO.Path]::GetTempFileName()  + '.psm1')
         $_.Contents | Out-File -FilePath $path -Force
         "Importing module [$($_.Name)] from [$path]"
         Import-Module $path
       }
   }

  $modules = $ModulePaths | % { @{Name = $_.Name; Contents = Get-Content $_ } }
  $params = @{
    Session = $Session;
    ScriptBlock = $remoteModuleImportScript;
    Argumentlist = @(,$modules);
  }

  Invoke-Command @params
}

调用方式

$session = New-PSSession -ComputerName Foo
Export-SourceModulesToSession $session -ModulePaths '.\module.psm1','.\module2.psm1'

还有一种理论上可能的方法,即将当前的localhost会话导出到模块中,并将其推送到网络上--未经测试的伪代码。这种方法可能不起作用...

$localSession = New-PSSession #defaults to localhost

# if you don't have modules automatically loading in the profile, etc, then manually load them
Invoke-Command -Computer $localSession -ScriptBlock { Import-Module 'foo'; Import-Module '.\module.ps1' }
Export-PSSession $localSession -OutputModule TempLocalModule
#now that you have TempLocalModule written out, it's possible you can send that thing across the wire in the same way

提示:如果模块位于PSModule路径中或已加载,则将ModulePaths评估更改为(Get-Module $Module)。Path可能很有用。 - 2xMax

3

我们可能需要在两端都使用PS 3.0,不是吗? - icnivad

2

所以我正在寻找类似的东西...在我的情况下,我只需要将一个函数导出到远程会话中...这就是我想出的方法。也许你可以尝试循环使用它。它不能用于内部命令,但它可以用于自定义模块中的函数(根据我所做的测试)。

function Export-FunctionToSession
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $Session,
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $FunctionName
    )
    $script = "Function $functionName(){" + (Get-Command $functionName).definition + '}'
    $scriptBlock = {Invoke-Expression $using:script}
    Invoke-Command -Session $session -ScriptBlock $scriptBlock
}

0
我建议使用类似以下的代码:
$rs = New-PSSession -ComputerName "RemoteComputer"
Invoke-Command -Session $rs -scriptblock {import-module ActiveDirectory}
Import-PSSession -Session $rs -Module ActiveDirectory

从那时起,您可以在会话中使用ActiveDirectory cmdlet。

希望这有所帮助。


0

我不相信你可以这样做。你可以反过来操作 - 将加载在远程机器上的命令导入到在本地机器上运行的远程会话中。你可以指定一个脚本给Invoke-Command,它会将该脚本复制到远程机器并运行它。但是,如果您需要snapins或其他模块,则需要确保这些模块已安装在每个远程机器上,并通过命令或脚本将它们加载到远程会话中。


-2

使用 CredSSP 身份验证

invoke-command -computername $localSession -Credential $Credential -Authentication Credssp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接