Using PowerShell in c# - c#

So basically im running a script to receive the IP adress of computer, date and time an account is locked out at work and to do this im making a small gui so all the network technicians can use it, my code Currently goes
private void btnSearch_Click(object sender, EventArgs e)
{
var username = textBox1.Text;
using (var powerShellInstance = PowerShell.Create())
{
try
{
powerShellInstance.AddScript(
"Get-WinEvent -ComputerName COMPUTERNAMEHERE -FilterHashtable #{logname=LOGNAMEHERE;id=IDHERE;data=" +
username + "} |" +
"Select-Object -Property timecreated, " +
"#{label='computername';expression={$_.properties[6].value.Split(':')[3]}} " +
"Get-WinEvent -ComputerName COMPUTERNAMEHERE -FilterHashtable #{logname=LOGNAMEHERE;id=IDHERE;data=" +
username + "} | " +
"Select-Object -Property timecreated, " +
"#{label='computername';expression={$_.properties[6].value.Split(':')[3]}} " +
"Get-WinEvent -ComputerName COMPUTERNAMEHERE -FilterHashtable #{logname=LOGNAMEHERE;id=IDHERE;data=" +
username + "} | " +
"Select-Object -Property timecreated, " +
"#{label='computername';expression={$_.properties[6].value.Split(':')[3]}} ");
// invoke execution on the pipeline (collecting output)
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)
{
listView1.Items.Add(OUTPUTHERE);
}
}
}
catch
{
listView1.Items.Add("Failed");
}
}
but it currently crashes on the part of code where i invoke it, I've never worked on power shell before so any help would be appreciated and im really sorry if this is a stupid question xD

Related

Send email with PowerShell

I have a function that is able to send email by sending it through PowerShell.
Using the System.Management.Automation reference I am able to use the PowerShell class that allows me to add PowerShell script that will send the email.
If I were to type it directly into the PowerShell window it would look like this:
$password = ConvertTo-SecureString 'PASSWORD' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('sender#email.com', $password)
Send-MailMessage -From 'sender#email.com' -To 'receiver#email.com' -Subject 'Heres the Email Subject' -Body 'This is what I want to say' -SmtpServer 'smtp.office365.com' -Port '587' -UseSsl -Credential $Cred –DeliveryNotificationOption OnSuccess
It is able to send an email but I how would I check if an email wasn't sent?
The function is below.
private void SendEmail()
{
string from = "sender#email.com";
string to = "receiver#email.com";
string subject = "Heres the Email Subject";
string body = "This is what I want to say";
string server = "smtp.office365.com";
string port = "587";
//Password goes here
string password = "PASSWORD";
string pw = "ConvertTo-SecureString '" + password + "' -AsPlainText -Force";
string cred = "New-Object System.Management.Automation.PSCredential('" + from + "', $password)";
string send = "Send-MailMessage -From '" + from + "' -To '" + to + "' -Subject '" + subject + "' -Body '" + body + "' -SmtpServer '" + server + "' -Port '" + port + "' -UseSsl -Credential $Cred -DeliveryNotificationOption OnSuccess";
string psScript = "$password = " + pw + System.Environment.NewLine +
"$Cred = " + cred + System.Environment.NewLine +
send;
using (PowerShell ps = PowerShell.Create())
{
ps.AddScript(psScript);
// invoke execution on the pipeline (collecting output)
Collection<PSObject> PSOutput = ps.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)
{
//TODO: do something with the output item
Console.WriteLine(outputItem.BaseObject.GetType().FullName);
Console.WriteLine(outputItem.BaseObject.ToString() + "\n");
}
}
}
}
I found a way to check for errors using ps.HadErrors
using (PowerShell ps = PowerShell.Create())
{
//Add the powershell script to the pipeline
ps.AddScript(psScript);
// invoke execution on the pipeline (collecting output)
Collection<PSObject> PSOutput = ps.Invoke();
//check for any errors
if (ps.HadErrors)
{
foreach (var errorRecord in ps.Streams.Error)
{
Console.WriteLine(errorRecord);
}
}
}

How to keep a process alive after remote PowerShell script completion?

I'm trying to start Zookeeper and Solr remotely via PowerShell scriptblock.
Then I see that the process is created in the remote machine (by checking the port 2181 of Zookeeper). And on the script completion it is being terminated.
How do I keep this alive even after the completion?
This code below stops the remote process on script completion. The script.ps1 does a lot of things that includes starting Zookeeper and Solr asJob.
int iRemotePort = 5985;
string strShellURI = #"http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
string strAppName = #"/wsman";
WSManConnectionInfo ci = new WSManConnectionInfo(
false,
machineName,
iRemotePort,
strAppName,
strShellURI,
new PSCredential(userName, secure));
Runspace runspace = RunspaceFactory.CreateRunspace(ci);
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
session.Log(#"c:\temp\script.ps1 -serverID " + counter + " -copySolrConfig $" + status + " -currentHost \"" + machineName + "\" -IP_PORT_List \"" + String.Join(", ", machineWithPort) + "\"");
ps.Runspace = runspace;
ps.AddScript(#"c:\temp\script.ps1 -serverID " + counter + " -copySolrConfig $" + status + " -currentHost \"" + machineName + "\" -IP_PORT_List \"" + String.Join(", ", machineWithPort) + "\"");
var results = ps.Invoke();
foreach (var result in results)
{
session.Log(result.ToString());
}
}
runspace.Close();
So after a long try I came to a conclusion, Using power-shell script alive is not a appropriate way to keep zookeeper and solr running effectively. So moved it as a windows service and installed it on the remote machine via WiX.

how to set a timeout when executing Powershell & Powercli commands inside asp.net mvc

I am working on an asp.net mvc web application, and i have the following code which define a loop over a list of servers and execute PowerCli commands inside my asp.net mvc for each server:-
//Start Loop
var shell = PowerShell.Create();
var shell2 = PowerShell.Create();
var shell3 = PowerShell.Create();
string PsCmd = "add-pssnapin VMware.VimAutomation.Core; $vCenterServer = '" + vCenterName + "';$vCenterAdmin = '" + vCenterUsername + "' ;$vCenterPassword = '" + vCenterPassword + "';" + System.Environment.NewLine;
PsCmd = PsCmd + "$VIServer = Connect-VIServer -Server $vCenterServer -User $vCenterAdmin -Password $vCenterPassword;" + System.Environment.NewLine;
PsCmd = PsCmd + "Get-VMHost " + System.Environment.NewLine;
string PsCmd2 = "add-pssnapin VMware.VimAutomation.Core; $vCenterServer = '" + vCenterName + "';$vCenterAdmin = '" + vCenterUsername + "' ;$vCenterPassword = '" + vCenterPassword + "';" + System.Environment.NewLine;
PsCmd2 = PsCmd2 + "$VIServer = Connect-VIServer -Server $vCenterServer -User $vCenterAdmin -Password $vCenterPassword;" + System.Environment.NewLine;
PsCmd2 = PsCmd2 + " Get-VMHost " + vCenterName + "| Get-VMHostNetworkAdapter -VMKernel" + System.Environment.NewLine;
shell.Commands.AddScript(PsCmd);
shell2.Commands.AddScript(PsCmd2);
dynamic results = shell.Invoke();
dynamic results2 = shell2.Invoke();
// end of loop
but i have noted that sometimes the shell commands will hang and the execution never ends,, so can i define a timeout behavior ,, so that after 5 minutes to skip the commands if no results were returned ...
You will have to roll your own timeout command. Below is code I wrote based on a MSDN Blog entry by Keith Babinec - Executing PowerShell scripts from C#. I wrote the sample in Console Application for demonstration purposes only. I find it easier to see what is happen. You can convert it to Asp.Net application by removing the Console Output and other adjustments.
Here is Program.cs
using System;
using System.Management.Automation;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string script = "Write-Host \"Testing lopping...\"" + Environment.NewLine
+ "for ($i=1; $i -le 5; $i++)" + Environment.NewLine
+ "{" + Environment.NewLine
+ "Write-Output $i" + Environment.NewLine
+ "Start-Sleep -s 3" + Environment.NewLine
+ "}" + Environment.NewLine
+ "Write-Host \"Done!\"" + Environment.NewLine;
PowerShell shell = PowerShell.Create();
shell.AddScript(script);
PowerShellHelper helper = new PowerShellHelper(shell);
try
{
// the script above should take 15 seconds to execute
// do timeout of 10 minutes
helper.ExecuteAsynchronously(new TimeSpan(0, 10, 0));
// do a really short timeout - 2 seconds
helper.ExecuteAsynchronously(new TimeSpan(0, 0, 2));
}
catch(TimeoutException te)
{
Console.WriteLine("\n\nScript took long!");
}
Console.WriteLine("Demo Finish");
Console.ReadLine();
}
}
}
And here is PowerShellHelper.cs
using System;
using System.Management.Automation;
using System.Threading;
// This code was build from MSDN Blogs entry by Keith Babinec
// http://blogs.msdn.com/b/kebab/archive/2014/04/28/executing-powershell-scripts-from-c.aspx
namespace ConsoleApplication1
{
class PowerShellHelper
{
private PowerShell shell_;
public PowerShellHelper(PowerShell shell)
{
shell_ = shell;
}
public void ExecuteAsynchronously(TimeSpan timeout)
{
// prepare a new collection to store output stream objects
PSDataCollection<PSObject> outputCollection = new PSDataCollection<PSObject>();
outputCollection.DataAdded += outputCollection_DataAdded;
// begin invoke execution on the pipeline
// use this overload to specify an output stream buffer
IAsyncResult result = shell_.BeginInvoke<PSObject, PSObject>(null, outputCollection);
// start the timer
DateTime startTime = DateTime.Now;
// do something else until execution has completed.
// this could be sleep/wait, or perhaps some other work
while (result.IsCompleted == false)
{
Console.WriteLine("Waiting for pipeline to finish...");
Thread.Sleep(100);
// we check on our timeout here
TimeSpan elasped = DateTime.Now.Subtract(startTime);
if (elasped > timeout)
{
// we can do a few things here, I like to throw exception
throw new TimeoutException("Powershell script taking too long");
}
}
Console.WriteLine("Execution has stopped. The pipeline state: " + shell_.InvocationStateInfo.State);
foreach (PSObject outputItem in outputCollection)
{
//TODO: handle/process the output items if required
if (outputItem != null)
{
Console.WriteLine(outputItem.BaseObject.ToString());
}
}
}
/// <summary>
/// Event handler for when data is added to the output stream.
/// </summary>
/// <param name="sender">Contains the complete PSDataCollection of all output items.</param>
/// <param name="e">Contains the index ID of the added collection item and the ID of the PowerShell instance this event belongs to.</param>
private void outputCollection_DataAdded(object sender, DataAddedEventArgs e)
{
// do something when an object is written to the output stream
Console.WriteLine("Object added to output.");
}
}
}
I prefer this short construction:
using (PowerShell ps = PowerShell.Create())
{
ps.AddScript(script);
var psAsyncResult = ps.BeginInvoke();
if (psAsyncResult.AsyncWaitHandle.WaitOne(timeoutMilliseconds))
{
// Execution finished
var results = ps.EndInvoke(psAsyncResult);
}
else
{
// Execution terminated by timeout
Console.WriteLine($"Unable to complete running powershell script within {timeoutMilliseconds} milliseconds");
}
}
Powershell invocation with timeout
There is a much shorter (thus less error-prone) solution:
using (PowerShell ps = PowerShell.Create())
{
ps.AddScript(script);
Task invocationTask = ps.InvokeAsync();
try
{
// Ensure the task is waited for the timeout duration.
// As documentation says if the timeout is reached then the task is faulted
if (!invocationTask.Wait(timeout))
{
isTimeouted = true;
}
}
finally
{
// task may not be completed here
// and disposal of not completed task will raise an exception
if (invocationTask != null && invocationTask.IsCompleted)
{
invocationTask.Dispose();
}
}
}

Unable to read Registry value using PowerShell in C#

I am not able to read specific registry values using PowerShell in C#. Here is the code:
Calling function:
public static string UserDisplayName()
{
// PowerShell Command: (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData\1').LoggedOnDisplayName
return GetPowerShellOutputString(#"(Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData\1').LoggedOnDisplayName");
}
Function definition:
private static string GetPowerShellOutputString(string PsCmd)
{
try
{
string PsOut = string.Empty;
Debug.Write("PsCmd: " + PsCmd + "; ");
Runspace rs = RunspaceFactory.CreateRunspace();
rs.Open();
Pipeline pipeline = rs.CreatePipeline();
pipeline.Commands.AddScript(PsCmd);
Collection<PSObject> results = pipeline.Invoke();
rs.Close();
foreach (PSObject obj in results)
if (obj != null) PsOut += obj.ToString() + ", ";
PsOut = (PsOut == string.Empty) ? strUnavailableString : PsOut.TrimEnd(',', ' ');
Debug.WriteLine("PsOut: " + PsOut);
return PsOut;
}
catch (Exception ex)
{
Debug.WriteLine("! " + ex.Message + ex.InnerException + "\n");
return strUnavailableString;
}
}
However, the same Function definition is working perfectly if I am trying to read any other registry value e.g.:
public static string UserOUPath()
{
try
{
if (UserDomain() == SystemInformation.ComputerName) return strUnavailableString; // Non-domain account
//For consistant performance, grab OU from registry instead of AD.
string userSID = WindowsIdentity.GetCurrent().User.ToString();
string ouPath = #"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\" + userSID;
string ou = GetPowerShellOutputString("(Get-ItemProperty -Path '" + ouPath + "').'Distinguished-Name'");
ou = ou.Remove(0, ou.IndexOf(",", 0) + 1); // Drop leading CN stuff
ou = ou.Remove(ou.IndexOf(",DC=", 0), ou.Length - ou.IndexOf(",DC=", 0)); // Drop trailing DC stuff
ou = ou.Replace(",OU=", "/");
ou = ou.Replace("OU=", "/");
ou = FlipOU(ou);
if (ou == null) throw new NullReferenceException();
return ou;
}
catch
{
return strUnavailableString;
}
}
For the first call (UserDisplayName()) when I did Debug mode, the results object is returning null. However if I run the same PowerShell Command in PowerShell window it is giving the value.
I am stumbled upon this as I am not able to get why and what is happening?
A couple of things. First I agree with all the comments about just using the Microsoft.Win32.Registry* classes directly in C#. However to answer your question about doing this from PowerShell, I believe what you are running into a registry virtualization issue. If your C# project is AnyCPU and run under Visual Studio it is likely running 32-bit and that registry path will be virtualized to the SysWow64 registry node. So your reg path won't exist. That can be fixed by making the exe compile as x64. Another option is to use the .NET Registry.OpenBaseKey() method will allows you to specify which reg hive you want to view (32-bit or 64-bit).
Second, you can simplify your code by using the PowerShell class e.g.:
var ps = PowerShell.Create();
ps.AddScript(PsCmd);
var results = ps.Invoke();
foreach (PSObject obj in results)
if (obj != null) PsOut += obj.ToString() + ", ";
Third, with the PowerShell class, after the Invoke() call, check the Error stream for errors like so:
foreach (var error in ps.Streams.Error)
Console.Error.WriteLine(error);

C# Citrix to Powershell

We have developed a new web based application which makes calls to Citrix Xenapp Powershell cmdlets through C#. The web application is hosted on the Citrix server(This is already in the farm) itself which we feel is enough to access all the servers in the Citrix farm.The code gives the output when it runs from Visual Studio IDE.
However, when I make a virtual directory on IIS and access it through Browser.
at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input, Hashtable errorResults, Boolean enumerate) at System.Management.Automation.Runspaces.LocalPipeline.InvokeHelper() at System.Management.Automation.Runspaces.LocalPipeline.InvokeThreadProc()
The following is the code
protected void Page_Load(object sender, EventArgs e)
{
string usrname = "";
string svrname = "";
string appname = "";
string clntnme = "";
string clntip="";
string farm = "";
Runspace rsp = RunspaceFactory.CreateRunspace();
//rsp.ApartmentState = System.Threading.ApartmentState.STA;
//rsp.ThreadOptions = PSThreadOptions.UseCurrentThread;
rsp.Open();
PSSnapInException exp;
rsp.RunspaceConfiguration.AddPSSnapIn("Citrix.XenApp.Commands", out exp);
try
{
PowerShell ps3 = PowerShell.Create().AddCommand("Get-XAFarm");
ps3.Runspace = rsp;
foreach (PSObject pss in ps3.Invoke())
{
farm = pss.Properties["FarmName"].Value.ToString();
}
Response.Write("<DIV align=" + "center" + " style=" + "width:100%;height:100%" + ">" + farm + "<TABLE style=" + "width:100%" + ">");
Response.Write("<TR><TH>User</TH><TH>Server</TH><TH>Application</TH><TH>Address</TH><TH>Client Name</TH></TR>");
PowerShell sessions = PowerShell.Create().AddCommand("Get-XASession").AddParameter("Full").AddCommand("where-object").AddParameter("FilterScript", ScriptBlock.Create("{$_.BrowserName -ne $null }"));
sessions.Runspace = rsp;
foreach (PSObject psr in sessions.Invoke())
{
//IPAddress.Parse(psr.Properties["ClientAddress"].Value.ToString());
usrname = psr.Properties["AccountName"].Value.ToString();
svrname = psr.Properties["ServerName"].Value.ToString();
appname = psr.Properties["BrowserName"].Value.ToString();
clntnme = psr.Properties["ClientName"].Value.ToString();
//if (psr.Properties["ClientAddress"].Value != null)
//{
clntip = psr.Properties["ClientAddress"].Value.ToString();
//}
//clntip = psr.Properties["ClientIPV4"].Value.ToString();
Response.Write("<TR><TD>" + usrname + "</TD><TD>" + svrname + "</TD><TD>" + appname + "</TD><TD>" + clntip + "</TD><TD>" + clntnme + "</TD></TR>");
}
Response.Write("</TABLE>");
}
catch (Exception ex)
{
Response.Write(ex.StackTrace.ToString());
}
finally
{
rsp.Close();
}
}**
Please let me know if any changes have to be done at IIS to enable me access the aspx page successfully.
The app needs to run as a user that has sufficient admin privileges in the XenApp farm. You could create a dedicated service account and make it an admin in the XenApp farm. To list farm state as your sample does, it only needs to be a 'View Only' XenApp administrator. Then configure IIS to run as that service account.

Categories