i am trying to make a small c# application to create my Hyper V VM's but i has got a problem with powershell in c# so i just running every powershell cmdlet using cmd.exe
but now i cant get how to set dvddrive as firstbootdevice in generation 2 virtual machine in just one line
for powershell script i use
$VMNAME= "SQL3"
$VMDVD = Get-VMDvdDrive -VMName $VMNAME
Set-VMFirmware -VMName $VMNAME -FirstBootDevice $VMDVD
but how can i do it in a single line
As a general rule, any variable reference can be substituted by a subexpression ($()):
Set-VMFirmware -VMName "SQL3" -FirstBootDevice $(Get-VMDvdDrive -VMName "SQL3")
If you have multiple DVD drives and know the ISO file
$DvdBootDrive = Get-VMDvdDrive -VMName $VmName | where {$_.Path -Like "*AutoInstall.iso*" }
Set-VMFirmware -VMName $VmName -FirstBootDevice $DvdBootDrive
Related
I'm working on a PowerShell script which is calling C# method to enable/ disable a specific task.
Everything is working fine But I only want to get the Json result. Instead I'm getting the below result, like TaskName , TaskPath and other unwanted white space . I'm using return $properties | ConvertTo-Json in my PowerShell script to convert it but looks like when we do Enable-ScheduledTask PowerShell automatically added TaskPath and TaskName.
Can anyone suggestion how I can avoid TaskName/TaskPath and get only Json data in {}.
I got my solution. I had to use out-null to suppress the output that's it.
Enable-ScheduledTask -TaskPath $TaskPath -TaskName $taskName | Out-Null
I followed this : https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/out-null?view=powershell-7.1
I want to catch the output exactly as I get it when I run commands in PowerShell.
For instance when I type LS, I get:
Yet when I use this code:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PowerShell ps = PowerShell.Create(); // Create a new PowerShell instance
ps.Runspace = runspace; // Add the instance to the runspace
ps.Commands.AddScript("ls"); // Add a script
Collection<PSObject> results = ps.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
Console.WriteLine(obj.ToString());
}
I get the following output:
Microsoft.Management.Infrastructure.dll
System.Management.Automation.dll
System.Management.Automation.xml
WpfApp1.exe
WpfApp1.exe.config
WpfApp1.pdb
Although this output may come in handy, with an other application I dont get the right output in return, so I would prefer the exact output as I see it in PowerShell itself.
Is there a way to read the output as I get it in PowerShell, line for line?
If you want the exact text that powershell produces you then you can use Out-String in the powershell command:
ps.Commands.AddScript("ls | Out-String");
You can also read the values by accessing the Properties of the PSObject:
foreach (PSObject obj in results)
{
var name = obj.Properties["Name"]?.Value.ToString()
var mode = obj.Properties["Mode"]?.Value.ToString();
var length = obj.Properties["Length"]?.Value.ToString();
var lastMod = (DateTime?)obj.Properties["LastWriteTime"]?.Value;
Console.WriteLine(string.Format("{0} {1} {2} {3}", mode, lastMod, length, name));
}
Note, as mentioned in mklement0's answer, you don't need to use Runspace to execute this powershell. Consider using Get-ChildItem rather than ls.
Note: This answer also recommends what part of haldo's helpful answer shows, in a more focused manner and with supplementary information.
Modify your script to pipe your command to the Out-String
cmdlet, which uses PowerShell's formatting system to render to a string, the same way that output renders to the console.
ps.AddScript("ls | Out-String"); // Add a script
Note:
Windows PowerShell assumes a fixed line width of 120 characters and with (implied) tabular (Format-Table) or wide (Format-Wide) formatting, truncates lines that are longer (except if the output object is of type [string]), with the point of truncation indicated with ...
PowerShell [Core] 7+ exhibits the same behavior fundamentally, but only uses default width 120 as a fallback: when the hosting (console-subsystem) executable is running in a console (terminal), the console window's width is used instead, which is the same behavior you get in a regular PowerShell console window (see this answer).
To fix that, pass a large-enough line width to -Width; e.g.:
ps.AddScript("ls | Out-String -Width 200");
Note:
In Windows PowerShell, do not use -Width ([int]::MaxValue-1), because every line is then padded to that width, which will result in excessively large output.
PowerShell [Core] 7+, this padding is no longer performed, and you can safely use
-Width ([int]::MaxValue-1)
A few asides:
For robustness, I suggest avoiding the use of aliases (such as ls for Get-ChildItem) in scripts and compiled code.
In the case at hand, ls wouldn't work on Unix-like platforms, because the alias isn't defined there, so as not to conflict with the platform-native ls utility.
It's best to wrap PowerShell ps = PowerShell.Create(); in a using block to ensure that the PowerShell instance is disposed of: using (PowerShell ps = PowerShell.Create()) { ... }
There is generally no need to create a runspace explicitly - PowerShell.Create() will create one for you.
The System.Management.Automation.PowerShell instance returned by PowerShell.Create() directly exposes methods such as .AddScript() - no need to use the .Commands property.
You can get compressed json output from powershell with this command
ls | ConvertTo-Json -Compress
Then deserialize. Also this command provide extra info than see in powershell output.
I am trying to retrieve data from PowerShell into a c# object. The data I am looking for is returned from a PowerShell Invoke() of GetExecutingRequests on a remote web server. The issue I'm having is that I am not getting an error code, but the results from the Invoke() that I'm looking for are nowhere in the return data, or on the PowerShell object.
using (Runspace runspace = RunspaceFactory.CreateRunspace(cxn))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
string script = String.Format("Get-WmiObject
WorkerProcess -Namespace root\\WebAdministration -ComputerName {0} |
Invoke-WmiMethod -Name GetExecutingRequests", server);
ps.AddScript(script);
ps.AddParameter("OutputElement", new HttpRequest[0]);
var result = ps.Invoke();
}
}
This code executes, and returns a Collection with 29 items. However, none of them show the GetExecutingRequests results, and there is nothing relevant on the PowerShell object either.
I would like to get the output of GetExecutingRequests into a c# object, so I can do further processing. the PSDataStreams on the ps object also have no results.
Any help would be appreciated.
MORE INFO:
I was able to solve this with a change to the PowerShell script I was sending:
string script = String.Format("Get-WmiObject WorkerProcess -Namespace root\\WebAdministration -ComputerName {0} | Invoke-WmiMethod -Name GetExecutingRequests | %{{ $_ | Select-Object -ExpandProperty OutputElement }}", server);
I'm not quite sure if I can resolve the results directly, but I'd advise running below command to get some some more information on the object being returned. From there, you could look into how you'd handle that returning in C#.
Get-WmiObject -Class $(<scriptblock>) | get-member
Define the object types in C# & see if you're able to capture it in that way first.
If you're not able to make any progress with capturing the object type being returned in powershell yourself, it may be worth posting here to see if anyone else might be able to offer any insight/experience with workarounds for interfacing those objects types into C#.
If above really isn't possible, forcing the powershell to return everything from standard out would help find if it's writing anything meaningful that you can scrape/format in C#. I think the best way to do that in your quoted powershell command would be like so:
return $(Get-WmiObject
WorkerProcess -Namespace root\WebAdministration -ComputerName {0} |
Invoke-WmiMethod -Name GetExecutingRequests | *>&1)
This returns all 5+ standard outs from powershell to the return object (for those reading, see these docs on ps streams). Youshould definitely be able to capture the return in your results variable, but it can't hurt to make sure you're able to throw/capture errors from overflow.
Hope this helps to continue the digging!
I'm trying to get a process ID by means of the process's execution-path. For that I'm executing the below Powershell command which runs perfectly in Powershell's console:
(Get-Process | Where-Object {$_.Path -eq 'C:\WINDOWS\system32\winlogon.exe'}).Id
But executing the same through C# is giving no results. below is the code snippet I'm following:
string cmd = "(Get-Process | Where-Object {{$_.Path -eq '{0}'}}).Id";
string path = #"C:\WINDOWS\system32\winlogon.exe";
string finalCmd = string.Format(cmd, System.IO.Path.GetFullPath(path));
powershell.Runspace = runspace;
powershell.AddScript(finalCmd);
var result = powershell.Invoke();
I'm using double-culry-braces for escape sequence. But still powershell.Invoke() returns nothing but null. Is there any other way to get the Process Id with its executable path?
My ultimate goal is that I should be able to push an application (MSI installer) to all the PCs in network through Active Directory(irrespective of x86/x64) and I should get the Process Ids for the given executable path. Thanks for the suggestions but in my case I need a generic solution which should work seamlessly for both x86 and x64.
Doesn't seem like you need to use Powershell here. .NET code can query processes directly.
Something like:
Process.GetProcesses().Where(p=>p.MainModule.FileName==path)
should return you an enumerable of all matching processes, from which you can easily retrieve their IDs. And decide what to do if you find more than one!
I wish to create multiple mailcontacts (external Contacts) in the GAL in Microsoft Online by running Powershell command from C#. The code below works, but is very slow and takes about 15-20 min to run for 400 mailcontacts.
foreach(EmailAdressVM emailAddressVM in emailList.emailAddresses1)
{
//Create New MailContact.
Pipeline pplNewMailContact = runspace.CreatePipeline();
Command cmdNewMailContact = new Command("New-MailContact");
cmdNewMailContact.Parameters.Add("Name", emailAddressVM.sExternalEmailAddress);
cmdNewMailContact.Parameters.Add("Displayname", emailAddressVM.sFullName.Trim());
cmdNewMailContact.Parameters.Add("Lastname", emailAddressVM.sLastName.Trim());
cmdNewMailContact.Parameters.Add("Firstname", emailAddressVM.sFirstName.Trim());
cmdNewMailContact.Parameters.Add("ExternalEmailAddress", emailAddressVM.sExternalEmailAddress.Trim());
pplNewMailContact.Commands.Add(cmdNewMailContact);
pplNewMailContact.Invoke();
pplNewMailContact.Stop();
pplNewMailContact.Dispose();
}
I am guessing that this is slow since I create a new Pipeline for every new mailcontact that is added and there has to be a more eficient way of doing this since running...
import-csv <filename> | ForEach {
new-mailcontact -name $_.emailaddress -displayname $_.FullName -lastname $_.lastname -firstname $_.firstname -externalemailaddress $_.emailaddress -alias $_.alias
}
...is much faster.
I have found some references after many hours of searching the web that you can do something similar to using a CSV when running Powershell commands from C#, i.e. send a list (or array) of values to a command (in this case the "new-mailcontact" command). But, I have not found any good example of how to send more than one value to a command and I need to supply many values (for example: -name $.emailAddress -displayname $.FullName, etc.) to the "new-mailcontact" command.
Is it possible to send a list (or array) in a similar way as the "import-csv" command (when using regular powershell) and will this be faster, or is there an evan better way? Would I get better performance if I use Powershell 3 instead of 1 (as I am using now).
Please provide working sample code i C#!
Please note that I cannot save a CSV file to disk and the execute powershell from CMD since I do not have write access to disk and that I do not think that I can run an entire script remotely (since remote scripting probably is disabled on Exchange Online).
The biggest reason I would think is because for each address you are creating a new Powershell instance and you are not multithreaded.
You code looks something like this from above:
Foreach email address{
Declare a new Powershell process
Add attributes to call later
Start Powershell and pipe stuff in
Close Powershell instance
}
I think you would be better off creating the Powershell instance / pipe once and then sending each object into it. More along the lines of:
Create PS Pipe
Foreach email address{
PS.SendArguments(Email, Name, DN, etc.);
}
I am not in an environment to get something working or tested right now, so hopefully this gives you at least most of what you need...