Powershell - Get-WinEvent - powershell

I have been looking all over the place to just figure out what this "Level" means running Get-WinEvent.
For example,
Get-WinEvent –FilterHashtable #{logname=’application’; level=2; starttime=$time; id=20}
What does level=2 represent here?
The reason that I am asking is I am trying to validate the severity of each log and does that level=2 represent anything related to severity.

Let's try and find out:
#Get sample object
$t = Get-WinEvent -MaxEvents 1 -FilterHashtable #{ Logname='application'; level=2 }
#Explore properties and type
$t.GetType().Fullname
System.Diagnostics.Eventing.Reader.EventLogRecord
A quick msdn-search for EventLogRecord points us to the EventLogRecord.Level Property
Gets the level of the event. The level signifies the severity of the
event. For the name of the level, get the value of the
LevelDisplayName property
#Check out Level vs LevelDisplayName
$t | Format-Table -Property Level, LevelDisplayName -AutoSize
Level LevelDisplayName
----- ----------------
2 Error
A quick search in my log to list some level-values:
Get-WinEvent #{ logname='application' } | Select-Object Level, LevelDisplayName -Unique | Sort-Object Level
Level LevelDisplayName
----- ----------------
0 Information
2 Error
3 Warning
4 Information
It also says on the Level-property page that it uses the StandardEventLevel enum, so lets list it's values:
[enum]::GetValues([System.Diagnostics.Eventing.Reader.StandardEventLevel]) | Select-Object {$_}, {$_.value__ }
$_ $_.value__
-- -----------
LogAlways 0
Critical 1
Error 2
Warning 3
Informational 4
Verbose 5

See this link for more info. MSDN
Effectively you're looking for a winmeta.xml file, but it'll have these for the base values :
LogAlways: 0,
Critical: 1,
Error: 2,
Warning: 3,
Information: 4,
Verbose: 5,
rest below 16 are reserved

Related

powershell select-object property AND expandproperty

Trying to execute a Select-Object, but i can't seem to return property and a value from a key-value list property. There are 3 properties i want returned RunStart, Message and 'tableName' from this Parameters property.
I get too much with this:
|Select-Object -Property Parameters,RunStart,Message
Parameters RunStart Message
---------- -------- -------
{[tableName, AHROR012], [schemaName, dbo]...} 11/14/2019 5:39:06 PM Operation on target failed
But I dont get the RunStart or Message when i do this:
|Select-Object -ExpandProperty Parameters -Property tableName,RunStart,Message
Key Value
--- -----
tableName AHROR012
How do i do it to get:
Parameters.tableName RunStart Message
---------- -------- -------
AHROR012 11/14/2019 5:39:06 PM Operation on target failed
THANKS!!!
... | Select-Object -Property #{
Name='ParametersTableName';
Expression={ $_.Parameters.tableName }
}, RunStart, Message
A more elegant solution would be to use a 2nd select statement:
... | select RunStart,Message -ExpandProperty Parameters | Select RunStart,Message,tableName

PowerShell Get-WinEvent Display only first line of Message property?

Getting acquinted with PowerShell, running into myriads of problems (lack of knowledge).
I'm trying to list myself Windows Security Log logged events by count, but I could also use a "friendly" description field which happens to be the first line of the "Message" Property. I can't figure out a way to extract only that.
Thus, I'm running the following to get an overview of events:
PS C:\TEST> Get-WinEvent -FilterHashtable #{logname="security"}| Group-Object id -NoElement | sort count
Count Name
----- ----
1 4724
1 4722
1 1102
1 4725
2 4718
2 6408
2 4739
2 1101
2 5038
2 4737
3 4717
4 6407
4 4731
10 4738
16 1100
19 4781
22 4904
22 4905
35 6406
38 5033
38 5024
39 4826
39 4608
39 4902
40 4735
113 4647
156 4616
239 5059
355 4688
551 4733
557 4732
605 4797
965 5061
977 5058
1647 4798
6364 4907
6759 4634
7000 4648
10950 4799
19407 4672
22049 4624
But what I want is to include the "Description/Message" Column to show what each event ID corresponds to. For example, for event ID the Message Property contains the following value(?):
An attempt was made to reset an account's password.
Subject:
Security ID: S-1-5-18
Account Name: [EDITED]
Account Domain: [EDITED]
Logon ID: 0x3E7
Target Account:
Security ID: [EDITED]
Account Name: Administrator
Account Domain: [EDITED]
Out of this entire Message I'd wish to extract only the following line:
An attempt was made to reset an account's password.
Thus getting back to my original view, ideally it would show the following:
Count Name Message
----- ---- ----
1 4724 An attempt was made to reset an account's password.
1 4722 A user account was enabled.
1 1102 The audit log was cleared.
(...)
Try this:
$Events = Get-WinEvent -FilterHashtable #{logname="security"} | Group-Object id
$Events | Select-Object Count,Name,#{Name='Message';Expression={ (($_.Group.Message | Select -First 1) -Split "`n")[0] }} | Sort-Object Count -Descending | Format-Table -Wrap
Works by removing the -NoElement parameter of Group-Object so that we get the Group result returned, which we can then retrieve the first line of the message property from.
We use Select-Object to add a calculated property to the result that contains the message.
Also using Format-Table -Wrap so the view of the final output doesn't truncate the first line if its long.
Example output:
Count Name Message
----- ---- -------
81 4798 A user's local group membership was enumerated.
13 5379 Credential Manager credentials were read.
5 5061 Cryptographic operation.
1 5058 Key file operation.

How to write a custom event log by an already existing provider with PowerShell?

I am trying to find out the Name/Value mappings of the "State" data in the message of the 'Network Connected' event log:
Path = Microsoft-Windows-NetworkProfile/Operational
Source = NetworkProfile
Event ID = 10000
So I figured I'll write a custom event log by the same provider and to the same log (path) while changing the "State" value of the message, then I can see the name mapping of that value in the event viewer.
For example, I have these Value/Name mappings so far:
1 --> 'Connected'
5 --> 'Connected, IPV4 (Local)'
9 --> 'Connected, IPV4 (Internet)'
and I want to know the rest of them.
So I tried the New-WinEvent CmdLet in PowerShell to write the logs:
New-WinEvent -ProviderName Microsoft-Windows-NetworkProfile -Id 10000 -Payload #("SSID","Description","{B58F86AB-F35D-4F73-A41E-98EA359E1D08}",0,1,0)
And it was created, but the last 4 arguments I passed to the -Payload parameter were not taking effect. Only the {"name" = "SSID" and "Description" = "Description"} were appearing in that event. The last 4 arguments stay at fixed values no matter how I change them, while there were no errors or warnings when executing this line, neither did -Verbose show anything.
I passed these arguments (especially last 3) in all types and values available. I even passed the arguments of an earlier event log (Not logged by me) to this parameter suspecting I was mistaking the data-types but nothing changed.
$a = ((Get-WinEvent -ProviderName Microsoft-Windows-NetworkProfile -MaxEvents 50 | Where-Object {$_.Id -eq 10000})[-1]).properties[3].value
$b = ((Get-WinEvent -ProviderName Microsoft-Windows-NetworkProfile -MaxEvents 50 | Where-Object {$_.Id -eq 10000})[-1]).properties[4].value
$c = ((Get-WinEvent -ProviderName Microsoft-Windows-NetworkProfile -MaxEvents 50 | Where-Object {$_.Id -eq 10000})[-1]).properties[5].value
New-WinEvent -ProviderName Microsoft-Windows-NetworkProfile -Id 10000 -Payload #("SSID","Description","{B58F86AB-F35D-4F73-A41E-98EA359E1D08}",$a,$b,$c)
Then I tried the Write-EventLog CmdLet:
Write-EventLog -LogName "Microsoft-Windows-NetworkProfile/Operational" -Source "NetworkProfile" -EventID 10000 -EntryType Information -Message $msg -Category 0
But I kept getting the error: Write-EventLog : The source name "NetworkProfile" does not exist on computer "localhost". Although the source does exist and it's the source of the 'Network Connected' log, as you can see from the screenshot.
What am I doing wrong with these 2 CmdLets?
I managed to make the first CmdLet New-WinEvent work. Oddly it was a data type issue.
The 'Network Connected' event expects 6 arguments for its message. The expected types for these arguments can be seen in this Warning I got from PowerShell
WARNING: Provided payload does not match with the template that was defined for event id 1000. The defined template is following:
I was passing the Guid argument as a string, but it expects it to have a [System.Guid] type, and apparently New-WinEvent doesn't give warnings when you pass the 6 arguments of the -Payload parameter in an array, even if one argument doesn't have the right type. It just creates a new event with some fixed default arguments (like what was happening in my problem).
So I had to cast the right type to this argument Guid. I got the name of its type from this:
$validEvent = (Get-WinEvent -ProviderName Microsoft-Windows-NetworkProfile -MaxEvents 500 | Where-Object {$_.Id -eq 10000} | Where-Object {$_.properties[4].Value -eq 9})[-1]
$validEvent.Properties[2].Value.GetType().FullName
Then I casted the right types to the arguments and passed them to -Payload and it worked:
$name = 'SSID'
$desc = 'Description'
[System.Guid]$guid = "c48f86ab-f35d-4f73-a41e-99ea359e1d08"
[System.UInt32]$type = 1
[System.UInt32]$state = 63
[System.UInt32]$categ = 2
New-WinEvent -ProviderName Microsoft-Windows-NetworkProfile -Id 10000 -Payload #($name, $desc, $guid, $type, $state, $categ)
Then I could change the value of $state to get its name mapping from the $newLog.Message.
However, the second CmdLet Write-EventLog didn't work; apparently it can't write to this log by the same provider.
As Max mentioned, this CmdLet can only write to the "classic" event log, that's why it couldn't find the NetworkProfile source.
Some links that helped me along the way:
How to store an object in the Windows Event Log? [Answer] by Grady G Cooper
Writing to the event log in .NET - the right way
MSDN - Event Sources
TechNet - New-WinEvent

Sorting by (custom) expression output

So I have this as my current code:
Get-Process | Sort Valid,ProcessName |
Format-Table #{n='ProcessName';e={$_.ProcessName}},
#{n='Valid';e={if(($_.mainmodule.filename | Get-AuthenticodeSignature).Status -eq 'Valid') {1} else {0}}} -AutoSize
which gives me an output of:
ProcessName Valid
----------- -----
3DG4me 1
Adobe CEF Helper 1
Adobe CEF Helper 1
Adobe Desktop Service 1
AdobeIPCBroker 1
AdobeUpdateService 1
AGSService 1
ApplicationFrameHost 1
audiodg 0
avgnt 1
avguard 1
Avira.ServiceHost 1
Avira.Systray 1
avshadow 1
Calculator 0
CCLibrary 1
....etc etc
Even though I put a sort before I formatted it won't let me sort by Valid, which is an integer. I've tried adding [int] before {1} and {0} but it doesn't seem to be working.
You cannot sort by properties that aren't created until after the sorting happened. Valid is not a property of System.Diagnostic.Process objects. If you want to sort by that calculated property you need to add it before sorting. This insertion is usually done via Select-Object:
Get-Process |
Select-Object ProcessName,
#{n='Valid';e={if(($_.mainmodule.filename | Get-AuthenticodeSignature).Status -eq 'Valid') {1} else {0}}} |
Sort Valid, ProcessName |
Format-Table -AutoSize

How to use the "-Property" parameter for PowerShell's "Measure-Object" cmdlet?

Why does
$a = GPS AcroRd32 | Measure
$a.Count
work, when
GPS AcroRd32 | Measure -Property Count
doesn't?
The first example returns a value of 2, which is what I want, an integer.
The second example returns this:
Measure-Object : Property "Count" cannot be found in any object(s) input.
At line:1 char:23
+ GPS AcroRd32 | Measure <<<< -Property Count
+ CategoryInfo : InvalidArgument: (:) [Measure-Object], PSArgumentException
+ FullyQualifiedErrorId : GenericMeasurePropertyNotFound,Microsoft.PowerShell.Commands.MeasureObjectCommand
This Scripting Guy entry is where I learned how to use the "Count" Property in the first code sample.
The second code sample is really confusing. In this Script Center reference, the following statement works:
Import-Csv c:\scripts\test.txt | Measure-Object score -ave -max -min
It still works even if it's re-written like so:
Import-Csv c:\scripts\test.txt | Measure-Object -ave -max -min -property score
I don't have too many problems with accepting this until I consider the Measure-Object help page. The parameter definition for -Property <string[]> states:
The default is the Count (Length) property of the object.
If Count is the default, then shouldn't an explicit pass of Count work?
GPS AcroRd32 | Measure -Property Count # Fails
The following provides me the information I need, except it doesn't provide me with an integer to perform operations on, as you'll see:
PS C:\Users\Me> $a = GPS AcroRd32 | Measure
PS C:\Users\Me> $a
Count : 2
Average :
Sum :
Maximum :
Minimum :
Property :
PS C:\Users\Me> $a -is [int]
False
So, why does Dot Notation ($a.count) work, but not an explicitly written statement (GPS | Measure -Property Count)?
If I'm supposed to use Dot Notation, then I will, but I'd like to take this opportunity to learn more about how and *why PowerShell works this way, rather than just building a perfunctory understanding of PowerShell's syntax. To put it another way, I want to avoid turning into a Cargo Cult Programmer/ Code Monkey.
Because the COUNT property is a property of the OUTPUT object (i.e. results of Measure-Object), not the INPUT object.
The -property parameter specifies which property(ies) of the input objects are to be evaluated. None of these is COUNT unless you pass an array or arrays or something.
I think what you want is something like this:
gps AcroRd32 | measure-object | select -expand Count
One thing you need to know is that in PowerShell generally, and particulary in CmdLets you manipulate objects or collection of objects.
Example: if only one 'AcroRd32' is running Get-Process will return a [System.Diagnostics.Process], if more than one are running it will return a collection of [System.Diagnostics.Process].
In the second case you can write:
(GPS AcroRd32).count
Because a collection has a count property. The duality object collection is also valid in CmdLets parameters that most of the time supports objects or list of objects (collection built with the operator ,).
PS C:\> (gps AcroRd32) -is [object[]]
True
Just use the Get-Member cmdlet:
PS C:\> (gps AcroRd32) | Get-Member
TypeName: System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------
Handles AliasProperty Handles = Handlecount
... ...
And
PS C:\> Get-Member -InputObject (gps AcroRd32)
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
... ...
If you're just looking for the count you can do the following:
$a = GPS AcroRd32
$a.Count = 2
$a = GPS AcroRd32 sets $a to an array of process objects. The array has a member call, Count, that will allow you to determine the number of elements already.
The Measure-Object commandlet (with alias measure) is used to measure the average, maximum, minimum, and sum values of a property. So you could do something like $a | measure -property Handles -sum and get a count of the total number of open handles.