15

Is there a way to use modules that were imported in a local session in a remote session? I looked at import-pssession, but I don't know how to get the local session. Here's a sample of what I want to do.

import-module .\MyModule\MyModule.ps1
$session = new-pssession -computerName RemoteComputer
invoke-command -session $session -scriptblock { Use-CmdletFromMyModule }

Also, I do not want to import-module in the remote session, as the ps1 files are not on that server.

JasonMArcher
  • 12,386
  • 20
  • 54
  • 51
Jonathan Matheus
  • 151
  • 1
  • 1
  • 3

7 Answers7

10

I ended up hacking this to work. What I did was create a local session, import modules into that session and used import-pssession to import modules from the created local session into the remote session. This is slow. If anyone has a better way of doing this, or if someone knows how to get an instance of the base session I'd love to hear from you!

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
}
Jonathan Matheus
  • 1,350
  • 2
  • 12
  • 15
  • I stand corrected. :-) Nice approach of taking the ability to import commmands from the remote computer and turning it on its head to import from the local computer. BTW are you importing based on Path (instead of module name) because you ran into a problem? – Keith Hill May 14 '10 at 17:13
  • You can only import a module by name if the module file is in one of the default module locations. If it's not, you have to give it the file path. I ran into an issue with assembly modules that are imported from a module manifest psd1. The PSModuleInfo.Path property for a binary module points to the dll instead of the psd1. If you import directly from the dll, then import-pssession will fail. Hence all the voodoo to get the psd1 file for an assembly module. – Jonathan Matheus May 14 '10 at 18:41
  • Do you still use this method or did you find a better way to do this? – icnivad Jan 13 '12 at 15:04
  • This is not an unreasonable way to do this - however it requires both ends of the wire having WinRM listening, which may be difficult to configure / allow depending on your environment. For internal environments, no biggie, but if your host is on EC2, and your internal machine is behind a firewall, this will be difficult to setup. Not downplaying the usefulness -- it's a nice trick, but just be aware of the issues. I have another method also with limitation posted below. IMHO, this is a sore spot in Powershell remoting (along with no real equiv to SFTP) – Ethan J. Brown Apr 26 '12 at 17:24
3

As an alternate to what Jonathan mentions, if you have source modules you want to push over the wire, then you can do that without too much trouble. If you have binaries, you might be able to do something similar.but I'd say all bets are off there. Essentially you push the files over as params in a hash, write to temp, then import.

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
}

Call like

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

Also theoretically possible, exporting a current localhost session to module and pushing that over the wire -- untested pseudo-code. This might not work...

$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
Ethan J. Brown
  • 2,288
  • 3
  • 20
  • 27
  • Tip: if the module is located in PSModule path or is already loaded, it can be useful to change ModulePaths evalueation to (Get-Module $Module).Path – 2xMax May 08 '14 at 16:59
2

in case this helps:

if you can switch to PowerShell 3.0, then the Get-Module and Import-Module cmdlets support modules on remote computers:

http://technet.microsoft.com/en-us/library/hh857339.aspx#BKMK_REM

It should be possible to run PowerShell on remote computer, that performs Import-Module, without having any ps1 scripts on the remote computer.

Sean
  • 939
  • 8
  • 16
2

So I was looking for something similar... In my case I just needed to export a single function to a remote session... this is what I came up with. Maybe you could loop over it to try it. It doesn't work with internal commands, but it does work on functions in custom modules (in the testing I've done).

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
}
SamuelWarren
  • 1,429
  • 12
  • 27
0

I don't believe you can. You can go the other way - import commands loaded on the remote machine into your remote session running on the local machine. You can specify a script to invoke-command and it will copy that script over to the remote machine and run it. But if you require snapins or additional modules, you will need to make sure those are installed on each remote machine and then load them into the remote session via commands or your script.

Keith Hill
  • 173,872
  • 36
  • 316
  • 347
0

I would suggest something like:

$rs = New-PSSession -ComputerName "RemoteComputer"
Invoke-Command -Session $rs -scriptblock {import-module ActiveDirectory}
Import-PSSession -Session $rs -Module ActiveDirectory

Since then, you can use ActiveDirectory cmdlets in your session.

Hope this helps.

Scott Munro
  • 12,689
  • 3
  • 67
  • 78
kubusz
  • 843
  • 3
  • 9
  • 17
-2

use credssp authentication

invoke-command -computername $localSession -Credential $Credential -Authentication Credssp
xenteros
  • 14,275
  • 12
  • 47
  • 81