Repository navigation
Expand file tree
/
Copy pathSend-Signal.ps1
More file actions
241 lines (205 loc) · 11.7 KB
/
Copy pathSend-Signal.ps1
File metadata and controls
241 lines (205 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#######################################################################################################################
# Send-Signal.ps1
#
# Parse System event log for specifc log, sources, and ids defined in the json files located at the
# $EventsJsonFilePath. Should be configured to run on a schedule via Task Scheduler.
#
# Requires Python27 to be in the PATH environment variable.
#
# Examples:
# PS> Send-Signal
# PS> Send-Signal -PyModule ".\report_event.py" -EventsJsonFilePath ".\"
#
#######################################################################################################################
[CmdletBinding()]
Param(
[String] $PyModule = "..\report_event.py",
[String] $EventsJsonFilePath = ".\json",
[String] $EnvironmentID = "123456789"
)
# start logging
$ErrorActionPreference="SilentlyContinue"
Stop-Transcript | out-null
$ErrorActionPreference = "Continue"
Start-Transcript -Path "$env:temp\Send-Signal.log" -append
function Send-Events {
[CmdletBinding()]
Param(
[Object[]] $EventCollection,
[String] $Source,
[PSCustomObject] $FiltersPSCustomObject,
[String] $MAC
)
if($EventCollection -eq $Null) {
Write-Verbose "eventcollection passed to Report-Events was null"
# stop logging
Stop-Transcript
return 0 >$Null
}
if($FiltersPSCustomObject -eq $Null) {
Write-Verbose "no filters found in FiltersPSCustomObject"
# stop logging
Stop-Transcript
return 1 >$Null
}
# Create a proper hashtable out of the PSCustomObject created from the ids json field down. The keys below
# correspond to the different ids, and the filters are an array of the filter text for each id. So filterTable
# is a hashtable with elements of type <string, string[]>
$filterTable = [System.Collections.Hashtable]@{}
$keys = ($FiltersPSCustomObject | Get-Member | Where-Object -Property "MemberType" -eq "NoteProperty").Name
foreach ($key in $keys) {
$filters = ($FiltersPSCustomObject.$key | Get-Member | Where-Object -Property "MemberType" -eq "NoteProperty").Name
$filterTable.Add($key, $filters) > $Null
}
foreach ( $evt in $EventCollection ) {
# only report events from node running this script
if ( ($evt.MachineName -like "$env:COMPUTERNAME") -Or ($evt.MachineName -like "$env:COMPUTERNAME.$env:USERDNSDOMAIN") ) {
$evt_summary = $evt_type = $evt_severity = $evt_category = $evt_layers = $Null
# check if event message matches one of the filters for this event's id
foreach ($filter in $filterTable.($evt.EventID.ToString())) {
# Try to match a filter from this event id in the hashtable to the event message (-Not $Null is a pattern match)
if (-Not (($evt.Message | Select-String -Pattern $filter) -eq $Null)) {
$evt_summary = $FiltersPSCustomObject.($evt.EventID.ToString()).$filter[0]
$evt_type = $FiltersPSCustomObject.($evt.EventID.ToString()).$filter[1]
$evt_severity = $FiltersPSCustomObject.($evt.EventID.ToString()).$filter[2]
$evt_category = $FiltersPSCustomObject.($evt.EventID.ToString()).$filter[3]
$evt_layers = [System.Collections.ArrayList]@()
$i = 4
while(-Not $FiltersPSCustomObject.($evt.EventID.ToString()).$filter[$i] -eq 0) {
$evt_layers.Add($FiltersPSCustomObject.($evt.EventID.ToString()).$filter[$i]) >$Null
$i++
}
}
}
# don't report this event if no filter matched the message
if ($evt_summary -eq $Null) {
Write-Verbose ("No filter found in " + $evt.Message + ". Skipping event.")
Continue
}
$tz = [TimeZoneInfo]::Local
$supportDst = $tz.SupportsDaylightSavingTime
$date = Get-Date
$dst = $date.IsDaylightSavingTime()
if($supportDst -match "True" -and $dst -match "True") {
$tzinfo = (($tz.BaseUtcOffset.Hours) + 1).ToString("00") + [Math]::abs($tz.BaseUtcOffset.Minutes).ToString("00")
} else {
$tzinfo = $tz.BaseUtcOffset.Hours.ToString("00") + [Math]::abs($tz.BaseUtcOffset.Minutes).ToString("00")
}
foreach ($layer in $evt_layers) {
Write-Verbose ("Sending the following event details to the python script:`n" + $EnvironmentID + "`n" + $MAC + "`n" + $Source + "`n" + $evt.EventID + "`n" + $evt_severity + "`n" + $evt.Message + "`n" + ($evt.TimeGenerated.ToString("yyyy-MM-ddTHH:mm:ss") + $tzinfo) + "`n" + $evt_summary + "`n" + $evt_type + "`n" + $evt_category + "`n" + $layer)
Invoke-Command -ScriptBlock {
Param(
[string] $a1,
[string] $a2,
[string] $a3,
[string] $a4,
[string] $a5,
[string] $a6,
[string] $a7,
[string] $a8,
[string] $a9,
[string] $a10,
[string] $a11
)
if($PyModule -Like "*.exe") {
&$PyModule $a1 $a2 $a3 $a4 $a5 $a6 $a7 $a8 $a9 $a10 $a11
} else {
&'python' $PyModule $a1 $a2 $a3 $a4 $a5 $a6 $a7 $a8 $a9 $a10 $a11
}
} -ArgumentList @(
$EnvironmentID,
$MAC,
$Source,
$evt.EventID,
$evt_severity,
$evt.Message,
($evt.TimeGenerated.ToString("yyyy-MM-ddTHH:mm:ss") + $tzinfo),
$evt_summary,
$evt_type,
$evt_category,
$layer
)
}
}
}
# stop logging
Stop-Transcript
return 0
}
### ENTRY POINT #######################################################################################################
# parse the current epoch time as a signed long, then create DateTime object from it
$nowUnixTime = [long] (Get-Date -Date ((Get-Date).ToUniversalTime()) -UFormat %s)
$nowUniversalDateTime = (([datetime]'1/1/1970').AddSeconds($nowUnixTime)).ToLocalTime()
Write-Verbose "time now is $nowUniversalDateTime"
if(Test-Path -Path $EventsJsonFilePath) {
# find first valid, active, non-virtual MAC address
$gm = &'getmac'
$macAddress = ""
$match = $gm | Select-String -Pattern "([0-9A-Z]{2}-?){6}"
if(-Not ($match -eq $Null)) {
$macAddress = $match.Matches[0].Value;
}
# stop execution if no valid MAC address was found
if($macAddress.Equals("")) {
Write-EventLog -LogName "Application" -Source "Windows_Signal" -EventID 9002 -EntryType Critical -Message "Failed to find a valid IPv4 MAC Address."
Write-Verbose "Failed to find a valid MAC address. Exiting."
# stop logging
Stop-Transcript
return $false
}
$jsonFiles = Get-ChildItem -Path $EventsJsonFilePath -Filter "*.json"
foreach ($file in $jsonFiles) {
# parse the events json file(s) into a PSCustomObject
$desiredLogs = Get-Content -Raw -Path $file.FullName | ConvertFrom-Json
# parse out the event logs (Application, System, etc) we need to scan containing the events we care about from the json file(s)
if ($desiredLogs -ne $Null) {
$eventLogs = $desiredLogs | Get-Member | Where-Object -Property "MemberType" -eq "NoteProperty" | foreach { $_.Name }
} else {
Write-EventLog -LogName "Application" -Source "Windows_Signal" -EventID 9001 -EntryType Critical -Message "Failed to parse events file found at $EventsJsonFilePath. Either no events of interest have been configured or the file is corrupt."
Write-Verbose "Failed to parse events file " + $file.FullName + ". Either no events of interest have been configured or the file is corrupt."
}
foreach ($log in $eventLogs) {
# parse out the sources that issue the events we care about so that we can query the target log for just those sources
$eventSources = $desiredLogs.$log | Get-Member | Where-Object -Property "MemberType" -eq "NoteProperty" | foreach { $_.Name }
foreach ($source in $eventSources) {
Write-Verbose "Looking for events from $log and $source..."
# parse the last epoch time this script succeeded then create DateTime object from it
$lastUnixTime = [long]$desiredLogs.$log.$source.lastReportTime
if ($lastUnixTime -eq 0) {
Write-Verbose "SETTING LAST UNIX TIME TO 5 MIN AGO"
$lastUnixTime = $nowUnixTime - 301 # if no time was recorded for last successful run, then just get last five minutes.
} else {
$lastUnixTime -= 1; # so we catch the missing second from the last time we ran Get-EventLog
}
$lastUniversalDateTime = (([datetime]'1/1/1970').AddSeconds($lastUnixTime)).ToLocalTime()
Write-Verbose "lastUnixTime: $lastUnixTime"
Write-Verbose "last successful run was performed at $lastUniversalDateTime"
# Get events from the specified $log and $source between the last run and the start of this one, but only those with ids
# as specified in the json file for this log and source.
$events = Get-EventLog -LogName $log -After $lastUniversalDateTime -Before $nowUniversalDateTime -Source $source | Where-Object {
($desiredLogs.$log.$source.ids | Get-Member -MemberType NoteProperty).Name -Contains $_.EventId.ToString()
}
# Package and send these events to the iQ appliance if they match the events specified in this json file (passed in via -FilersPSCustomObject)
Send-Events -EventCollection $events -Source $source -FiltersPSCustomObject ($desiredLogs.$log.$source.ids) -MAC $macAddress
# update the timestamps for this $log,$source if Report-Events succeeded
if ($?) {
if (($desiredLogs.$log.$source | Get-Member -MemberType NoteProperty).Name -Contains "lastReportTime") {
Write-Verbose "updating timestamp for $source"
$desiredLogs.$log.$source.lastReportTime = $nowUnixTime
} else {
Write-Verbose "adding lastReportTime member with value $nowUnixTime"
$desiredLogs.$log.$source | Add-Member -NotePropertyName lastReportTime -NotePropertyValue $nowUnixTime
}
}
}
}
# overwrite the events file to update the successful timestamps with the time we started running this script
$desiredLogs | ConvertTo-Json -Depth 5 > $file.FullName
}
} else { # fail this run as the path containing the events json file(s) is not reachable
Write-EventLog -LogName "Application" -Source "Windows_Signal" -EventID 9000 -EntryType Critical -Message "No events file found at $EventsJsonFilePath."
Write-Verbose "No events file found at $EventsJsonFilePath."
# stop logging
Stop-Transcript
return $true
}