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();
}
Related
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);
I know we can can run physical powershell script from c# but how to create powershell script file itself dynamically based on input params.
Kindly suggest.
you can execute powershell script/ commands directly from your c# project.
you'll need to add a reference to system.managment.automation dll in project's references.
example:
function that will take script as a string , execute it and then returns a result:
private string RunScript(string script)
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipline = runspace.CreatePipeline();
pipline.Commands.AddScript(script);
pipline.Commands.Add("Out-String");
Collection<PSObject> results = pipline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach(PSObject pSObject in results)
{
stringBuilder.AppendLine(pSObject.ToString());
}
return stringBuilder.ToString();
}
function call be like:
Console.WriteLine(RunScript("Your Powershell Script"));
you can check out the full code examples on github - PowershellCommand-CSharp
TLDW: To support a specific process I need to go from a powershell instances/module to kick off a known safe executable for some c# magic, which then in turn needs to kick and execute a powershell script before exiting.
Powershell Entry point
{
Known Good Exe
{
Powershell Work To Do
}
}
Now ideally, this would all run from a single console instance so that all of the output is simple to look at. The exe -> powershell logging all works fine and as expected when using powershell.Streams.... The write-hosts in the powershell work all show up in the console and I get all the info I want.
powerShell.Streams.Information.DataAdded += LogMessage;
The problem comes when the outer powershell module is introduced. This one is needed because the parent process and execution environment this is running from is powershell. Once this whole stack is started from within a powershell instance, I get console logging from the outer powershell, and from the exe. BUT all of the write-hosts from the inner powershell modules disappear.
I've tried disabling the stream redirects, and a few other things, but this isn't resolving in the manner I would hope. I'm hoping someone knows if there is a way to get this to work as it solves so many problems if it just would.
PowerShell Outer:
$C#Exe = Join-Path $PSScriptRoot $C#ExePath
$C#Args = #()
Write-Host "Hello world" # will show up
& $C#Exe $C#Args
C# Exe Code:
public static void Main(string[] args)
{
Console.WriteLine("Hello world"); #Will show up
var powerShell = PowerShell.Create().AddScript("PowerShellInner.ps1");
powerShell.Streams.Information.DataAdded += LogMessage<InformationRecord>;
powerShell.Streams.Warning.DataAdded += LogMessage<WarningRecord>;
powerShell.Streams.Error.DataAdded += LogMessage<ErrorRecord>;
powerShell.Streams.Verbose.DataAdded += LogMessage<VerboseRecord>;
powerShell.Streams.Debug.DataAdded += LogMessage<DebugRecord>;
StringBuilder resultString = new StringBuilder();
foreach (dynamic item in powerShell.Invoke().ToList())
{
resultString.AppendLine(item.ToString());
}
Console.WriteLine(resultString.ToString());
}
private static void LogMessage<T>(object sender, DataAddedEventArgs e)
{
var data = (sender as PSDataCollection<T>)[e.Index];
Console.WriteLine($"[{typeof(T).Name}] {Convert.ToString(data)}");
}
PowerShell Inner:
Write-Host "Hello world" #Wont show up
UPDATE on 1/22/2020
I can't fully explain why you're experiencing what you're seeing, but the following code works.
Output from executing PowerShellOuter.ps1
Code
Notes:
Your c# program doesn't show any code manipulating input arguments,
so I didn't model any input args
You mis-used the AddScript method. It needs the text of the script, not the script name
The code below assumes that the c# exe and the two PS scripts are in the same folder
In PowerShellInner.ps1, use write-output. write-host does not output data to PowerShell Objectflow Engine but rather, as the name implies, writes directly to the host and sends nothing to the PowerShell engine to be forwarded to commands later in the pipeline. See Write-Output or Write-Host in PowerShell
PowerShellOuter.ps1
cls
$CSharpExe = Join-Path $PSScriptRoot "PowerShellExecutionSample.exe"
Write-Host "Hello from PowerShellOuter.ps1"
&$CSharpExe
pause
PowerShellInner.ps1
Write-Output "Hello from PowerShellInner.ps1"
Code for PowerShellExecutionSample.exe
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Management.Automation;
namespace PowerShellExecutionSample
{
class Program
{
static void Main(string[] args)
{
PowerShellExecutor t = new PowerShellExecutor();
t.ExecuteSynchronously();
}
}
/// <summary>
/// Provides PowerShell script execution examples
/// </summary>
class PowerShellExecutor
{
public void ExecuteSynchronously()
{
using (PowerShell PowerShellInstance = PowerShell.Create())
{
//You mis-used the AddScript method. It needs the text of the script, not the script name
string scriptText = File.ReadAllText(string.Format(#"{0}\PowerShellInner.ps1", Environment.CurrentDirectory));
PowerShellInstance.AddScript(scriptText);
Collection <PSObject> PSOutput = PowerShellInstance.Invoke();
// loop through each output object item
foreach (PSObject outputItem in PSOutput)
{
// if null object was dumped to the pipeline during the script then a null
// object may be present here. check for null to prevent potential NRE.
if (outputItem != null)
{
Console.WriteLine(outputItem.BaseObject.ToString() + "\n");
}
}
}
}
}
}
Original Answer on 1/21/2020
Updating your question to break out your code out helped - thanks.
I think you have two issues:
1) Modify your c# program to obtain the streams coming from PowerShell Inner and make your c# program re-emit the data from the PowerShell Inner output streams. Here is a Microsoft blog entry I used to crack the same nut: Executing PowerShell scripts from C#
2) Modify your PowerShell Outer to obtain the streams coming from the c# program. Here is a blog entry that seems to crack that nut: How to redirect output of console program to a file in PowerShell. The heart of this is to execute the following from your PowerShell Outer:
cmd /c XXX.exe ^>log.txt 2^>^&1
Note: The ^ are really backticks
I have a C# application that runs PowerShell scripts after reading them off database as strings. Assume script1, script2 and utilityfunctions are scripts read from database.
var rs = RunspaceFactory.CreateRunspace();
rs.Open();
var ps = PowerShell.Create();
ps.Runspace = rs;
ps.AddScript(utilityFunctions);
ps.AddScript(script1);
Is there anyway possible that I can call functions in utilityFuntions from script1? I have tried using $MyInvocation and go through its properties but did not find anything useful. When going through ps.Commands properties, I can of course see two items but when going through commands within the script, I cannot get to anything inside utilityFunctions. I guess I can read all scripts and concatenate them as string and only pass one script but I am just wondering if there is a way to do it without string concatenation.
I am looking at creating a small interpreter for C# that I could load in some applications. Something that could be able to run things like this:
> var arr = new[] { 1.5,2.0 };
arr = { 1.5, 2.0 }
> var sum = arr.Sum();
sum = 3.5
And so I was thinking this could be achieved by creating a dictionary of all the variables and their types and then compile each of the row as they come, and then execute the function, get the result and stick it in the dictionary of variables.
However, it seems to me that this may be actually quite difficult to build and possibly very inefficient.
Then I thought that Powershell was doing what I needed. But how is it done? Can anyone enlighten me as to how Powershell works or what a good way would be to build a .Net interpreter?
How about you host the PowerShell engine in your application and let it do the interpreting for you? For example:
private static void Main(string[] args)
{
// Call the PowerShell.Create() method to create an
// empty pipeline.
PowerShell ps = PowerShell.Create();
// Call the PowerShell.AddScript(string) method to add
// some PowerShell script to execute.
ps.AddScript("$arr = 1.5,2.0"); # Execute each line read from prompt
// Call the PowerShell.Invoke() method to run the
// commands of the pipeline.
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result.ToString());
}
}
If your goal is to learn how to build an interpreter, have a look at the interpreter pattern.
Look at either Roslyn http://msdn.microsoft.com/en-US/roslyn or Mono Compiler http://www.mono-project.com/CSharp_Compiler . Both should be able to do what you are looking for
Somthing like this? Or perhaps Roslyn(http://msdn.microsoft.com/en-gb/roslyn)
Added comment as answer, as it seems more useful than I first thought