Invoked PS command to string - c#

Is there way to convert invoked powershell command from C# to string?.
Let's say for example i have something like this:
PowerShell ps = PowerShell.Create();
ps.AddCommand("Add-VpnConnection");
ps.AddParameter("Name", "VPN_" + ClientName);
ps.AddParameter("ServerAddress", VPN_SERVER_IP);
ps.AddParameter("AllUserConnection");
ps.AddParameter("SplitTunneling", true);
ps.AddParameter("TunnelType", "L2tp");
And i would like to save invoked command to log file.
Can i somehow return whole command as string?

I believe what you want essentially is this.
PowerShell ps = PowerShell.Create();
ps.AddScript($"Add-VpnConnection -Name \"VPN_{ClientName}\" -ServerAddress {VPNServerIP} -AllUserConnection -SplitTunneling -TunnelType L2tp");
ps.Invoke();
The invoke return will contain a collection of PSObject so you can read it and save the information like you want in a log in c#.

Note: This answer does not solve the OP's problem. Instead, it shows how to capture a PowerShell command's output as a string in C#, formatted in the same way that the command's output would print to the display (console), if it were run in an interactive PowerShell session.
Out-String is the cmdlet that produces formatted, for-display representations of output objects as strings, as they would print to the screen in a PowerShell console.
Therefore, you simply need to use another .AddCommand() in order to pipe the output from your Add-VpnConnection call to Out-String:
string formattedOutput;
using (PowerShell ps = PowerShell.Create())
{
ps.AddCommand("Add-VpnConnection")
.AddParameter("Name", "VPN_" + ClientName)
.AddParameter("ServerAddress")
.AddParameter("AllUserConnection", VPN_SERVER_IP)
.AddParameter("SplitTunneling", true)
.AddParameter("TunnelType", "L2tp");
// Add an Out-String call to which the previous command's output is piped to.
// Use a -Width argument (column count) large enough to show all data.
ps.AddCommand("Out-String").AddParameter("Width", 512);
// Due to use of Out-String, a *single string* is effectively returned,
// as the only element of the output collection.
formattedOutput = ps.Invoke<string>()[0];
}
Console.Write(formattedOutput);

Related

Running the PowerShell Script from the c#

I am trying to call PowerShell ISE Script from the C#.
I have command that I am running it on the PowerShell
. .\Commands.ps1; Set-Product -bProduct 'Reg' -IPPoint 'ServerAddress' -Location 'testlocation' -Terminal 3
Now I am trying to create the Command with the c# I have wrote some code Like this.
//Set Execution Policy to un restrict
powershell.AddCommand("Set-ExecutionPolicy");
powershell.AddArgument("unrestricted");
powershell.Invoke();
powershell.Commands.Clear();
powershell.AddScript("K:\\Auto\\Cases\\Location\\Commands.ps1", false);
powershell.AddArgument("Set-Product").AddParameter("bProduct ", "Reg").
AddParameter("IPPoint", "ServerAddress").
AddParameter("Location", "testlocation").AddParameter("Terminal", 3);
powershell.Invoke();
I can see its running fine. But its not updating values in my xml file. It suppose to update my values in file. When I try to run it with powershell It does run and works file. But c# code does not work.
Any hint or clue will be appreciated.
Mind the semicolon, so this is basically two statements:
1.) Dot-sourcing the script Commands.ps1
. .\Commands.ps1
2.) Invoking the cmdlet Set-Product
Set-Product -bProduct 'Reg' -IPPoint 'ServerAddress' -Location 'testlocation' -Terminal 3
So, you have to treat them as such. Also, AddScript expects code, not a file name.
powershell
// dot-source the script
.AddScript(#". 'K:\Auto\Cases\Location\Commands.ps1'")
// this is the semicolon = add another statement
.AddStatement()
// add the cmdlet
.AddCommand("Set-Product")
.AddParameter("bProduct", "Reg")
.AddParameter("IPPoint", "ServerAddress")
.AddParameter("Location", "testlocation")
.AddParameter("Terminal", 3)
// invoke all statements
.Invoke();
(Alternatively to AddStatement() you can of course split this up in two calls and call Invoke() twice.)

Launching powershell scripts (with parameters) from c# app

I'm trying to make a simple (or so i thought) app that will make it easier to launch .ps1 scripts, so that non-powershell savvy users can use them.
Here is how its supposed to look like
Now, i managed to figure out one part about running scripts:
private string RunPowershell_1(string skripta)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (string str in PowerShell.Create().AddScript(skripta).AddCommand("Out-String").Invoke<string>())
{
stringBuilder.AppendLine(str);
}
return stringBuilder.ToString();
}
But i would normally run scripts that require parameters, so i would like to be able to read list of parameters from the script i import, assign value to them and then run the scrip (output should go to either txtPreview or to a file).
Is there a way to do this?
If there is another (better) approach to this I'm all ears.
You can use this powershell command to get the list of parameters.
(get-command get-netadapter).Parameters
And this is how you can read its output in C#:
using (PowerShell ps = PowerShell.Create())
{
ps.AddScript("...");
Collection<PSObject> output = ps.Invoke();
}

Using "New-Object" cmdlet in C#

I'm trying to run some Powershell cmdlet in a C# program I've been working on.
The cmdlet I've been trying to run is the following:
$cred = New-Object System.Management.Automation.PSCredential (user, (ConvertTo-SecureString pass –ASPlainText –Force));
And what I did in my C# programm was the following:
string user = textBox1.Text;
string pass = textBox2.Text;
PowerShell ps = PowerShell.Create();
ps.AddCommand("New-Object");
ps.AddArgument("System.Management.Automation.PSCredential ("+user+", (ConvertTo-SecureString "+pass+" –ASPlainText –Force))");
var cred = ps.Invoke();
But when i do this I get prompted with the following error:
A constructor was not found. Cannot find an appropriate constructor for type System.Management.Automation.PSCredential (user, (ConvertTo-SecureString pass –ASPlainText –Force)).
So my question is, how can I run this Powershell cmdlet from my C# program, and store the result in a variable inside the C# program?
Thank you!
This can be done without invoking powershell. Whether thats useful or not depends upon what you are up to.
var user = "username";
var pass = new System.Security.SecureString();
foreach (char c in "password")
{
pass.AppendChar(c);
}
var cred = new System.Management.Automation.PSCredential(user, pass);
Martin Brown's helpful answer is definitely the best solution in your case.
As for what you tried:
You didn't correctly translate your PowerShell command to a PowerShell SDK call.
Specifically, the way you add arguments is incorrect:
ps.AddArgument("System.Management.Automation.PSCredential ("+user+", (ConvertTo-SecureString "+pass+" –ASPlainText –Force))");
You must add arguments one by one, via .AddArgument(<val>), or, preferably, as named parameters via .AddParameter(<name>, <val>)).
You cannot use embedded PowerShell commands as arguments.
If we leave the issue of obtaining a SecureString instance aside and use just a dummy instance, this is what your statement would have to look like:
ps.AddCommand("New-Object")
.AddParameter("TypeName", "System.Management.Automation.PSCredential")
.AddParameter("ArgumentList", new object[] { user, new System.Security.SecureString() });
Note the use of parameter names and how parameter -ArgumentList must be passed as an array.
If you do need to execute PowerShell code via the SDK, use the .AddScript() method instead, but note that you can only pass a single string that contains the code to execute (note the use of an interpolated C# string, $"..." for embedding C# variable values):
ps.AddScript(
$"New-Object PSCredential \"{user}\", (ConvertTo-SecureString \"{pass}\" –AsPlainText –Force)"
);
Caveat: Unlike a command added with .AddCommand(), an .AddScript()-added command always fails silently on execution with .Invoke() - no exception occurs; you'll have to inspect ps.HadErrors and ps.Streams.Error to check for errors. By contrast, .AddCommand() does throw an exception if the target command reports a (statement-)terminating error (though these are rare; an example is passing an invalid parameter name).

C# - Utilize Quest active directory cmdlets for Powershell by calling PS script

C# code (Source):
private string RunScript(string scriptText)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// add an extra command to transform the script
// output objects into nicely formatted strings
// remove this line to get the actual objects
// that the script returns. For example, the script
// "Get-Process" returns a collection
// of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
Collection<psobject /> results = pipeline.Invoke();
// close the runspace
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
}
Powershell code
#Dummy code for example purpose
ASNP Quest*
#Example of cmdlet I want to use
$Users = Get-QADGroupMember -Identity $Group -Enabled
return $Users.count
As you can see, my goal is to call the script utilizing RunScript above in a Button_Click event in my WPF app. I've been able to correctly call the script but the call to Quest cmdlets clearly doesn't go trough as wanted since I would receive 0 in the above example.
TL;DR
Script is running correctly but calls to Quest cmdlets don't work since it return nothing (or 0 in the above example). Is there something I'm missing ?
EDIT
Important to note that the exact same script ran in Powershell returns the correct values. Calling it from C# don't.

Trouble with calling PS script (with parameter) from C#

I am trying to setup a simple .aspx web page that will accept a user's input of a string (later, more than one string) and use that string as the parameter value for a Powershell script.
The PS script looks like this right now:
[CmdletBinding()]
param (
[string] $ServiceName
)
$ServiceName | out-file c:\it\test.txt
$ServiceName | Out-String
The C# code looks like this:
var shell = PowerShell.Create();
// Add the script to the PowerShell object
shell.Commands.AddScript("C:\\it\\test.ps1 -ServiceName BITS");
// Execute the script
var results = shell.Invoke();
When I run that, I get "BITS" written to the test.txt file. What I need to do now, is setup the application to call the script, passing in the "ServiceName" parameter. I found this: Call PowerShell script file with parameters in C# and tried the following code:
PowerShell ps = PowerShell.Create();
ps.AddScript(#"c:\it\test.ps1").AddParameter("ServiceName", "BITS");
var results = ps.Invoke();
In this case, the script was called and the test.txt file was created, but the value (BITS) was not written to the file. What am I missing here? Why isn't the parameter being passed to the script?
Thanks.
I ended up using
var ps = #"C:\it\test.ps1";
processInfo = new ProcessStartInfo("powershell.exe", "-File " + ps + " -ServiceName BITS);
I don't like this as much, but it works. shrug
Here are three possible solutions for future readers including me:
using (PowerShell ps = PowerShell.Create())
{
//Solution #1
//ps.AddCommand(#"C:\it\test.ps1", true).AddParameter("ServiceName", "BITS");
//Solution #2
//ps.AddScript(#"C:\it\test.ps1 -ServiceName 'BITS'", true);
//Solution #3
ps.AddScript(File.ReadAllText(#"C:\it\test.ps1"), true).AddParameter("ServiceName", "BITS");
Collection<PSObject> results = ps.Invoke();
}
I haven't seen solution #3 documented anywhere else, though I got the idea for it from https://blogs.msdn.microsoft.com/kebab/2014/04/28/executing-powershell-scripts-from-c/

Categories