I am trying to write a web interface that will show the current VMs on a remote Hyper-V host.
So far, I have this
protected void getVMS(object sender, GridViewCommandEventArgs e)
{
//command to run
string cmdToRun = "get-vm -computername fsyovs02";
var shell = PowerShell.Create();
shell.Commands.AddScript(cmdToRun);
var results = shell.Invoke();
if (results.Count > 0)
{
var builder = new StringBuilder();
foreach (PSObject vm in results)
{
builder.Append(vm.BaseObject.ToString() + "\r\n");
}
ResultBox.Text = Server.HtmlEncode(builder.ToString());
}
}
This is returning something, but not what I want. For each VM, it is returning the line
Microsoft.HyperV.PowerShell.VirtualMachine
What I want is it to display exactly how it does via powershell.
Can anyone help me please - as I am going out of my mind!
Many Thanks
Mark
Sorry had to edit my answer as you cannot access those types from C#. You should then use the Members collection on PSObject to access particular properties of the VirtualMachine. Please check this approach in your case:
foreach (PSObject vm in results)
{
builder.Append(vm.Members["Name"].Value + "\r\n");
}
Related
I need to get the session id of remote users programmatically. I wrote a C# code to submit powershell scripts/commands and get the answer from there using "query session /server:remoteComputer". The script runs well from PowerShell and I got all the session IDs. But when I try to execute the script from my C# code I got "'query' is not recognized as the name of a cmdlet..." . I even tried to submit the same command "query session /server:remoteComputer" from C# and I got the same result. Any clue or advice will be very appreciated. Thanks!
string errorMesg = string.Empty;
//psinstance.AddScript(#"\\fs\PC_Support\PC_Support_SW\getloggedusers.ps1");
psinstance.AddScript("query session /server:remoteComputer");
psinstance.AddCommand("out-string");
PSDataCollection<PSObject> outputCollection = new PSDataCollection<PSObject>();
psinstance.Streams.Error.DataAdded += (object sender1, DataAddedEventArgs e1) =>
{
errorMesg = ((PSDataCollection<ErrorRecord>)sender1)[e1.Index].ToString();
};
IAsyncResult result = psinstance.BeginInvoke<PSObject, PSObject>(null, outputCollection);
psinstance.EndInvoke(result);
//Collection<PSObject> psOutput = psinstance.Invoke();
StringBuilder sb = new StringBuilder();
foreach (var outputItem in outputCollection)
{
sb.AppendLine(outputItem.BaseObject.ToString());
}
if(!string.IsNullOrEmpty(errorMesg))
{
MessageBox.Show(errorMesg);
}
else
{
MessageBox.Show(sb.ToString());
}
return;
Trying to mimic the command Get-CimInstance CIM_ManagedSystemElement in C#
string NamespacePath = "\\\\.\\Root\\CIMv2";
string ClassName = "CIM_ManagedSystemElement";
//Create ManagementClass
ManagementClass oClass = new ManagementClass(NamespacePath + ":" + ClassName);
//Get all instances of the class and enumerate them
foreach (ManagementObject oObject in oClass.GetInstances())
{
//access a property of the Management object
Console.WriteLine("Caption : {0}", oObject["Caption"]);
}
Sadly, that didnt work as expected, would like to get some help
Thanks
You do this like this (you have to add System.Management namespace)
Because CIM_ManagedSystemElement is at the default WMI namespace( which is Root\CIMV2) you don't have to specify it at ManagementObjectSearcher.
Also, be sure that you have the minimum supported client- Windows Vista
string query = #"SELECT * FROM CIM_ManagedSystemElement";
var moSearch = new ManagementObjectSearcher(query);
var moCollection = moSearch.Get();
foreach (ManagementObject mo in moCollection)
{
Console.WriteLine("Caption = " + mo["Caption"]);
}
Furthermore i suggest you use an ORM to remove boilerplate code like ORMi or Kexla
I also couldn't get your code to work, but in the meantime if you need a workaround you can use the PowerShell API from within C# using this simple program I wrote based on some online documentation. It will give you an output you're looking for. You should have access to all the properties in OutputCollection_DataAdded so if you need more than Caption you can grab it here. Also, at the end of the execution there is a foreach() loop that will contain the entire output collection if you need to do something with that. The execution is extremely slow so I had to make it async to work.
static void Main(string[] args)
{
using (PowerShell ps = PowerShell.Create())
{
ps.AddCommand("Get-CimInstance");
ps.AddParameter("-ClassName", "CIM_ManagedSystemElement");
var outputCollection = new PSDataCollection<PSObject>();
outputCollection.DataAdded += OutputCollection_DataAdded;
// invoke execution on the pipeline (collecting output)
var async = ps.BeginInvoke<PSObject, PSObject>(null, outputCollection);
// do something else until execution has completed.
// this could be sleep/wait, or perhaps some other work
while (async.IsCompleted == false)
{
Console.WriteLine("Waiting for pipeline to finish...");
Thread.Sleep(1000);
// might want to place a timeout here...
}
Console.WriteLine("Execution has stopped. The pipeline state: " + ps.InvocationStateInfo.State);
// loop through each output object item
foreach (PSObject outputItem in ps.EndInvoke(async))
{
// 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
// outputItem.BaseOBject
}
}
Console.Read();
}
}
private static void OutputCollection_DataAdded(object sender, DataAddedEventArgs e)
{
if (sender is PSDataCollection<PSObject>)
{
var output = (PSDataCollection<PSObject>)sender;
// Handle the output item here
var caption = output.Last().Properties["Caption"];
if (caption != null)
{
Console.WriteLine($"Caption: {caption.Value}");
}
}
}
I've been making a C# application that runs some Powershell stuff, the issue is that it doesn't seem to be be collecting the correct/all results of the Powershell commands.
The below code:
private void Button_Click(object sender, RoutedEventArgs e)
{
List<string> results = RunScript("Get-ADUser testuser2 -Properties *");
this.ShowMessageAsync("OK",results[0]);
}
private List<string> RunScript(string comand)
{
var powerShell = PowerShell.Create();
powerShell.Runspace = runspace;
powerShell.AddScript(comand);
Collection<PSObject> results = powerShell.Invoke();
List<string> resultsList = new List<string>();
if (powerShell.Streams.Error.Count > 0)
{
this.ShowMessageAsync("Something went wrong!", "Check error log for details!");
runspaceBox.AppendText(powerShell.Streams.Error[0] + "\r\n");
}
else
{
foreach (PSObject obj in results)
{
runspaceBox.AppendText(obj.ToString() + "\r\n");
resultsList.Add(obj.ToString());
}
}
return resultsList;
}
Results in:
{CN=testuser2,DC=testdomain,DC=com}
Putting the command into Powershell directly results in the below:
PS C:\Users\Ultimate-V1> Get-ADUser testuser2 -Properties *
[A bunch of extra stuff above]
Description :
DisplayName : Test User2
DistinguishedName : CN=Test
One,CN=Users,DC=testdomain,DC=com
Division :
DoesNotRequirePreAuth : False
[A lot of extra stuff below]
I've snipped a lot of the results out of the above command as it is very long.
What I can't work out is why it is only returning the Distinguished Name and not all of the results, this happens with some commands but not others. The command to return all AD groups on a user works fine for example.
If possible i'd love it to have EVERYTHING that the command returns, I will be formatting the resultsList later in the code.
Any help or pointers would be greatly appreciated!
Because you casted the result as a string, so you got a string representation of the complex object (whatever the result of the object's .ToString() method).
In PowerShell itself, it doesn't cast everything to a string to display it; it has various rules about how to display objects in general (usually a table or list format), and some types have specific formatters.
PetSerAl already answered in the comments, but the way to get a string representation of what PowerShell displays on the console is to send your output through Out-String:
powerShell.AddScript(comand).AddCommand("Out-String");
I have a project which calls a bunch of Powershell scripts, a few of which take several minutes to run through and cause the page to timeout. I thought about using Start-Job to run several processes in the background to free up the page from timing out, but each time I run a Powershell command it opens a new instance of Powershell and cannot find any jobs.
protected void RunShell()
{
var Shell = PowerShell.Create();
Shell.Commands.AddScript(Textbox.Text);
var results = Shell.Invoke();
if (results.Count > 0)
{
var builder = new StringBuilder();
foreach (var psObject in results)
{
builder.AppendLine(psObject.BaseObject.ToString();
}
Textbox2.Text = builder.ToString();
}
}
protected void button1_Click(object sender, EventAgrs e)
{
Textbox1.Text = "Start-Job -ScriptBlock{.\\script.ps1} | Out-String";
RunShell();
}
protected void button2_Click(object sender, EventArgs e)
{
Textbox1.Text = "Get-Job * | Out-String";
RunShell();
}
I am now hoping to create a persistent session of powershell that I am able to send commands to and retrieve the jobs and statuses from. I cannot figure out how to set up this snippet of code as a global variable.
Any pointers in the right direction would be appreciated.
Did you try this?
PowerShell.Create(CurrentRunspace)
http://msdn.microsoft.com/en-us/library/system.management.automation.runspacemode%28v=vs.85%29.aspx
Edit: If you want to share a PowerShell, you can do this (to share it across the entire application, replace Session with Application):
private PowerShell GetPowerShell()
{
PowerShell Shell = Session["PowerShell"] as PowerShell;
if (Shell == null)
{
Shell = PowerShell.Create();
Session["PowerShell"] = Shell;
}
return Shell;
}
I trying to execute the powershell script in C#. But I am getting the exception like "Assignment statements are not allowed in restricted language mode or a Data section."
Here is my C# code :
string script = System.IO.File.ReadAllText(#"C:\script.ps1");
PowerShell exec_script = PowerShell.Create();
exec_script.RunspacePool = rs;
exec_script.AddScript(script);
IAsyncResult exec_AsyncResult = exec_script.BeginInvoke();
PSDataCollection exec_Result = exec_script.EndInvoke(exec_AsyncResult);
foreach (PSObject cmdlet in exec_Result)
{
PSMemberInfoCollection collec = cmdlet.Members;
foreach (PSMemberInfo temp in collec)
{
Console.WriteLine(temp.Name + "\t\t\t\t:\t" + temp.Value);
}
}
Here is my PowerShell script :
[Collections.ArrayList]$serverList = New-Object Collections.ArrayList
[string]$server
if ($server -eq "")
{
$objects = Get-MailboxServer
foreach ($object in $objects)
{
$out = $serverList.Add($object.Name)
}
}
else
{
$serverList.Add($server)
}
wondering what is the problem.
Thanks is advance,
Viswanath B
It looks like it's a server restriction on Exchange: check this thread on technet.