Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have 2 computers. On the computer A, I have a custom module written in C# for powershell 3.0 and installed via a MSI. I also have a shortcut that open powershell with the module already loaded.

I can just double click on my shortcut and run my command Do-Something on this computer without any problem, like the Exchange Server powershell.

But now I would like to do it from a remote session on computer B in C#.

So my question is, how can I open a remote powershell session to computer A with my module already loaded and the shell configured so I can just run my command and obtain the same result than if I run it on computer A?

share|improve this question

migrated from stackoverflow.com Dec 9 '12 at 13:59

This question came from our site for professional and enthusiast programmers.

    
Did you have a look at psexec? – Frank van Puffelen Dec 9 '12 at 13:22
    
No i didn't try psexec could you give me an example? I can open a remote powershell session whithout problem the problem is that it just start the regular powershell. I would prefer to start the powershell with my module already loaded but doing it from the remote session might be more hard because I don't know the installation path of the module at this point. I saw some information about PSSessionConfiguration which allow a startup script but I'm not sure its the good way to go. – Yann Lebel Dec 9 '12 at 15:00
up vote 2 down vote accepted

I finally find a way to accomplish what I needed.

First I created a powershell script to import my module and module script file

ShellInitScript

function Get-ScriptDirectory
{
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    Split-Path $Invocation.MyCommand.Path
}
$MyModuleDirectory = Get-ScriptDirectory
Set-Location $MyModuleDirectory 
Import-Module .\MyModule.psd1

MyModule.psd1

@{
GUID                = "[GENERATE A GUID HERE]" # Generate using $myGuid = [guid]::NewGuid() 

Author              = "Me"

CompanyName         = "My Company"

Copyright           = "© My Company. All rights reserved."

ModuleVersion       = "1.0.0.0"

ModuleToProcess     = "MyModule.psm1"
}

MyModule.psm1

Import-Module .\MyModuleCSharpAssembly.dll

function Enable-MyShellRemoting([switch]$Force)
{
    $proceed = 0

    if($Force -eq $false)
    {
        Write-Warning "Enable-MyShellRemoting restarts the WinRM service and all dependent services.`r`nAll WinRM sessions connected to Windows PowerShell session configurations are disconnected."

        $title = "Are you sure you want to perform this action?"
        $message = "Performing this operation will allow selected users to remotely run MyModule commands on this computer."

        $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes"

        $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No"

        $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

        $proceed = $Host.UI.PromptForChoice($title, $message, $options, 0) 
    }

    if($proceed -eq 0)
    {
        Enable-PSRemoting -Force

        if((Get-WmiObject Win32_ComputerSystem).PartOfDomain -eq $false){
            Set-Item WSMan:\localhost\Client\TrustedHosts *
            Restart-Service WinRM
        }

        $path = Join-Path -Path $MyModuleDirectory  -ChildPath "ShellInitScript.ps1"
        Register-PSSessionConfiguration -Name "MyShellUri" -StartupScript $path -Force
    }
}
function Disable-MyShellRemoting([switch]$Force, [switch]$DisablePSRemoting)
{
    $proceed = 0

    if($Force -eq $false)
    {
        Write-Warning "Disable-MyShellRemoting restarts the WinRM service and all dependent services.`r`nAll WinRM sessions connected to Windows PowerShell session configurations are disconnected."

        $title = "Are you sure you want to perform this action?"
        $message = "Performing this operation will prevent all users to remotely run MyModule commands on this computer.`r`nUse the the -DisablePSRemoting switch to disable all PowerShell remoting features."

        $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes"

        $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No"

        $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

        $proceed = $Host.UI.PromptForChoice($title, $message, $options, 0) 
    }

    if($proceed -eq 0)
    {
        if($DisablePSRemoting)
        {
            Disable-PSRemoting
        }

        if((Get-PSSessionConfiguration -Name "MyShellUri" -ErrorAction SilentlyContinue) -eq $null){
            Write-Host "The session configuration MyShellUri is already unregistered."
        }
        else {        
            Unregister-PSSessionConfiguration -Name "MyShellUri" -Force -ErrorAction Ignore
        }
    }
}

function Test-MyShellRemote()
{
    return "Test completed"
}

The dll MyModuleCSharpAssembly.dll is just a regular .Net assembly that contains custom Cmdlets.

The solution to the problem is in the function Enable-MyShellRemoting this method enable powershell remoting and register a custom Powershell Session Configuration with a statup script that load the module.

Then in C# all we have to do it's specify the ShellUri in the connection object like this

WSManConnectionInfo connectionInfo = new WSManConnectionInfo();
connectionInfo.ComputerName = "MyComputer";
connectionInfo.ShellUri = "http://schemas.microsoft.com/powershell/MyShellUri";
using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
     remoteRunspace.Open();
     using (PowerShell shell = PowerShell.Create())
     {
          shell.Runspace = remoteRunspace;
          shell.AddCommand("Test-MyShellRemote");
          Collection<PSObject> results = shell.Invoke();
          //the results collection contains the string return by the command
     }
     remoteRunspace.Close();
 }
share|improve this answer
    
You can use [guid]::NewGuid() then copy paste the generated guid. This guid should always be the same for you module. – Yann Lebel Mar 21 '13 at 13:46
    
ShellInitScript can be included in profile.ps1, isn't ? – Kiquenet Apr 9 '13 at 11:58
    
Yes it's possible but in this case the module will be loaded in all powershell console of the user but if you keep them separated you can create a separated shell more like the Exchange Server Shell. – Yann Lebel Apr 9 '13 at 15:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .