I would like to execute this event using c#.
Get-WinEvent -Path 'D:\Events\myevents.evt' -Oldest | Select-Object
-Property * | ForEach-Object {$_ | ConvertTo-Json}
I have written upto
path = "D:\\Events\\myevents.evt";
var powerShell = PowerShell.Create();
powerShell.AddCommand("Get-WinEvent");
powerShell.AddParameter("Path");
powerShell.AddArgument(path);
powerShell.AddParameter("Oldest");
powerShell.AddCommand("Select-Object");
powerShell.AddParameter("Property");
powerShell.AddArgument("*");
I am stuck on writing for ForEach-Object {$_ | ConvertTo-Json}. Let me know how to proceed.
Appreciate help.
Keith's answer is totally valid if Path come from trusted source.
Otherwise, it can be vulnerable for script injection. (demo https://gist.github.com/vors/528faab6411db74869d4)
I suggest a compromised solution:
wrap you script in a function, which takes dynamic arguments and Invoke it with AddScript(). Now you have a function in your powershell runspace/session. You can call this function with AddCommand() + AddParameter(). Remember, that you need to call powershell.Commands.Clear() after first Invoke, otherwise commands will be piped.
Code can look like that:
const string script = #"function wrapper($path) {return (Get-WinEvent -Path $path -Oldest | Select-Object -Property * | ForEach-Object {$_ | ConvertTo-Json}) }";
ps.AddScript(script);
ps.Invoke();
ps.Commands.Clear();
ps.AddCommand("wrapper").AddParameter("path", path);
You could just use the AddScript method:
powershell.AddScript("Get-WinEvent D:\Events\myevents.evt -Oldest | ConvertTo-Json");
I think you could also simplify that script and pipe directly to ConvertTo-Json.
Related
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!
Namespace:System.Management.Automation
Trying to do where-object matches in C# using Powershell in the Automation namespace
Tried this, but it returns -like as an unknown parameter
PowerShell shell = PowerShell.Create("Get-Childitem").
AddCommand("where-object { $_.Name -like "*a" }");
C# Newbie here. I am not sure if what I'm going for is even possible.
Basically, I'm trying to read the properties of a system object array within a Powershell object in C#.
For simplicity's sake, here is a basic Powershell code that has a similar output:
$object = New-Object -TypeName PSObject
$object | Add-Member -MemberType NoteProperty -Name "Name" -Value "Yad"
$object | Add-Member -MemberType NoteProperty -Name "Title" -Value "C# Noob"
$object | Add-Member -MemberType NotePropety -Name "Machine Services" -Value (Get-Service)
return $object
On the C# side of things, I am able to fetch the results by mapping the properties as below:
var shell = PowerShell.Create();
shell.Commands.AddScript("C:\\Scripts\\Powershell\\TestScript.ps1 -user " + userName);
Collection<PSObject> results = shell.Invoke();
Profile userProfile = new Profile(); //Profile class is declared prior this line
foreach (PSObject psObject in results)
{
userProfile.Name = Convert.ToString(psObject.Properties["Name"].Value);
userProfile.Title= Convert.ToString(psObject.Properties["Title"].Value);
userProfile.MachineServices= psObject.Properties["Machine Services"].Value;
}
With this, the 'userProfile' object is equivalent to the Powershell output.
Now, the 'MachineServices' property is an object that contains its own set of properties (status, name, and displayname in Powershell). Is it possible to call these properties and retrieve their values?
I tried something similar to below but, of course, I get an error as the Powershell script is completely separate and the properties are unknown to the C# code prior runtime.
userProfile.MachineServices.Status
Any ideas?
If you investigate Get-Service in PowerShell with .GetType() you can find out that it returns an Object array, where the members are of type ServiceController.
I haven't tried it in C# but you should be able to declare Profile.MachineServices as System.ServiceProcess.ServiceController[] and then use the assignment
userProfile.MachineServices= (System.ServiceProcess.ServiceController[])(psObject.Properties["Machine Services"].Value);
Once C# knows the correct object type, you can access the properties and methods, get IntelliSense and so on.
I want to get all certificates from my system.
So I used the System.Security.Cryptography.X509Certificates class.
When I remove the () after the X509Store I getting the same results like I entered "My"
What is the right membername to see all certificates? It is possible?
MSDN StoreName Enumeration
$store=new-object System.Security.Cryptography.X509Certificates.X509Store("CA")
# Put in CA, My, root etc.
$store.open("ReadOnly")
$store.Certificates
$store.Certificates.count
You can get them from your local cert drive:
Get-ChildItem Cert:\CurrentUser\CA # user certs
Get-ChildItem Cert:\LocalMachine\CA # machine certs
Get-ChildItem Cert:\LocalMachine\My
This is fun if you have WinRM installed but in a much more standard way to find all certificate it is much better to use something like
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("\\$server_name\My","LocalMachine")
$store.Open("ReadOnly")
$store.Certificates
The following PowerShell script will ask for the DNS name of a remote computer, then it asks for Domain Admin credentials so it can connect and query. The resulting $AllCerts var has every certificate from every store. It then also exports them to a CSV file in the $env:temp folder and opens the folder in Windows Explorer.
function Get-Cert( $computer=$env:computername ){
$cred = Get-Credential -Message "Enter credentials for a Domain Admin"
$ro=[System.Security.Cryptography.X509Certificates.OpenFlags]"ReadOnly"
$lm=[System.Security.Cryptography.X509Certificates.StoreLocation]"LocalMachine"
$Stores = (Invoke-Command $computer {Get-ChildItem cert:\LocalMachine} -Credential $cred).Name
$AllStores = #()
foreach ($store in $Stores){
$AllStores += new-object System.Security.Cryptography.X509Certificates.X509Store("\\$computer\$store",$lm)
}
$AllStores.Open($ro)
$AllStores.Certificates
}
write-host "Enter remote computer to poll certificate information from" -ForegroundColor Cyan
$remoteComputer = read-host
$AllCerts = Get-Cert $remoteComputer
$AllCerts = $AllCerts | Select Subject,Issuer,Thumbprint,NotBefore,NotAfter
$AllCerts | Where-Object {$_.NotAfter -lt (Get-Date)} | format-list
$AllCerts | export-csv -NoTypeInformation $env:temp\$($remoteComputer)_AllCerts.csv
start $env:temp
Fantastic Script, I had issue with it naming and could be me easily, but changed this and very happy with the output, thanks!
From:
$AllCerts | export-csv -NoTypeInformation $env:temp\$($remoteComputer)_AllCerts.csv
start $env:temp
To:
$AllCerts | export-csv c:\temp\AllCerts.csv -NoTypeInformation
I'm using this as a reference: http://msdn.microsoft.com/en-us/library/dd182449(v=VS.85).aspx
So the implementation is very similar. I'm executing the following PowerShell command to retrieve process information from the intended computer through the 1-liner command below:
$computer = "Remote.Computer.Here"; Get-Process -computer $computer | Sort-Object WorkingSet -desc | Select-Object -first 10 | Format-Table -property name, ID, #{Expression= {$_.WorkingSet/1mb};Label="MemoryLoad";} -auto
The command above executes perfectly in PS window. However, when called from C# app code, I get no returns. Especially so, when I access it through the following:
PowerShell shell = PowerShell.Create();
shell.AddScript(CmdletMap[PSVocab.OsProcLoad]);
Collection<PSObject> obj = shell.Invoke();
DataTable dt = new DataTable();
dt.Columns.Add("ProcessName");
dt.Columns.Add("ID");
dt.Columns.Add("MemoryLoad");
DataRow row;
foreach (PSObject resultObject in obj)
{
row = dt.NewRow();
row["ProcessName"] = resultObject.Members["name"].Value;
row["ID"] = resultObject.Members["id"].Value;
row["MemoryCol"] = resultObject.Members["MemoryLoad"].Value;
dt.Rows.Add(row);
}
Doing a quick-watch of resultObject.Members[].Value would simply return null.
Any help?
Thanks.
Inspect shell.Streams.Error to see what error is happening with your invocation of the script.
In PowerShell, the default for a failing operation is to return nothing. PowerShell has several well-known streams, and your error is either lying in the error stream ([PowerShell].Streams.Error) or it is a terminating error ([Powershell].InvocationStateInfo.Reason).
Hope this helps,
Use two slightly different commands: one for C# (and console) and another for console only.
For invoking from C# and console:
$computer = "."
Get-Process -computer $computer | Sort-Object WorkingSet -desc | Select-Object -first 10 |
Select-Object -property name, ID, #{Expression= {$_.WorkingSet/1mb};Label="MemoryLoad"}
For interactive host (i.e. console, ISE, etc.) with prettier look:
$computer = "."
Get-Process -computer $computer | Sort-Object WorkingSet -desc | Select-Object -first 10 |
Select-Object -property name, ID, #{Expression= {$_.WorkingSet/1mb};Label="MemoryLoad"} |
Format-Table -AutoSize
It is the Format-Table that makes problems in C#. Do not use it in C#. As for console, it should be the last command in the pipeline, it produces objects for printing, not for further use. Example: the first command shows two columns name and ID but the second command does not get any name properties:
Get-Process | Format-Table -property name, ID
Get-Process | Format-Table -property name, ID | Select-Object name
According to the Technet, your syntax is wrong. . .
http://technet.microsoft.com/en-us/library/dd347630.aspx
Syntax
Get-Process [[-Name] ] [-ComputerName ]
[-FileVersionInfo] [-Module] []
Get-Process -Id [-ComputerName ]
[-FileVersionInfo] [-Module] []
Get-Process -InputObject [-ComputerName ]
[-FileVersionInfo] [-Module] []
Specifically, you need to use -computername, and not computer. And I have no idea what "Remote.Computer.Here" is. . .. you can use localhost.
edit
Nm, my coworker is an idiot. I just had to swap Remote.Computer.here with . and it looks all fine and dandy. See if that works.