Copy-Item -ToSession but with C#

This topic contains 3 replies, has 2 voices, and was last updated by  Don Jones 1 month, 1 week ago.

  • Author
    Posts
  • #87997

    Justin Mathews
    Participant

    Hi all,

    I'm looking to use C# to accomplish what Copy-Item with the -ToSession parameter does. PowerShell is my tool of choice but in this context I'm attempting to replace C# code that uses ftp to transfer a file. I'd like to basically call the Copy-Item command and pass it a PSSession object. I have 95% of this working and have managed to copy the file locally, but have hit a road block with the PSSession. I can't seem to create one of type System.Management.Automation.Runspaces.PSSession. I've tried everything I have been able to find on the old interwebs but still can't seem to get it to work. Below is a snippet of code and if anyone out there has done this, some help would be greatly appreciated.

    Thanks,
    Justin

    //Create a PowerShell object and load the New-PSSession command
    PowerShell pshsession = PowerShell.Create();
    pshsession.AddCommand("New-PSSession");
    pshsession.AddParameter("ComputerName", computer);
    pshsession.AddParameter("Credential", cred);
    
    //Create a second PowerShell object and load the Copy-Item command
    PowerShell localpsh = PowerShell.Create();
    localpsh.AddCommand("Copy-Item");
    localpsh.AddParameter("Path", "C:\\temp\\todo.txt");
    localpsh.AddParameter("Destination", "C:\\temp\\todo.txt");
    //The idea here is to create the PSSession as the parameter is called. I was hoping this would force it to type PSSession
    //but it hasn't worked. I've also tried to implicitly convert it but I get an exception that it can't do that.
    localpsh.AddParameter("ToSession", pshsession.Invoke());
    localpsh.AddParameter("Force", true);
    localpsh.Invoke();
    
  • #88870

    Don Jones
    Keymaster

    I hate to do this, but I think you've kind of exceeded this community's purpose ;). We don't get a lot of C# coders here.

    My guess is that you can't "copy" a PSSession object between PowerShell engines – that would require a level of marshaling that I suspect doesn't exist. When we do this in the shell, we're doing it inside a single engine, and persisting (usually) the PSSession in a variable within that engine's VARIABLE: drive. That's a lot harder to gin up in your own host.

  • #88892

    Justin Mathews
    Participant

    Thanks for reply Don. I actually figured out a way to do this yesterday and if anyone is interested in it I've posted the code below. Now, I totally agree that this is way easier and should be done in PowerShell. For this case I ran into a problem that we wanted to solve with PowerShell but we already had a C# program that handled the "legacy" version of it along with a bunch of other stuff. I could have called a PS script that did it all but our first option was to have C# code that did the job for us. And I passes this of to a C# developer so if it looks terrible, then it probably is but he should be able to clean it up 🙂

    -Justin

    // Username and Password for the remote machine.
    var userName = "DOMAIN\\USERNAME";
    string pw = "MYSECRETPWASPLAINTEXT";
    
    // Path to the files are to be copied
    string path = "C:\\temp\\stuff.txt";
    
    // Path on the remote machine where the files need to be placed
    string destination = "C:\\temp\\mystuff.txt";
    
    //ComputerName
    string computerName = "REMOTESERVER"
    
    // Creates a secure string for the password
    SecureString securePassword = new SecureString();
    foreach (char c in pw)
    {
        securePassword.AppendChar(c);
    }
    securePassword.MakeReadOnly();
    
    // Creates a PSCredential object
    PSCredential creds = new PSCredential(userName, securePassword);
    
    // Creates the runspace for PowerShell
    Runspace runspace = RunspaceFactory.CreateRunspace();
    
    // Create the PSSession connection to the remote machine.
    PowerShell powershell = PowerShell.Create();
    PSCommand command = new PSCommand();
    command.AddCommand("New-PSSession");
    command.AddParameter("ComputerName", computerName);
    command.AddParameter("Credential", creds);
    powershell.Commands = command;
    runspace.Open(); powershell.Runspace = runspace;
    Collection result = powershell.Invoke();
    
    // Takes the PSSession object and places it into a PowerShell variable
    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();
    
    // Calls the Copy-Item cmdlet as a script and passes the PSSession, Path and destination parameters to it
    powershell = PowerShell.Create();
    command = new PSCommand();
    command.AddScript("Copy-Item -Path " + path + " -Destination " + destination + " -ToSession $ra");
    powershell.Commands = command;
    powershell.Runspace = runspace;
    powershell.Invoke();
    
  • #88895

    Don Jones
    Keymaster

    Another option would be to refactor the legacy C# code into a PowerShell cmdlet, which is pretty trivial in terms of work, usually. Then you've got a compiled module you can just load directly into the shell. Thanks for sharing the solution code!

You must be logged in to reply to this topic.