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

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.

Related

Powershell to match the current line and next line then out-file

I'm trying to extract the data whereby:
line 1 = Report ID + Line 2 = "Machine no" + Line 3 = OFFLINE
Then Out-File to a new file.
Sample data
Report ID page1
Machine no 1234
OTHERS
12
offline
12
OTHERS
23
offline
37
OTHERS
89
offline
65
The result I'm looking for look something like the below after processing:
Report ID page 4
Machine no 1234
offline
12
offline
37
offline
65
You can use the Select-String cmdlet with the -Context Parameter to search through a file and then select how many lines of contextual info you want to get back from your search.
For instance, if we take your input file and store it in a variable called $input like so:
$inputFile= Get-Content C:\Path\To\YourFile.txt
$inputFile| Select-string 'machine no'
>Machine no 1234
We can then find matches for the phrase 'offline' with this:
$inputFile| Select-String offline -Context 0,1
This states that I want you to search for the word 'offline' and give me zero lines proceeding it, and one line after it, giving us this output:
> offline
12
> offline
37
> offline
65
We can put this all together to build this and generate a new output file that would look like this.
$out= ''
$out += $inputFile| Select-string 'machine no'
$out += "`n"
$out += $inputFile| Select-String offline -Context 0,1 | ForEach-Object {$_.Context.DisplayPostContext}
#Resulting file would look this, just the name of the machine and then the number of each offline...uh, whatever it is.
Machine no 1234
12 37 65
If I were you, I'd adapt this flow to make PowerShell objects and properties instead, like this:
$Report = [pscustomobject]#{ID='';MachineNo='';OfflineMembers=#()}
$Report.ID = ($inputFile | select-string page -Context 0 ).ToString().Split('page')[-1]
$Report.MachineNo = ($inputFile | Select-string 'machine no').ToString().Trim().Split()[-1]
$Report.OfflineMembers = $inputFile | Select-String offline -Context 0,1 | ForEach-Object {
[pscustomobject]#{Value='Offline';ID=$_.Context.DisplayPostContext.Trim()}
}
>$Report
ID MachineNo OfflineMembers
-- --------- --------------
1 1234 {#{Value=Offline; ID=12}, #{Value=Offline; ID=37}, #{Value=Offline; ID=65}}
$Report.OfflineMembers
Value ID
----- --
Offline 12
Offline 37
Offline 65

No output from Powershell command when run from script vs cli

This one command doesn't write output to the screen when running it in a script, but it works when executing it in the PowerShell ISE cli:
$toptenseverity = $csvData | select Severity, Title -Unique | sort Severity -Descending | select -First 11
$toptenseverity
Code:
Write-Host "`r`nTop 10 most severe vulnerabilities:"
$toptenseverity = $csvData | select Severity, Title -Unique | sort Severity -Descending | select -First 11
$toptenseverity
Write-Host "Trying again to write output of toptenseverity using write host toptenseverity:"
Write-Host $toptenseverity
Write-Host "Trying again to write output of toptenseverity using write output toptenseverity:"
Write-Output $toptenseverity
Output:
Generating P1 report. Please wait...
Total P1 count: 352
Severity 5 total: 11
Severity 4 total: 16
Severity 3 total: 325
Top 10 most severe vulnerabilities:
Trying again to write output of toptenseverity using write host toptenseverity:
#{Severity=5; YouDon'tNeedToKnowThis} #{Severity=4; Title=YouDon'tNeedToKnowThis} #{Severity=4; Title=YouDon'tNeedToKnowThis
} #{Severity=4; Title=YouDon'tNeedToKnowThis} #{Severity=4; Title=YouDon'tNeedToKnowThis}
Trying again to write output of toptenseverity using write output toptenseverity:
When I run it from the cli in PS ISE I get this output:
Severity Title
-------- -----
5 YouDon'tNeedToKnowThis
4 YouDon'tNeedToKnowThis
4 YouDon'tNeedToKnowThis
4 YouDon'tNeedToKnowThis
4 YouDon'tNeedToKnowThis
Use:
Write-Output $toptenseverity | Format-Table
This forces the object to be formatted as a table, which is what's going on in the ISE (by default)

Displaying results where count equals value

I want to identify the top few processes by average CPU usage over a period of couple of seconds. Each result needs to list the PID, Process Name and UserID.
The script needs to be run on several servers at the same time that are in another domain. The credentials for those servers are different to the computer it is being run from.
I can’t get Get-Process to work as it doesn’t accept the –credentials parameter.
Invoke-Command will not work as scripting is restricted on these servers.
What I’ve ended up with in $TopProcess is:
User Name CPU ID Description
--------- --- -- -----------
User73 25 68680 App89.exe
User73 25 68888 App57.exe
LOCAL SERVICE 2.5 63868 WmiPrvSE.exe
User48 0 66308 App38.exe
User48 0 62608 App54.exe
User73 25 68888 App57.exe
User73 25 68680 App89.exe
LOCAL SERVICE 2.5 63868 WmiPrvSE.exe
User48 0 59336 dwm.exe
User48 0 52528 App57.exe
User73 25 68888 App57.exe
User73 25 68680 App89.exe
User39 19.5 48792 App43.exe
User39 2.5 65996 App48.exe
LOCAL SERVICE 2.5 63868 WmiPrvSE.exe
but I need to list only the results that have a CPU over 15 and are listed three times but only to list each once and
$Result = $TopProcess | Where {$_.CPU -gt $Threshold} | Measure | Where {$_.Count -eq $NoRS}
returns nothing.
Use the Group-Object cmdlet instead of Measure to group your result. I think you only want to get the UserName property:
$Result = $TopProcess |
Where {$_.CPU -gt $Threshold} |
group 'User Name' |
Where Count -eq $NoRS |
select Name

Powershell - Get-WinEvent

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

Returning the member in a group with the highest of a specific property

I'm trying to find the process with the highest CPU in a given group, in my case the group is simply by processname. Assume I have these processes running:
Id ProcessName CPU Memory Threads
-- ----------- --- ------ -------
7532 MicrosoftEdgeCP 40,3125 355,51953125 27
1680 powershell_ise 47,875 214,1015625 23
7568 lync 7,9375 213,859375 52
9664 chrome 19,71875 167,9609375 12
4216 MicrosoftEdgeCP 92,578125 152,2890625 26
5392 explorer 31,09375 116,390625 66
2676 chrome 23,390625 110,96875 41
9812 chrome 14,625 100,859375 8
2872 MsMpEng 100,5234375 36
5752 SearchUI 2,609375 90,0625 23
I'm trying to find Chrome (9664). Currently I have this code:
$list = #()
$grouped = Get-Process | Where ProcessName -Like "c*" |
Sort CPU -Descending |
Group ProcessName |
Select $_ -First 10
$grouped
foreach($item in $grouped) {
$list += #($item.Group |
Sort CPU -Descending |
Select Id, ProcessName, CPU, Memory, Threads -First 1)
}
$list | ft -Wrap
Is it possible to do the same without storing the first in each group in the $list array?
So you wanted to get the process where the CPU is the highest among the threads of the same name correct?
Get-Process -Name "c*" |
Sort-Object CPU -Descending |
Group ProcessName |
ForEach-Object{$_.Group | Select Id, ProcessName, CPU, Memory, Threads -First 1}
-Name supports wildcards so we can save returning all the processes just to drop most of them. Do the first sort and group on processname. Then just take the first entry from each individual group. The last for each is required to prevent the object array from being unrolled and only returning the highest overall entry.