1

I am new to PowerShell. I am trying to execute PowerShell script from C#. PS Script that I have written transfer xml file from host computer (running PS script) to a remote computer. Script is as follows

$Username = "User"
$Password = "Pass"
$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force"
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList     $Username,$SecurePass"
 $contents = [IO.File]::ReadAllBytes("D:\MyFile.xml")
 Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes("D:\MyFile.xml",$using:contents)}

I store username and password of remote computer, to which I want to transfer my file, in variables. Convert password to secure string and create a credential object containing username and password. Use credential to copy the contents of file from my computer to remote computer. This code works fine if I execute these commands one by one from my powershell or by running these lines from a .ps1 powershell script from my computer.

I used following code in C# to execute above script and transfer the file.

        Runspace runSpace = RunspaceFactory.CreateRunspace();

        Pipeline pipeline = runSpace.CreatePipeline();

        runSpace.Open();

        string script = "";
        script = "$Username = \"User\"\n";
        script = script + "$Password = \"Pass\"\n";
        script = script + "$SecurePass  = ConvertTo-SecureString -AsPlainText $Password -Force\n";
        script = script + "$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass\n";
        script = script + "$contents = [IO.File]::ReadAllBytes(\"D:\\MyFile.xml\")\n";
        script = script + "Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes(\"D:\\MyFile.xml\",$using:contents)}\n";

        //   pipeline.Runspace.SessionStateProxy.SetVariable

        pipeline.AddScript(script);


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

However, it did not transfer the file. So, I thought I need to Add each line in script one by one to the pipeline and then execute it. So I changed it to following.

        string[] commands = script.Split('\n');

        foreach (string command in commands)
        {
            pipeline.AddScript(command);
        }

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

It did not work either. I executed single Invoke-Command from C# code on my computer and it worked fine. Also, if I try to execute other single PS commands they work. So I assessed that setting variable values like this $User = "user" is not working through C#.

I used SessionStateSetProcy.SetVariable to Store variable values in powershell session as follows.

        pipeline.Runspace.SessionStateProxy.SetVariable("User", "User");
        pipeline.Runspace.SessionStateProxy.SetVariable("Password", "Pass");
        var value = pipeline.Runspace.SessionStateProxy.GetVariable("Password");
        Console.WriteLine(value);

So, I was able to set value and retrieve as well. But, since I was using Other cmdlets like ConvertTo-SecureString, I realized I need to execute these cmdlets separately in my code.

I finally I came up with following piece of code.

        pipeline.Runspace.SessionStateProxy.SetVariable("User", "User");
        pipeline.Runspace.SessionStateProxy.SetVariable("Password", "Pass");



        Command command = new Command("ConvertTo-SecureString");
        string pass_value = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("Password").ToString();
        command.Parameters.Add("AsPlainText",pass_value);
        command.Parameters.Add("Force");

        pipeline.Runspace.SessionStateProxy.SetVariable("SecurePass",command);

        /*Build a command2*/
        Command command1 = new Command("New-Object");
        string user_name = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("User").ToString();
        command1.Parameters.Add("TypeName", "System.Management.Automation.PSCredential");
        string args = user_name + " , "+pass_value;
        command1.Parameters.Add("-ArgumentList", args);


        pipeline.Runspace.SessionStateProxy.SetVariable("Cred", command1);

        byte[] wifiXML = System.IO.File.ReadAllBytes(@"D:\MyFile.xml");
        pipeline.Runspace.SessionStateProxy.SetVariable("contents", wifiXML);


        Object a = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("contents");
        string script = "Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes(\"D:\MyFile.xml\",$using:contents)}";

        pipeline.Commands.AddScript(script);
        Collection<PSObject> results = pipeline.Invoke();

It gave some weird error in System.Management.Automation. I know in SetVariable I am doing it wrong by passing command variable.

I have tried PowerShell ps = PowerShell.Create() instead of pipeline as well and tried ps.AddCommand and ps.AddParameters followed by ps.AddArgument as well. But I am not sure that once I run cmdlet ConvertTo-SecureString through ps.AddCommad, ps.AddArgument etc., how should I set it's output value to $Cred variable?

I am aware that solution to this won't be this complicated and someone must have executed multiple line powershell script through C#. All I want to accomplish is execute the .ps1 script lines mentioned in very start from C# code.I have tried bunch of methods including mentioned above to no use, so any help related to it would be much appreciated?

1 Answer 1

5

Try in this way

 string script = @"
$Username = 'User'
$Password = 'Password'
$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList     $Username,$SecurePass
$contents = [IO.File]::ReadAllBytes('D:\MyFile.xml')
Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes('D:\\MyFile.xml',$using:contents)}
";

 pipeline.Commands.AddScript(script);
 Collection<PSObject> results = pipeline.Invoke();
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much for your answer. It works fine in C#. Transfers the file to remote computer. However, I am using IronPython and passing the script from IronPtyhon to C# and run in PS inside C#. It doesn't work in that case and it is exactly Invoke-Command that does not work. Anyways, thanks and your solution works in C#.
Why is all this powershell c# stuff not more intuitive??

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.