We are currently planning our migration from a Windows XP network to Windows 7. Included in all of this we are setting up a new domain and building a new Exchange 2010 cluster.

I am trying to create a C# Windows application that will do all of user migration for our engineers to save time. It will modify any databases that they have access to, move home folders etc. What i want to incorporate into this is migrate exchange mailboxes from the 2007 to 2010 servers (cross-forest).

Ive read a few articles about executing powershell scripts remotely but i dont seem to be having much success with the Prepare-MoveRequest.ps1 script.

Here is my code:


var RemoteUsername = "OLDDOMAIN\\Admin";
var RemotePassword = "OLDPASSWORD";
var ssRPassword = new SecureString();
Array.ForEach(RemotePassword.ToCharArray(), ssRPassword.AppendChar);
var RemoteCredential = new PSCredential(RemoteUsername, ssRPassword);

var LocalUsername = "NEWDOMAIN\\admin";
var LocalPassword = "NEWPASSWORD";
var ssLPassword = new SecureString();
Array.ForEach(LocalPassword.ToCharArray(), ssLPassword.AppendChar);
var LocalCredential = new PSCredential(LocalUsername, ssLPassword);


System.Uri uri = new Uri("http://email-cas1/powershell");

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", uri);
command.AddParameter("Credential", LocalCredential);
command.AddParameter("Authentication", "Basic");
PSSessionOption sessionOption = new PSSessionOption();
sessionOption.SkipCACheck = true;
sessionOption.SkipCNCheck = true;
sessionOption.SkipRevocationCheck = true;
command.AddParameter("SessionOption", sessionOption);

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
Collection<PSSession> result = powershell.Invoke<PSSession>();

foreach (ErrorRecord current in powershell.Streams.Error)
{
Console.WriteLine("Exception: " + current.Exception.ToString());
Console.WriteLine("Inner Exception: " + 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();



// First import the cmdlets in the current runspace (using Import-PSSession)
powershell = PowerShell.Create();
command = new PSCommand();
command.AddScript("Import-PSSession -Session $ra");
powershell.Commands = command;
powershell.Runspace = runspace;
powershell.Invoke();



// Now run get-ExchangeServer
System.Collections.ObjectModel.Collection<PSObject> results = new System.Collections.ObjectModel.Collection<PSObject>();

powershell = PowerShell.Create();
powershell.Runspace = runspace;



//Change the Path to the Script to suit your needs
System.IO.StreamReader sr = new System.IO.StreamReader(Environment.CurrentDirectory + "\\Prepare-MoveRequest.ps1");
powershell.AddScript(sr.ReadToEnd());

powershell.AddParameter("TargetMailUserOU", comboBox1.Text);

powershell.Runspace.SessionStateProxy.SetVariable("RemoteForestCredential", RemoteCredential);
powershell.Runspace.SessionStateProxy.SetVariable("LocalForestCredential", LocalCredential);
powershell.AddParameter("Identity", "Mailbox.Test");
powershell.AddParameter("RemoteForestDomainController", "OLDDC.avonfirebrigade.gov.uk");
powershell.AddParameter("LocalForestDomainController", "NEWDC.afrs.gov.uk");

results = powershell.Invoke();

if (powershell.Streams.Error.Count > 1)
{
foreach (ErrorRecord er in powershell.Streams.Error)
Console.WriteLine(er.ErrorDetails);
}
else
{
foreach (PSObject ps in results)
{
txtMigrateStatus.Text = ps.ToString();
}
}
}
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;
}


When it hits the results = powershell.Invoke() line, i get the following error:

Exception calling "SecureStringToBSTR" with "1" argument(s): "Value cannot be null.
Parameter name: s"


Does anybody have any experience with sending secure strings across the remote powershell host? Or if you can spot anything im doing wrong.

Im not looking forward to migrating 1200 users manually.