Taking inspiration on Get informations on Windows Scheduled Task duration (execution time)
we aim to filter on a set of tasks, instead of fetching all from task scheduler.
something of this sort:
$logName = 'Microsoft-Windows-TaskScheduler/Operational'
$xPathFilter = #'
*[
System[(EventID=200 or EventID=201)] and
EventData[
Data[#Name="TaskName"] = "001_task_A_nightly"
]
]
'#
Get-WinEvent -LogName $logName -FilterXPath $xPathFilter | Group-Object ActivityID | ForEach-Object {
$start = $_.Group |
Where-Object { $_.Id -eq 200 } |
Select-Object -Expand TimeCreated -First 1
$end = $_.Group |
Where-Object { $_.Id -eq 201 } |
Select-Object -Expand TimeCreated -First 1
New-Object -Type PSObject -Property #{
'TaskName' = $_.Group[0].Properties[0].Value
'Duration' = ($end - $start).TotalSeconds
Is there a way to adapt this one above?
Use the -FilterXPath parameter set instead, it'll allow you to granularly filter on the contents of the underlying event XML:
$logName = 'Microsoft-Windows-TaskScheduler/Operational'
$xPathFilter = #'
*[
System[(EventID=200 or EventID=201)] and
EventData[
Data[#Name="TaskName"] = "\MyTask" or
Data[#Name="TaskName"] = "\MyOtherTask" or
Data[#Name="TaskName"] = "\TaskFolder\SomeThirdTask"
]
]
'#
Get-WinEvent -LogName $logName -FilterXPath $xPathFilter |Group-Object ...
Related
The following code in powershell creates a file with key/value pairs.
$result = #()
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-5)) -InstanceId 4624 |
ForEach-Object {
if ($_.ReplacementStrings[5] -ne "SYSTEM")
{
$result += [PSCustomObject]#{
Time = $_.TimeGenerated
Workstation = $_.ReplacementStrings[11]
}
}
}
#$result | Export-Csv -Path .\Logins.csv -NoTypeInformation
$result | Out-File "C:\Temp\Logins.csv"
The above results in the following file contents:
However, I want the contents in CSV format. If I change the commented lines out as below:
$result = #()
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-5)) -InstanceId 4624 |
ForEach-Object {
if ($_.ReplacementStrings[5] -ne "SYSTEM")
{
$result += [PSCustomObject]#{
Time = $_.TimeGenerated
Workstation = $_.ReplacementStrings[11]
}
}
}
$result | Export-Csv -Path .\Logins.csv -NoTypeInformation
#$result | Out-File "C:\Temp\Logins.csv"
Then I get the following:
Googling around through myriad pages and examples, I (mis?)understand this to be a hashtable and that the Export-Csv should work to create a csv file. I cannot seem to get it to work.
Any help is greatly appreciated.
Hmmm ... the following code works exactly like I'd expect it:
$result =
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-5)) -InstanceId 4624 |
ForEach-Object {
if ($_.ReplacementStrings[5] -ne "SYSTEM") {
[PSCustomObject]#{
Time = $_.TimeGenerated
Workstation = $_.ReplacementStrings[11]
}
}
}
$result | Export-Csv -Path .\Logins.csv -NoTypeInformation
BTW: It is recommended not to use Get-Eventlog anymore. Use Get-WinEvent instead. ;-)
Another option could be just this:
Clear-Host
$result = #()
Get-EventLog -LogName Security -After ((Get-Date).AddDays(-1)) -InstanceId 4624 |
ForEach-Object {
if ($_.ReplacementStrings[5] -ne "SYSTEM")
{
$result += [PSCustomObject]#{
Time = $PSItem.TimeGenerated
Workstation = $PSItem.ReplacementStrings[11]
}
}
}
$result | ConvertTo-Csv -NoTypeInformation | Out-File -FilePath 'Logins.csv'
Get-Content -Path 'Logins.csv'
# Results
<#
"Time","Workstation"
...
"06-Aug-22 16:45:37","-"
"06-Aug-22 16:45:17","-"
"06-Aug-22 16:44:29","-"
...
#>
Update as per my comment.
The results are the same as the above, either to the screen or the file:
Clear-Host
(Get-EventLog -LogName Security -After ((Get-Date).AddDays(-1)) -InstanceId 4624) -ne 'SYSTEM' |
Select-Object -Property #{Name = 'Time';Expression = {$PSItem.TimeGenerated}},
#{Name = 'Workstation';Expression = {$PSItem.ReplacementStrings[11]}} |
Export-Csv -Path 'Logins.csv'
Get-Content -Path 'Logins.csv'
I need to read specific information from event log. in below script I cannot export the some value contains in XML View. In the XML view I Want Target username: BRSYSAD and Subjectusername : 900011-LT
Script
$filter = "*[System[EventID=4740 and Provider[#Name='Microsoft-Windows-Security-Auditing']]]"
$result = Get-WinEvent -LogName Security -FilterXPath $filter | ForEach-Object {
# convert the event to XML and grab the Event node
$eventXml = ([xml]$_.ToXml()).Event
# output the properties you need
[PSCustomObject]#{
EventID = $eventXml.System.EventID.'#text'
TimeCreated = $eventXml.System.TimeCreated.SystemTime -replace '\.\d+.*$'
Computer = $eventXml.System.Computer
Data = $eventXml.EventData.Data
}
}
# output on screen
$result | Format-Table -AutoSize
# save as CSV file if you like
$result | Export-Csv -Path 'C:\MyProgr_Events_302.csv' -NoTypeInformation
You should be able to get the TargetUserName and SubjectUserName properties by filtering the EventData for those specifically named attributes.
Example code updated (I've also removed the .'#text' part from the EventID line to ensure this value is captured)
$filter = "*[System[EventID=4740 and Provider[#Name='Microsoft-Windows-Security-Auditing']]]"
$result = Get-WinEvent -LogName Security -FilterXPath $filter | ForEach-Object {
# convert the event to XML and grab the Event node
$eventXml = ([xml]$_.ToXml()).Event
# output the properties you need
[PSCustomObject]#{
EventID = $eventXml.System.EventID
TimeCreated = $eventXml.System.TimeCreated.SystemTime -replace '\.\d+.*$'
Computer = $eventXml.System.Computer
TargetUserName = ($eventXml.EventData.Data | Where-Object { $_.Name -eq "TargetUserName"}).'#text'
SubjectUserName = ($eventXml.EventData.Data | Where-Object { $_.Name -eq "SubjectUserName"}).'#text'
}
}
# output on screen
$result | Format-Table -AutoSize
# save as CSV file if you like
$result | Export-Csv -Path 'C:\MyProgr_Events_302.csv' -NoTypeInformation
If you prefer, you could pull out all attributes with the following instead:
$filter = "*[System[EventID=4740 and Provider[#Name='Microsoft-Windows-Security-Auditing']]]"
$result = Get-WinEvent -LogName Security -FilterXPath $filter | ForEach-Object {
# convert the event to XML and grab the Event node
$eventXml = ([xml]$_.ToXml()).Event
# output the properties you need
$object = [PSCustomObject]#{
EventID = $eventXml.System.EventID
TimeCreated = $eventXml.System.TimeCreated.SystemTime -replace '\.\d+.*$'
Computer = $eventXml.System.Computer
}
$eventXml.EventData.Data | ForEach-Object { $object | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_.'#text' }
$object
}
# output on screen$
$result | Format-Table -AutoSize
You'd then end up with all the attributes available to you and each result would contain data such as:
looking for info on the below piece of code, it actually does what's expected of it. It retrieves the time duration of the 'CreateTimesheets' task for the past 7 days. However it finishes with an error message.
Get-WinEvent -FilterHashtable #{
'LogName' = 'Microsoft-Windows-TaskScheduler/Operational'
'ID' = 200, 201
'StartTime' = [datetime]::Today.AddDays(-7)
'EndTime' = [datetime]::Today
} | Group-Object ActivityID | ForEach-Object {
if($_.Group.Properties[0].Value -like '*CreateTimesheets*'){
$start = $_.Group |
Where-Object { $_.Id -eq 200 } |
Select-Object -Expand TimeCreated -First 1
$end = $_.Group |
Where-Object { $_.Id -eq 201 } |
Select-Object -Expand TimeCreated -First 1
New-Object -Type PSObject -Property #{
'TaskName' = $_.Group[0].Properties[0].Value
'StartTime' = $start
'Duration' = ($end - $start).TotalSeconds
} }
}
The error message is as follows
Cannot find an overload for "op_Subtraction" and the argument count: "2".
At line:15 char:12
+ New-Object -Type PSObject -Property #{
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
i'm quite new to powershell scripting and have found the same error message on other stack overflow questions but the scenarios seem quite different to this, could anyone clear this up for me? thanks
My Guess is you have a grouped ActivityID without a matching 200 / 201 ID. (Task that didn't finish during the time you ran your script)
If you only want to tasks that have finished and have an $end value - see if modifying line
6 like this does the trick:
} | Group-Object ActivityID | Where-Object {$_.Count -gt 1} | ForEach-Object {
You may want to clear out the $start and $end variables just to ensure values aren't being carried over from the previous group. This might reveal which record(s) are causing the error(s). I was not able to duplicate the issue. Depending on amount of entries you're processing it might also be beneficial to filter out the taskname prior to grouping. Finally, it's slightly faster to use the [PSCustomObject] type accelerator instead of New-Object. I've made a couple of adjustments to your code in the first example and then a slightly different version of the same thing following that.
Updated original
Get-WinEvent -FilterHashtable #{
'LogName' = 'Microsoft-Windows-TaskScheduler/Operational'
'ID' = 200, 201
'StartTime' = [datetime]::Today.AddDays(-7)
'EndTime' = [datetime]::Today
} | Group-Object ActivityID | ForEach-Object {
if($_.Group.Properties[0].Value -like '*CreateTimesheets*'){
Remove-Variable start,end -ErrorAction SilentlyContinue
$start = $_.Group |
Where-Object { $_.Id -eq 200 } |
Select-Object -Expand TimeCreated -First 1
$end = $_.Group |
Where-Object { $_.Id -eq 201 } |
Select-Object -Expand TimeCreated -First 1
[PSCustomObject]#{
TaskName = $_.Group.Properties[0].Value
StartTime = $start
Duration = ($end - $start).TotalSeconds
}
}
}
Another possible version
Get-WinEvent -FilterHashtable #{
'LogName' = 'Microsoft-Windows-TaskScheduler/Operational'
'ID' = 200, 201
'StartTime' = [datetime]::Today.AddDays(-7)
'EndTime' = [datetime]::Today
} | Where-Object Message -match "CreateTimesheets" | Group-Object ActivityID | ForEach-Object {
Remove-Variable start,end -ErrorAction SilentlyContinue
$start,$end = $_.Group.where({$_.Id -eq 200 },'split').timecreated
[PSCustomObject]#{
TaskName = $_.Group.Properties[0].Value
StartTime = $start
Duration = ($end - $start).TotalSeconds
}
}
The adjusted version tested ~10% faster than the original but I didn't have a suitable custom task to filter for. Both provided the same exact records in the end for all tests.
I try to filter Windows events (id=4633) from eventlogs out of a given filestructure recursivly with Get-ChildItem.
The filestructure looks like this:
C:\Temp\raw_data\2018-09\Securitylog\Securitylog_2018-09-14_13-30.evtx
The problem is, that the Get-ChildItem only handles the first level. Every level below the first one seems to be ignored.
I tried using the -Recurse parameter without success. I don't receive any errors, so I believe the syntax is correct. The codesample is given below.
$out = New-Object System.Text.StringBuilder
$out.AppendLine("ServerName,EventID,TimeCreated,UserName,File_or_Folder,AccessMask")
$ns = #{e = "http://schemas.microsoft.com/win/2004/08/events/event"}
Get-ChildItem "C:\temp\raw_data" -Recurse | ForEach-Object {
{
$evts = Get-WinEvent -FilterHashtable #{Path=$_;id="4663"} -Oldest
foreach ($evt in $evts) {
$xml = $evt.ToXml()
$SubjectUserName = Select-Xml -Xml $xml -Namespace $ns -XPath "//e:Data[#Name='SubjectUserName']/text()" |
Select-Object -ExpandProperty Node |
Select-Object -ExpandProperty Value
$ObjectName = Select-Xml -Xml $xml -Namespace $ns -XPath "//e:Data[#Name='ObjectName']/text()" |
Select-Object -ExpandProperty Node |
Select-Object -ExpandProperty Value
$AccessMask = Select-Xml -Xml $xml -Namespace $ns -XPath "//e:Data[#Name='AccessMask']/text()" |
Select-Object -ExpandProperty Node |
Select-Object -ExpandProperty Value
$out.AppendLine("$($svr),$($evt.id),$($evt.TimeCreated),$SubjectUserName,$ObjectName,$AccessMask")
Write-Host $svr
Write-Host $evt.id, $evt.TimeCreated, $SubjectUserName, $ObjectName,$AccessMask
}
}
}
$out.ToString() | Out-File -FilePath "C:\Temp\X4663Events.csv"
I have no idea, why no file is processed. The file will be created, but it is empty execpt for the headline.
It doesn't have to be that hard. Typical powershell looks more like this. Build an object, then export to csv. Know what you're doing, and verify each piece.
$evts = get-winevent security -MaxEvents 3
$objs = foreach ($evt in $evts) {
[xml]$xml = $evt.toxml()
$subjectusername = $xml.event.EventData.data |
where name -eq subjectusername |
select -expand '#text'
$subjectdomainname = $xml.event.EventData.data |
where name -eq subjectdomainname |
select -expand '#text'
[pscustomobject]#{
SubjectUserName = $subjectusername
SubjectDomainName = $subjectdomainname
}
}
$objs
$objs | export-csv mylog.csv
get-content mylog.csv
SubjectUserName SubjectDomainName
--------------- -----------------
SYSTEM NT AUTHORITY
MYCOMP$ AD
MYCOMP$ AD
#TYPE System.Management.Automation.PSCustomObject
"SubjectUserName","SubjectDomainName"
"SYSTEM","NT AUTHORITY"
"MYCOMP$","AD"
"MYCOMP$","AD"
I want to get the most errors of a list of servers and save it in the variable $AllErrors.
But the variable is empty if I want to print it out.
Is there a way to pass the variable out of the Invoke-Command?
This is my code:
Get-Content -Path C:\Users\$env:USERNAME\Desktop\Server.txt |
ForEach-Object{
Invoke-Command -ComputerName $_ -ScriptBlock{
$Date = (Get-Date).AddHours(-12)
$Events = Get-WinEvent -FilterHashtable #{LogName = 'System'; StartTime = $Date; Level = 2}
$Errors = $Events | Group-Object -Property ID -NoElement | Sort-Object -Property Count -Descending |
Select-Object -Property Name -First 1
}
}
$Errors | Out-File -FilePath C:\Users\$env:USERNAME\Desktop\AllErrors.txt
No, it's not possible, however you can assign the output of Invoke-Command to a variable. $Errors seems to be the only output of your Invoke-Command, so this should work. But looking at your code you will only get Errors for the last computer as you are constantly overwriting the $Errors variable in cycle. You should either declare $Errors outside of cycle, and append errors to it, or append to file after each cycle:
Get-Content -Path C:\Users\$env:USERNAME\Desktop\Server.txt |
ForEach-Object{
$Errors = Invoke-Command -ComputerName $_ -ScriptBlock{
$Date = (Get-Date).AddHours(-12)
$Events = Get-WinEvent -FilterHashtable #{LogName = 'System'; StartTime = $Date; Level = 2}
$Events | Group-Object -Property ID -NoElement | Sort-Object -Property Count -Descending |
Select-Object -Property Name -First 1
}
$Errors | Out-File -FilePath C:\Users\$env:USERNAME\Desktop\AllErrors.txt -Append
}