wraping powershell script from C# - c#

i'm trying to wrap an already made powershell script with C# GUI client.
the script acts according to inputs from the user, and it has also outputs according to them.
i would like to get the powershell outputs, display them to my C# client and
and then to enter the input according to his choice.
this is my script, and the code below is what i came up to but it doesn't work.
(i can only get the output at the end, and only if i don't have Read-Host in my powershell script). hope that it helps some how.
Write-Host
Write-Host 'Hello World!'
Write-Host "Good-bye World! `n"
$option = Read-Host "Please enter your number"
switch ($option){
"1"
{
Write-Host "jack"
}
"2"
{
Write-Host "john"
}
"3"
{
Write-Host "joe"
}
}
public void WrapPowerShell()
{
string directory = Directory.GetCurrentDirectory();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = #"powershell.exe";
startInfo.Arguments = string.Format(#"& '{0}'", directory + #"\hello.ps1");
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = false;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
string output = process.StandardOutput.ReadToEnd();
// Assert.IsTrue(output.Contains("StringToBeVerifiedInAUnitTest"));
string errors = process.StandardError.ReadToEnd();
}
Big Thanks in advance For your answers!!

This is a helper class that I wrote to help me create an ASP.NET web app that allowed people to run powershell scripts.
PSHelper.cs
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
namespace admin_scripts {
public class PSHelper {
// See the _ps_user_lookup method for demonstration of using this method
public static Runspace new_runspace() {
InitialSessionState init_state = InitialSessionState.CreateDefault();
init_state.ThreadOptions = PSThreadOptions.UseCurrentThread;
init_state.ImportPSModule(new[] { "ActiveDirectory", "C:\\ps_modules\\disable_user.psm1" });
// Custom PS module containing functions related
// to disabling AD accounts at work.
// You would use your own module here, obviously.
return RunspaceFactory.CreateRunspace(init_state);
}
// This method is for dead-simple scripts that you only want text output from
// Not as flexible as the previous method, but it's good for the really simple cases
public static string run_simple_script( string body ) {
Runspace runspace = new_runspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(body);
pipeline.Commands.Add("Out-String");
Collection<PSObject> output = pipeline.Invoke();
runspace.Close();
StringBuilder sb = new StringBuilder();
foreach( PSObject line in output ) {
sb.AppendLine(line.ToString());
}
return sb.ToString();
}
}
}
I would invoke a script from an ASP.NET handler like so:
private PSObject _ps_user_lookup( string id_param ) {
Runspace runspace = PSHelper.new_runspace();
runspace.Open();
using( Pipeline pipe = runspace.CreatePipeline() ) {
Command cmd = new Command("Get-ADUser");
cmd.Parameters.Add(new CommandParameter("Identity", id_param));
cmd.Parameters.Add(new CommandParameter("Properties", "*"));
cmd.Parameters.Add(new CommandParameter("Server", "REDACTED"));
pipe.Commands.Add(cmd);
return pipe.Invoke().FirstOrDefault();
}
}
The real beauty of this arrangement is that the web application would run the script using the credentials of the web user's domain account. I hope you find it useful.

Related

How to call Powershell script using web API (cs or asp.net)

Below is the function to keep server in SCOM maintenance mode and I would like to call this function through cs or asp.net as API call by passing variables.
function set-scomderegister {
param(
[Parameter( Mandatory = $True, ValueFromPipeline = $true)][string]
$SCOMServer,
[Parameter( Mandatory = $True, ValueFromPipeline = $true)]
$Computername
)
ForEach($Comp in $Computername)
{
New-SCManagementGroupConnection -ComputerName $SCOMServer
$numberOfMin = 100
$ReasonComment = "Server got docomissioned "
$Instance = Get-SCOMClassInstance -Name $Comp
$Time = ((Get-Date).AddMinutes($numberOfMin))
Start-SCOMMaintenanceMode -Instance $Instance -EndTime $Time -Comment $ReasonComment -Reason PlannedOther;
}
}
System.Management.Automation namespace would be useful for you.
You can install a nuget package "System.Management.Automation".
Once this is installed you will have this namespace available.
You can invoke a script with parameter as shown below:
public void RunWithParameters()
{
// create empty pipeline
PowerShell ps = PowerShell.Create();
// add command
ps.AddCommand("test-path").AddParameter("Path", Environment.CurrentDirectory); ;
var obj = ps.Invoke();
}
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();
}
There is another option to use Process.Start to start the powershell prompt. Then pass the file path to the process.
public static int RunPowershellScript(string ps)
{
int errorLevel;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo("powershell.exe", "-File " + ps);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
process = Process.Start(processInfo);
process.WaitForExit();
errorLevel = process.ExitCode;
process.Close();
return errorLevel;
}
Hope this helps.

How to input parameters without using command-line parameters

I need to launch program jphide (https://github.com/h3xx/jphs) many times in the cycle
Example:
jphide input-jpeg-file output-jpeg-file file-to-be-hidden
I tryed to use command line to transmit the parameters to program
public static void Hide(string imagePath, string passWord)
{
Collection<PSObject> results;
string fullPath = System.IO.Path.GetFullPath(imagePath);
string pathToOutFile = ".\\outImages\\" + imagePath;
pathToOutFile = System.IO.Path.GetFullPath(pathToOutFile);
System.IO.File.WriteAllLines("test.txt", new string[] { passWord });
string hiddenFile = System.IO.Path.GetFullPath(#".\test.txt");
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
//Here's how you add a new script with arguments
Command myCommand = new Command(#".\script.bat");
CommandParameter pathParameter = new CommandParameter(fullPath);
CommandParameter passWordParameter = new CommandParameter(passWord);
CommandParameter pathToOutputFileParameter = new CommandParameter(pathToOutFile);
CommandParameter pathToHiddenFileParameter = new CommandParameter(hiddenFile);
myCommand.Parameters.Add(pathParameter);
myCommand.Parameters.Add(passWordParameter);
myCommand.Parameters.Add(pathToOutputFileParameter);
myCommand.Parameters.Add(pathToHiddenFileParameter);
pipeline.Commands.Add(myCommand);
// Execute PowerShell script
results = pipeline.Invoke();
}
script.bat
jphide "%1" "%3" "%4"
%2
%2
But in doesn't work. This program after executing
jphide input-jpeg-file output-jpeg-file file-to-be-hidden
ask me Passphrase 2 times
C:\Users\Kostyaj\Downloads\JpegTest\JpegTest\JpegTest\bin\Debug>jphide 067.jpg 068.jpg test.txt
Welcome to jphide Rev 0.51 (c) 1998 Allan Latham <alatham#flexsys-group.com>
This program is freeware.
No charge is made for its use.
Use at your own risk. No liability accepted whatever happens.
Contains cryptogaphy which may be subject to local laws.
Passphrase:
Re-enter :
which i need to input manually
How i can write script or change C# code to automate input Passphrase and Re-enter?
UPD
This code
var info = new ProcessStartInfo("cmd.exe");
info.UseShellExecute = false;
info.RedirectStandardInput = true;
var process = Process.Start(info);
Thread.Sleep(1000);
process.StandardInput.WriteLine("jphide \"" + pathParameter.Name + "\" \"" + pathToOutputFileParameter.Name +
"\" \"" + pathToHiddenFileParameter.Name + "\"");
process.StandardInput.WriteLine("echo " + passWord);
creates this output in the command line:
jphide "C:\Users\Kostyaj\Downloads\JpegTest\JpegTest\JpegTest\bin\Debug\067.jpg" "C:\Users\Kostyaj\Downloads\JpegTest\JpegTest\JpegTest\bin\Debug\outImages\067.jpg" "C:\Users\Kostyaj\Downloads\JpegTest\JpegTest\JpegTest\bin\Debug\test.txt" || echo aaa
Welcome to jphide Rev 0.51 (c) 1998 Allan Latham <alatham#flexsys-group.com>
This program is freeware.
No charge is made for its use.
Use at your own risk. No liability accepted whatever happens.
Contains cryptogaphy which may be subject to local laws.
Passphrase:
Re-enter :
C:\Users\Kostyaj\Downloads\JpegTest\JpegTest\JpegTest\bin\Debug>echo 1234
1234
But the program had been stopped to wait input of PassPhrase. I typed it and the execution went on
I need to input "1234" automatically in the program jphide
If you redirect the standard input you can then write to the standard input to fill in the passphrase, for example:
var info = new ProcessStartInfo("cmd.exe");
info.UseShellExecute = false;
info.RedirectStandardInput = true;
var process = Process.Start(info);
Thread.Sleep(1000);
process.StandardInput.WriteLine("echo Hello World");

How to get value from powershell script into C# variable?

I have .ps1 script which should return me SID of current user (or administrator). But i have no idea how can i get this value from that script into my C# code. Can somebody help me, please?
Currently i am calling one script in this way:
ProcessStartInfo newProcessInfo = new ProcessStartInfo();
newProcessInfo.FileName = #"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe";
newProcessInfo.Verb = "runas";
newProcessInfo.Arguments = #"sfc /scannow";
Process.Start(newProcessInfo);
newProcessInfo.Arguments = #"-Command ""sfc /scannow""";
newProcessInfo.Arguments = #"–ExecutionPolicy Bypass -File ""c:\\Users\\HP\\Desktop\\HotelMode-PB\\HotelMode.ps1""";
And i need to get SID of user from another .ps1 script and then use it in this cmd command:
HotelMode.ps1 -UserSid "" [-debug]
Use the System.Management.Automation API directly instead of launching powershell.exe:
using System;
using System.Collections.ObjectModel;
using System.Management.Automation;
// ...
using(PowerShell ps = PowerShell.Create())
{
ps.AddCommand("Set-ExecutionPolicy")
.AddParameter("ExecutionPolicy","Bypass")
.AddParameter("Scope","Process")
.AddParameter("Force");
ps.AddScript(#"C:\Users\HP\Desktop\HotelMode-PB\HotelMode.ps1");
Collection<PSObject> result = ps.Invoke();
foreach(var outputObject in result)
{
// outputObject contains the result of the powershell script
}
}

Executing powershell .ps1 file on server and viewing results C# asp.net

I'm trying to execute a .ps1 PowerShell file on my server using a C# asp.net webpage.
The script takes one parameter, and I've verified that it works by using the command prompt on the server. After it runs, I need to display the results on the webpage.
Currently, I'm using:
protected void btnClickCmdLine(object sender, EventArgs e)
{
lblResults.Text = "Please wait...";
try
{
string tempGETCMD = null;
Process CMDprocess = new Process();
System.Diagnostics.ProcessStartInfo StartInfo = new System.Diagnostics.ProcessStartInfo();
StartInfo.FileName = "cmd"; //starts cmd window
StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
StartInfo.CreateNoWindow = true;
StartInfo.RedirectStandardInput = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.UseShellExecute = false; //required to redirect
CMDprocess.StartInfo = StartInfo;
CMDprocess.Start();
lblResults.Text = "Starting....";
System.IO.StreamReader SR = CMDprocess.StandardOutput;
System.IO.StreamWriter SW = CMDprocess.StandardInput;
SW.WriteLine("#echo on");
SW.WriteLine("cd C:\\Tools\\PowerShell\\");
SW.WriteLine("powershell .\\poweron.ps1 **parameter**");
SW.WriteLine("exit"); //exits command prompt window
tempGETCMD = SR.ReadToEnd(); //returns results of the command window
lblResults.Text = tempGETCMD;
SW.Close();
SR.Close();
}
catch (Exception ex)
{
lblErrorMEssage.Text = ex.ToString();
showError();
}
}
However, it won't even display the initial "Please wait.." if I include the line where it calls powershell. It will just eventually timeout, even though I have increased the AsyncPostBackTimeout on the ScriptManager.
Can anyone please tell me what I'm doing wrong?
Thanks
A little dated; however, to those in search of a similar solution, I would not create a cmd and pass powershell to it but leverage the System.Management.Automation namespace and create a PowerShell console object server-side without the middle man of cmd. You can pass commands or .ps1 files to the AddScript() function - both with arguments - to it for execution. Much cleaner than a separate shell that will have to then call the powershell.exe.
Ensure appropriate identity of the application pool and that principal has the appropriate level of rights required to perform the PowerShell commands and/or scripts. Also, make sure you have the execution policy configured via Set-ExecutionPolicy to the appropriate level (unrestricted/ or remote signed, unless you're signing), in the event you're still going to go the way of executing a .ps1 file server side.
Here's some starter code that is executing commands submitted by a TextBox web form as if it were a a PowerShell console using these objects - should illustrate the approach:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Management.Automation;
using System.Text;
namespace PowerShellExecution
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void ExecuteCode_Click(object sender, EventArgs e)
{
// Clean the Result TextBox
ResultBox.Text = string.Empty;
// Initialize PowerShell engine
var shell = PowerShell.Create();
// Add the script to the PowerShell object
shell.Commands.AddScript(Input.Text);
// Execute the script
var results = shell.Invoke();
// display results, with BaseObject converted to string
// Note : use |out-string for console-like output
if (results.Count > 0)
{
// We use a string builder ton create our result text
var builder = new StringBuilder();
foreach (var psObject in results)
{
// Convert the Base Object to a string and append it to the string builder.
// Add \r\n for line breaks
builder.Append(psObject.BaseObject.ToString() + "\r\n");
}
// Encode the string in HTML (prevent security issue with 'dangerous' caracters like < >
ResultBox.Text = Server.HtmlEncode(builder.ToString());
}
}
}
}
Here's is a write up for you that covers how to create a page from start to finish with Visual Studio and get this done, http://grokgarble.com/blog/?p=142.
I would think you can not run powershell script from aspx page directly like this, since it may related with security reason. in order to run and capture the output:
create a remote runspace:
http://social.msdn.microsoft.com/Forums/hu/sharepointgeneralprevious/thread/88b11fe3-c218-49a3-ac4b-d1a04939980c
http://msdn.microsoft.com/en-us/library/windows/desktop/ee706560%28v=vs.85%29.aspx
PSHost:
Capturing Powershell output in C# after Pipeline.Invoke throws
1 works very well for me.
BTW, the label is not updated because the event is not finished. you may need use ajax to show the label while waiting for powershell.
When I tried this I found the standard input needs to be flushed or closed before SR.ReadToEnd() can complete. Try this:
lblResults.Text = "Please wait...";
try
{
string tempGETCMD = null;
Process CMDprocess = new Process();
System.Diagnostics.ProcessStartInfo StartInfo = new System.Diagnostics.ProcessStartInfo();
StartInfo.FileName = "cmd"; //starts cmd window
StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
StartInfo.CreateNoWindow = true;
StartInfo.RedirectStandardInput = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.UseShellExecute = false; //required to redirect
CMDprocess.StartInfo = StartInfo;
CMDprocess.Start();
lblResults.Text = "Starting....";
using (System.IO.StreamReader SR = CMDprocess.StandardOutput)
{
using (System.IO.StreamWriter SW = CMDprocess.StandardInput)
{
SW.WriteLine("#echo on");
SW.WriteLine("cd C:\\Tools\\PowerShell\\");
SW.WriteLine("powershell .\\poweron.ps1 **parameter**");
SW.WriteLine("exit"); //exits command prompt window
}
tempGETCMD = SR.ReadToEnd(); //returns results of the command window
}
lblResults.Text = tempGETCMD;
}
catch (Exception ex)
{
lblErrorMessage.Text = ex.ToString();
showError();
}
}

Execute Powershell script in C# without UserName and Password

How I will execute the Powershell script in C# without use of UserName and Password?
Powershell Script:
#Script:This Script generate the Package (Zip file) dynamically for unittest
#DirectoryName is Out Directory of Unittest execution
#location is Out\Resource Directory
#locationdetail is Array of files Directory for generate the Package
#FileName is name of Package generated through this script
#option is use for switch case implementation,currently not implemented
param(
[string]$DirectoryName="."
, [string]$location=""
, [string]$FileName="Test.zip"
, [int] $option
)
[System.Reflection.Assembly]::LoadFrom($DirectoryName+"\\"+"Ionic.Zip.dll");
$locationdetail = "Package\\Resource", "Package\\ResourceBad", "Package\\ResourceEmptymetada","Package\\ResourceEmptydatafile","Package\\EmptyTest"
$directoryToZip=""
foreach ($element in $locationdetail)
{
If($element -eq "Package\\Resource")
{
$directoryToZip= $location+"\\"+"Package\\Resource"
$FileName ="Package (Good Data).zip"
}
If($element -eq "Package\\ResourceBad")
{
$directoryToZip= $location+"\\"+"Package\\ResourceBad"
$FileName ="Package (Bad Data).zip"
}
If($element -eq "Package\\ResourceEmptymetada")
{
$directoryToZip= $location+"\\"+"Package\\ResourceEmptymetada"
$FileName ="Package (Bad with ManifestEmpty).zip"
}
If($element -eq "Package\\ResourceEmptydatafile")
{
$directoryToZip= $location+"\\"+"Package\\ResourceEmptydatafile"
$FileName ="Package (Bad with Datafile Empty).zip"
}
If($element -eq "Package\\EmptyTest")
{
$directoryToZip= $location+"\\"+"Package\\EmptyTest"
$FileName ="EmptyTest.zip"
}
$zipfile = new-object Ionic.Zip.ZipFile
$e= $zipfile.AddDirectory($directoryToZip)
$zipfile.Save( $location+"\\"+$FileName)
$zipfile.Dispose()
$directoryToZip=""
}
and My C# code
private static void StartPowerShell(string args,string TempScript)
{
string powerShellPath ="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
string commandLine = #" -Sta -Command ""& {{ try {{ {0} }} catch {{throw}} }} """.InvariantCultureFormat(args);
var info = new ProcessStartInfo();
info.FileName = powerShellPath;
info.Arguments = commandLine;
info.WorkingDirectory = Environment.CurrentDirectory;
info.UseShellExecute = false;
info.CreateNoWindow = false;
info.RedirectStandardError = true;
info.RedirectStandardOutput = true;
//Pass the Username and Password here in ProcessStartInfo
info.UserName = "Saroop";
string pw = "saroop";
System.Security.SecureString password = new System.Security.SecureString();
foreach (var item in pw)
{
password.AppendChar(item);
}
info.Password = password;
info.RedirectStandardError = true;
info.RedirectStandardOutput = true;
Process p = new Process();
p.StartInfo = info;
bool flag = p.Start();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
}
Please guide me anyone. I try to use runas command to start my Powershell.exe as a User(Administrator) but it's also ask for Username and Password.
I try for System.Management.Automation. dll but it's no more existed in .Net4.0
Reference link :
http://msdn.microsoft.com/en-us/library/system.management.automation(v=vs.85).aspx
Please help me.
What you wrote in the comment will works, but i'd like to give you another direction to run powershell scripts from C# code.
You can use System.Management.Automation.Runspaces namespace which expose really easy and more elegant way to run powershell commands and retrieve the results from C# (and using the returned objects and not just the output textual stream). Try to read about it.
I found the answer of my query .
FOR EXECUTE THE POWERSHELL SCRIPT MUST EXECUTE THE POLICY COMMAND FOR
SYSTEM32
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
AND IF YOUR SYSTEM IS 64BIT THEN ALSO EXECUTE SAME POLICY FOR 64BIT POWERSHELL.EXE TOO.
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\POWERSHELL.EXE
/// COMMAND : `Set-ExecutionPolicy Unrestricted`

Categories

Resources