Programmatic Access via Remote PowerShell in Exchange Server 2010


The management experience given by Exchange 2010 through PowerShell has been moved all the way from Local to Remote. This will mean that enterprise Admins will have to adjust their regular scripts to connect to Remote PowerShell instead of creating a local session.

Here are some examples on how can this be achieved and the differences that may have to be done in order to create the connection and run the cmdlets.

Using programmatic API

The programmatic API is the simplest method that will allow you to make a remote connection requiring only the Uri for the connection and a set of suitable credentials that need to be provided through a method.

SCredential credential = new PSCredential(username, password);

Note: The password here must be of type SecureString.

After you just need to create the connection Information that will allow the creation of the runspace.

// Set the connection Info
    WSManConnectionInfo connectionInfo = new WSManConnectionInfo(
      new Uri(liveIdconnectionUri),
      http://schemas.microsoft.com/powershell/Microsoft.Exchange,
      credential);

    connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;

// create a runspace on a remote path
// the returned instance must be of type RemoteRunspace

Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);

From here it is just you only need to create a PowerShell instance and fill it with your cmdlets and then invoke it through the run space we’ve just created. Here is a simple example with get-mailbox.

PowerShell powershell = PowerShell.Create();
      PSCommand command = new PSCommand();
      command.AddCommand(“Get-Mailbox”);
      command.AddParameter(“Identity”, mailboxName);
      powershell.Commands = command;
      try
      {
          // open the remote runspace
          runspace.Open();
          // associate the runspace with powershell
          powershell.Runspace = runspace;
          // invoke the powershell to obtain the results
          return powershell.Invoke();
      }
      finally
      {
          // dispose the runspace and enable garbage collection
          runspace.Dispose();
          runspace = null;
          // Finally dispose the powershell and set all variables to null to free
          // up any resources.
          powershell.Dispose();
          powershell = null;
      }

Using Programmatic API and Certificate Thumbprint

This uses the exact same syntax than the Programmatic API except that we would need to connect to a Uri that is has a Certificate Thumbprint enabled when we create the WSMAN connection in this syntax.

WSManConnectionInfo connectionInfo = new WSManConnectionInfo(
              “E75C847ADF7B355DAAC2C6D1A4EDD8284A0C0FDC”,
              new Uri(certconnectionUri),
              “http://schemas.microsoft.com/powershell/Microsoft.Exchange”);

Remote Request using a local run space (Scripting the remote class)

This is the best to create scripts and run your cmdlets using remote PowerShell. For this case, we have to script in the code a call to create a New-PSSession using our credential, the connection Uri and method of authentication. This is basically using the cmdlet:

New-PSSession -ConnectionUri Microsoft.Exchange -ConnectionUri $Uri -Credential $LiveCred -Authentication Basic

To do this, we have to create the Run space in which we will run the cmdlet and then create a PowerShell instance to add the cmdlet.

Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace();
        PowerShell powershell = PowerShell.Create();
        PSCommand command = new PSCommand();
        command.AddCommand(“New-PSSession”);
        command.AddParameter(“ConfigurationName”, “Microsoft.Exchange”);
        command.AddParameter(“ConnectionUri”, new Uri(liveIdconnectionUri));
        command.AddParameter(“Credential”, cred);
        command.AddParameter(“Authentication”, “Basic”);
        powershell.Commands = command;

Now invoke this cmdlet and set it as a variable on the local Run Space that will be used to do the remote calls.

try
     {
         // open the remote runspace
         runspace.Open();
         // associate the runspace with powershell
         powershell.Runspace = runspace;
         // invoke the powershell to obtain the results
         Collection<RemoteRunspaceInfo> result = powershell.Invoke<RemoteRunspaceInfo>();
         foreach (ErrorRecord current in powershell.Streams.Error)
         Console.WriteLine(“The following Error happen when opening the remote Runspace: ” + current.Exception.ToString() + ” | InnerException: ” + current.Exception.InnerException);
         if (result.Count != 1)
             throw new Exception(“Unexpected number of Remote Runspace connections returned.”);
         // Set the runspace as a local variable on the runspace
     powershell = PowerShell.Create();
     command = new PSCommand();
     command.AddCommand(“Set-Variable”);
     command.AddParameter(“Name”, “ra”);
     command.AddParameter(“Value”, result[0]);
     powershell.Commands = command;
         powershell.Runspace = runspace;
         powershell.Invoke();

After we have done this, we can now do the remote calls that will be executed on the server side as script blocks.

         powershell = PowerShell.Create();
     command = new PSCommand();
     command.AddScript(“Invoke-Command -ScriptBlock { Get-Mailbox -Identity:” + mailboxName + ” } –     Session $ra”);
     powershell.Commands = command;
     powershell.Runspace = runspace;
         return powershell.Invoke();
     }
     finally
     {
         // dispose the runspace and enable garbage collection
         runspace.Dispose();
         runspace = null;
         // Finally dispose the powershell and set all variables to null to free
         // up any resources.
         powershell.Dispose();
         powershell = null;
     }

Mario Trigueros Solorio

Comments (7)
  1. sting says:

    Hi, I am in the process of creating a project around remoting via Powershell from 2008 C#/VB using powershell 2.0 & WinRM 2.0 from a Windows Server 2008 SP2 machine. I have added the system.management.automation reference, but I am unable expose the RemoteRunspaceInfo class and my code therefore fails.  I cannot seem to expose in the fashion listed in the example above. Can you provide more information around which this class.

  2. Cameronk says:

    The sticking point for me seems to be the liveIdConnectionUri… what should be there? I’m using an Exchange 2010 installation that is not federated… should I point to
    https://login.live.com?

  3. cute.devil says:

    when executed a powershell 2.0 command using vb.net to add mailbox to MS Exchange 2010 having WinRM client and server machines as same machine, i encountered the following error.

    "The WinRM client cannot process the request. The authentication mechanism requested by the client is not supported by the server or unencrypted traffic is disabled in the service configuration. Verify the unencrypted traffic setting in the service configuration or specify one of the authentication mechanisms supported by the server. To use Kerberos, specify the computer name as the remote destination. Also verify that the client computer and the destination computer are joined to a domain. To use Basic, specify the computer name as the remote destination, specify Basic authentication and provide user name and password. Possible authentication mechanisms reported by server: For more information, see the about_Remote_Troubleshooting Help topic."

    Environment i am using is

    Windows Server 2008 R2 64-bit

    MS Exchange 2010

    PowerShell V2.0

  4. cute.devil says:

    Hi ,

    i executed following code to get MailBoxDatabase  

    Dim Pass As New System.Security.SecureString

           Dim MyPassword As String = Password

           For Each c As Char In MyPassword

               Pass.AppendChar(c)

           Next

           Dim credential As PSCredential = New PSCredential(UserName, Pass)

           Dim connectionInfo As New WSManConnectionInfo(New Uri("http://&quot; & FQDN & "/powershell?serializationLevel=Full"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange&quot;, credential)

           connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos

           Dim RSpace As Runspace

           Try

               RSpace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo)

               

           Catch ex As Exception

               Throw New Exception("RSPACE: " & ex.Message)

           End Try

     

     Dim Shell As PowerShell = PowerShell.Create()

           Dim PCommand As New PSCommand

           Try

               PCommand.AddCommand("Get-MailboxDatabase")

               PCommand.AddParameter("Server", New Microsoft.Exchange.Configuration.Tasks.ServerIdParameter(New Microsoft.Exchange.Data.Fqdn(ServerName)))

               ‘ PCommand.AddParameter("DomainController", PreferredDC)

               Shell.Commands = PCommand

           Catch ex As Exception

               Throw New Exception("Build command: " & ex.Message)

           End Try

           

           Dim results As Collection(Of PSObject)

           Dim Msg As String

           Dim Databases As String

           Try

               RSpace.Open()

               Shell.Runspace = RSpace

               results = Shell.Invoke()

               Dim Enm As System.Collections.Generic.IEnumerator(Of System.Management.Automation.PSObject) = results.GetEnumerator

               Dim index As Integer = 0

               Do While Enm.MoveNext()

                   Databases = Enm.Current.Members("Identity").Value.ToString ‘& "" & Enm.Current.Members("AdminDisplayName").Value.ToString

               

               Loop

               RSpace.Close()

           Catch err As Exception

               Throw New Exception("Run command: " & err.Message)

           Finally

               RSpace.Dispose()

               RSpace = Nothing

               Shell.Dispose()

               Shell = Nothing

           End Try

    and encountered the following error.

    Cannot process argument transformation on parameter ‘Server’. Cannot convert the "Clients.exmtemp.com" value of type "Deserialized.Microsoft.Exchange.Configuration.Tasks.ServerIdParameter" to type "Microsoft.Exchange.Configuration.Tasks.ServerIdParameter".

    any help regarding above issue will be appreciated.

    Thanks in Advance.

    Best Regards,

    cute devil

  5. Mgeriesa says:

    How can I write a code that recognize the exchange powershell and the basic powershell?

    I mean:

    get-mailbox | Format-list

    Format-list is not recognized as a powershell command, when I use the constructor with the credential!!

  6. What should the liveIdconnectionUri be in

       WSManConnectionInfo connectionInfo = new WSManConnectionInfo(

         new Uri(liveIdconnectionUri),

         "http://schemas.microsoft.com/powershell/Microsoft.Exchange&quot;,

         credential);

Comments are closed.