Running Remote Powershell scripts from VB.NET

I’ve been writing an XMA that uses powershell to communicate with MSOnline. Because of various connectivity issues I have to run the powershell commands on a server other than the FIM server – that is, I have to use remote powershell. There will be more detail on this XMA in a future post, however today I’m going to write up just the VB.Net to remote powershell bit, because as usual I had to spend a lot of time going through blog posts and forums to supplement the meager information available on MSDN.

Enabling Remote Powershell

I am running the code on a Windows 2008 server, and the powershell scripts on a Windows 2003 server. As neither of these OS’s natively include WinRM and powershell 2.0 you have to download and install the Windows Management Framework update.

Next you have to enable remoting and trust the remote host. Here is a good walkthrough.

Libraries

You need the System.Management.Automation library, which you can copy out of the GAC and reference in your code:

copy C:\windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll C:\path_to_project_folder

Then you’ll need these imports:

Imports System.Management.Automation
Imports System.Management.Automation.Host
Imports System.Management.Automation.Runspaces

The Code

I had a number of powershell commands to run on the remote server (including adding a snapin) so the simplest thing was to wrap it in a powershell script and just call that.

Note also that I also had a problem with the script arguments (neither AddArgument nor AddParameter actually passed the arguments to the remote server), so when I call this subroutine I pass the script arguments along with the script name like so:

RunRemotePSScript("myserver", "username", "password", "c:\scripts\myscript.ps1 " & arg1 & " " & arg2)

And here’s the code…

    Sub RunRemotePSScript(ByVal connectTo As String, ByVal user As String, ByVal password As String, ByVal script As String)
        Const SHELL_URI As String = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell"
        Dim serverUri As New Uri("http://" & connectTo & ":5985/wsman")

        Dim securepass As New Security.SecureString
        Dim c As Char
        For Each c In password
            securepass.AppendChar(c)
        Next
        Dim remotecred As New PSCredential(user, securepass)

        Dim connectionInfo As New WSManConnectionInfo(serverUri, SHELL_URI, remotecred)
        Dim myRunSpace As Runspace = RunspaceFactory.CreateRunspace(connectionInfo)
        Dim psresult As New System.Collections.ObjectModel.Collection(Of PSObject)
        myRunSpace.Open()

        Dim psh As PowerShell = PowerShell.Create()
        psh.Runspace = myRunSpace

        psh.AddScript(script)
        psresult = psh.Invoke()
        psh.Dispose()
        myRunSpace.Close()
    End Sub

The biggest bruise from knocking my head

came from trying to get those two URI addresses correct. During that time I saw this error a lot:

“System.Management.Automation.Remoting.PSRemotingTransportException: Connecting to remote server failed with the following error message :
<h1>Bad Request (Invalid Hostname)</h1>
For more information, see the about_Remote_Troubleshooting Help topic.

Eventually this blog post got me on the right track: http://scorpiotek.com/blog/?p=770 – so thanks for that!