CTP3 Remote Runspace .Net (C#)

267 views
Skip to first unread message

noli44

unread,
Mar 1, 2009, 9:21:27 PM3/1/09
to

*Background*
I am currently developing an automated API for my company to manage
public email provisioning in ExchangeLabs (Outlook live).

*Problem
*Using the System.Management.Automation.Runspace namespace I can not
find any logical classes/methods for creating a remote runspace outside
of invoking a script to enter a PSSession.

Basically I want to Enter a Runspace/Session with the arguments;
username, password, uri, configuration, authentication. Essentially
creating the statement below using managed code (C#).

New-PSSession -ConfigurationName Microsoft.Exchange -Credentials $cred
-Uri $livedatacenter -Authentication Basic

Any help will be appreciated.

Thanks Anthony.


--
noli44

Josh Einstein

unread,
Mar 3, 2009, 1:54:18 AM3/3/09
to
Well, you could always create an instance of the New-PSSession cmdlet in C#
and call its Invoke method but that kinda feels like a hack.

Using Reflector on System.Management.Automation.dll, I can see that
Enter-PSSession uses RemoteRunspace which is an internal class. I'm not
sure how far you'll get without taking a dependency on this internal class,
but it appears the entry point to it is
RunspaceFactory.CreateRunspace(RunspaceConnectionInfo, PSHost, TypeTable)
and it returns type Runspace, which is abstract.

I gotta say, I've dug into the guts of PowerShell and there's an assload of
internal classes and methods that really limit what you can do from C#. For
example, I've been trying to find an easy way from a cmdlet to use the
metadata produced by Format-Table (or the metadata used by Format-Table to
produce displays based on format xml files) and it's really hard to do
without running into internal methods.

Hell, even just invoking a ScriptBlock from C# that receives an automatic $_
variable...

Josh

"noli44" <gu...@unknown-email.com> wrote in message
news:0192c52188241952...@nntp-gateway.com...

noli44

unread,
Mar 3, 2009, 6:13:52 PM3/3/09
to

Josh,

Thanks for the reply. I agree when you say you feel limited by the
capabilities
of the powershell libraries used in C#.

I was really looking for a clean implementation,
but looks as though I may have to resort to hacks. Atleast until the
next RC.

Anthony

> "noli44" <gu...@xxxxxx-email.com> wrote in message
> news:0192c52188241952...@xxxxxx-gateway.com...> > >

> > >
> > > *Background*
> > > I am currently developing an automated API for my company to manage
> > > public email provisioning in ExchangeLabs (Outlook live).
> > >
> > > *Problem
> > > *Using the System.Management.Automation.Runspace namespace I can not
> > > find any logical classes/methods for creating a remote runspace
> > outside
> > > of invoking a script to enter a PSSession.
> > >
> > > Basically I want to Enter a Runspace/Session with the arguments;
> > > username, password, uri, configuration, authentication. Essentially
> > > creating the statement below using managed code (C#).
> > >
> > > New-PSSession -ConfigurationName Microsoft.Exchange -Credentials
> > $cred
> > > -Uri $livedatacenter -Authentication Basic
> > >
> > > Any help will be appreciated.
> > >
> > > Thanks Anthony.
> > >
> > >
> > > --
> > > noli44 > >


--
noli44

noli44

unread,
Mar 9, 2009, 7:58:04 PM3/9/09
to

For anyone that is interested I ended up Invoking the PSSession command
with the arguments stated in teh previous post and captured the PSObject
returned.

Not the nicest implementation, but after wrapping it in a custom
session object it does the job.

(This might be hard to make sense of without the complete
implementation)

Here's the underlying code:

public static PSObject GenerateSession(IPowershellSessionInformation
sessionInformation)
{
IDictionary<string,object> nameValuePairs = new
Dictionary<string, object>();

if
(!String.IsNullOrEmpty(sessionInformation.Configuration))
nameValuePairs["Configuration"] =
sessionInformation.Configuration;
if
(!String.IsNullOrEmpty(sessionInformation.Authentication))
nameValuePairs["Authentication"] =
sessionInformation.Authentication;
if (!String.IsNullOrEmpty(sessionInformation.Uri))
nameValuePairs["Uri"] = sessionInformation.Uri;
if (sessionInformation.Credential != null)
nameValuePairs["Credential"] =
sessionInformation.Credential;
if (!String.IsNullOrEmpty(sessionInformation.ComputerName))
nameValuePairs["Computer"] =
sessionInformation.ComputerName;

Command command =
TranslateToPowershellCommand("Create-Session", nameValuePairs); //
mapping is performed to translate pre-defined arguments/commands into
valid powershell Command Object.

// my implementation of a "Remote Runspace", takes argument of
IPowershellSessionInformation. // If default ctor invoked just uses
default runspace
IPowershellExtension extension = new
ExchangeLabsPowershellExtension();

ICollection<PSObject> collection =
extension.PerformInvokeCommand(command);

// return session object if it exists
if (collection.Count == 0)
throw new ConfigurationException("Session was not
created, revise Session parameters.");

return collection.SingleOrDefault();
}

// very important to remove the PSSession after use
public static bool CleanSession(IPowershellSessionInformation
sessionInformation)
{
if (sessionInformation == null)
return true;

IDictionary<string, object> nameValuePairs = new
Dictionary<string, object>();

nameValuePairs.Add("Session", sessionInformation.Session);

Command command =
ExchangeLabsProvisioningCommands.TranslateToPowershellCommand("Remove-Session",

nameValuePairs);

IPowershellExtension extension = new
ExchangeLabsPowershellExtension();

ICollection<PSObject> collection =
extension.PerformInvokeCommand(command);

return true;
}

If anyone is interested in the complete implementation please email me
at: fargnoli...@gmail.com


--
noli44

noli44

unread,
Apr 27, 2009, 7:03:22 PM4/27/09
to

I have finally figured out the correct way of doing this.

After the installation of WinRM ctpv3 and powershell ctpv3 you need to
reference the correct System.Management.Automation.dll.

First add a reference tag from within the csproj file that you wish to
use the Automation library. To do this open the csproj file with a text
editor and locate the starting tag; <ItemGroup>.

Inbetween these tags you should see a bunch of <Reference Include>
tags. Locate the last </ItemGroup> tag and on the line before it place
<Reference Include="System.Management.Automation />

This will give you access to the System.Management.Automation.Remoting
namespace.

You can now use the WSManConnectionInfo class which allows you create
Remote Runspaces.

Enjoy!

<Code>

WSManConnectionInfo connectionInfo = new WSManConnectionInfo(
new Uri(liveIdconnectionUri),

"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
creds);

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);

</Code>


--
noli44

Eva2009

unread,
Jul 20, 2009, 3:29:15 PM7/20/09
to

Hi, for my internship, i'm developing a dll in c# to create and manage
users in outlook live.But i have some trouble with it.

After creating the runspace, i tried with basic cmdlets like "dir" but
i have an exception "The term 'dir' is not recognized as a cmdlet,
function, operable program, or script file. Verify the term and try
again." , the same message with other cmdlets.

There is the code:

public void create_runspace(String uri, String shelluri, String
username, String pass)
{
SecureString ss = ConvertToSecureString(pass);
PSCredential creds = new PSCredential(username, ss);


WSManConnectionInfo connectionInfo = new
WSManConnectionInfo(
new

Uri("https://pod51002psh.outlook.com/PowerShell-LiveID\0"),


"http://schemas.microsoft.com/powershell/Microsoft.Exchange", creds);
connectionInfo.AuthenticationMechanism =
AuthenticationMechanism.Basic;

Runspace runspace =
RunspaceFactory.CreateRunspace(connectionInfo);
runspace.Open();

Pipeline pipl = runspace.CreatePipeline("dir");
Collection<PSObject> results = pipl.Invoke();

foreach (PSObject res in results)
{
Console.WriteLine("{0}",
res.Members["Name"].Value);
}
runspace.Close();

}

And the call:

create_runspace("https://pod51002psh.outlook.com/PowerShell-LiveID\0","http://schemas.microsoft.com/powershell/Microsoft.Exchange",adminusername,adminpass);

When i call the WSManConnectionInfo Constructor without parameters it
works.

I didn't find much documentation on the net, so i woud be very grateful
if someone could help me to find what's wrong.

Thanks a lot.
Eva


--
Eva2009

Marco Shaw [MVP]

unread,
Jul 20, 2009, 6:24:02 PM7/20/09
to
"dir" is not a cmdlet. What happens in the PowerShell console is that dir
gets aliased to the get-childitem cmdlet.

So you'd have to set an alias in your custom host if you wanted to use "dir"
specifically.

Marco

"Eva2009" <gu...@unknown-email.com> wrote in message
news:5839e9a9e860887a...@nntp-gateway.com...

noli44

unread,
Jul 20, 2009, 6:15:43 PM7/20/09
to

Eva,

You will probably find that the command 'dir' is not a valid
command-let on the outlook live remote runspace.

Your best bet is to connect to outlook live via the powershell console
and run a 'Get-Command' in the remote runspace to see what is available
to you. This will give you the ability to experiment with the powershell
command-lets available. Once you figure out the command-let you would
like to run you can easily translate it into managed code.

btw if you create a runspace with the default constructor it will
connect to your local runspace (where you will be able to run 'dir').

Hope this helps

Anthony


--
noli44

Eva2009

unread,
Jul 22, 2009, 4:27:47 AM7/22/09
to

Thanks Anthony for the quick answer, i was doubting about the
connectionInfo part.
It works with other cmdlets.

I'm trying now to write into managed code the user add part from the
script csv_parser.ps1 :

%{Invoke-Command -Session $Script:RS {param
($name,$email,$firstName,$lastName,$displayName) new-mailuser -Name
$name -ExternalEmailAddress $email -FirstName $firstName -LastName
$lastName -DisplayName $displayName}-arg
$this_name,$this_email,$this_firstName,$this_lastName,$this_displayName}
> $results

i wrote this code for invoke-command:(i also tried with a basic command
to test, but i tested it in powershell, it works in remote)

Command myCommand = new Command("invoke-command");
myCommand.Parameters.Add(new CommandParameter("Computername",
"pod51002psh.outlook.com"));
myCommand.Parameters.Add(new CommandParameter("Credential", creds));
myCommand.Parameters.Add(new
CommandParameter("Scriptblock",@"{get-command}"));

i get this exception:

Cannot bind parameter 'ScriptBlock'. Cannot convert value
"{get-command}" to type "System.Management.Automation.ScriptBlock".
Error: "Invalid cast from 'System.String' to
'System.Management.Automation.ScriptBlock'."

the same exception when i replace with :

ScriptBlock sb=ScriptBlock.Create(@"{get-command}");
myCommand.Parameters.Add(new CommandParameter("Scriptblock",sb));

Does anyone have an idea how to deal with that? and is it the best way
to perform the user add?

thanks
Eva


--
Eva2009

noli44

unread,
Jul 22, 2009, 6:37:15 PM7/22/09
to

Eva,

The good news is you don't have to write script blocks or use the
invoke-command cmdlet. When you use the WSManConnectionInfo class to
create the runspace it is similar to using Enter-PSSession in a
powershell console. Therefore you can run commands directly within the
remote runspace without the need to invoke scripts.

eg. (assumes you have created the runspace)


Command myCommand = new Command("New-Mailbox");
myCommand.Parameters.Add("Name", mailbox_username);
myCommand.Parameters.Add("Password",
CreateSecureString(mailbox_password));
myCommand.Parameters.Add("WindowsLiveID", mailbox_emailaddress);
myCommand.Parameters.Add("FirstName", mailbox_firstname);
myCommand.Parameters.Add("LastName", mailbox_lastname);
myCommand.Parameters.Add("DisplayName", mailbox_displayname);
myCommand.Parameters.Add("Alias", mailbox_alias);

you can now run this command on a pipeline like so;

Pipeline pipl = runspace.CreatePipeline();

pipl.Commands.Add(myCommand);


Collection<PSObject> results = pipl.Invoke();


--
noli44

noli44

unread,
Jul 22, 2009, 7:14:29 PM7/22/09
to

Here is a full example of creating a remote runspace in C#

PSCredential credential = new PSCredential(admin_username,
CreateSecureString(password));

WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new
Uri(liveIdconnectionUri1),
"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

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

// get all mailboxes starting with 'a'
Pipeline pipeline = runspace.CreatePipeline();
Command command = new Command("Get-Mailbox");
command.Parameters.Add("Identity", "a*");

pipeline.Commands.Add(command);
IList<PSObject> list = pipeline.Invoke();

foreach (var psObj in list)
{
// cycle through returned objects
}
}


--
noli44

Eva2009

unread,
Jul 22, 2009, 7:03:24 PM7/22/09
to

Thanks so much Anthony .It works now :)


--
Eva2009

Reply all
Reply to author
Forward
0 new messages