forked from guyrleech/Citrix
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGet PVS boot time stats.ps1
More file actions
365 lines (287 loc) · 11.9 KB
/
Get PVS boot time stats.ps1
File metadata and controls
365 lines (287 loc) · 11.9 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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
#requires -version 3.0
<#
Get Citrix PVS target boot time events from event log and convert to CSV for reporting or alerting purposes
Ensure that each PVS server's stream service has event logging enabled
Guy Leech, 2017
Modification history:
13/02/18 GL Added chart view option
#>
<#
.SYNOPSIS
Get Citrix PVS target boot time events from event logs and output to CSV or a chart for reporting or alerting, by email, purposes
.DESCRIPTION
.PARAMETER computers
A comma separated list of PVS servers to query. Defaults to the computer running the script if none is given.
.PARAMETER last
Show boot times in the preceding period where 's' is seconds, 'm' is minutes, 'h' is hours, 'd' is days, 'w' is weeks and 'y' is years so 7d will show all events in the last 7 days. The default is all events.
.PARAMETER output
CSV file to create with the results.
.PARAMETER meanAbove
If the mean (average) time (in seconds) exceeds this value then an email alert is sent.
.PARAMETER medianAbove
If the median time (in seconds) exceeds this value then an email alert is sent.
.PARAMETER modeAbove
If the mode time (in seconds) exceeds this value then an email alert is sent.
.PARAMETER slowestAbove
If the slowest time (in seconds) exceeds this value then an email alert is sent.
.PARAMETER gridView
Output the results to a graphical grid view where they can be sorted/filtered
.PARAMETER chartView
Display the results in a chart
.PARAMETER mailserver
The SMTP email server to use for sending the email.
.PARAMETER recipients
Comma separated list of email addresses to send the email to.
.PARAMETER subject
The subject of the email. A default one is provided if none is specified.
.PARAMETER alertSubject
The subject of the alert email, if one is sent. A default one is provided if none is specified.
.PARAMETER from
The email address of the sender. A default one is provided if none is specified.
.PARAMETER useSSL
If specified, will communicate with the email server using SSL.
.PARAMETER search
Regex pattern to use when searching and replacing server names when they need sanitising for security purposes.
.PARAMETER replace
Regex pattern to replace in server names when they need sanitising for security purposes.
.EXAMPLE
& '.\Get PVS boot time stats.ps1' -last 7d -output c:\boot.times.csv
Will show PVS boot times in the last 7 days for the PVS server running the script and also output them to the file c:\boot.times.csv
.EXAMPLE
& '.\Get PVS boot time stats.ps1' -last 90d -chartview -computers pvsserver1,pvsserver2
Will show PVS boot times in a chart in the last 90 days for the PVS servers pvsserver1 and pvsserver2
.EXAMPLE
& '.\Get PVS boot time stats.ps1' -last 7d -output c:\boot.times.csv -mailserver mailserver -recipients someone@somewhere.com -meanAbove 180 -computers pvsserver1,pvsserver2
Will show PVS boot times in the last 7 days for the PVS servers pvsserver1 and pvsserver2 and if the average boot time is longer than 3 minutes it will send an email with the details to someone@somewhere.com.
.NOTES
Ensure that each PVS server's stream service has event logging enabled in order for the events this script looks for are generated.
#>
[CmdletBinding()]
Param
(
[string[]]$computers = @( 'localhost' ) ,
[string]$last ,
[string]$output ,
[switch]$gridView ,
[switch]$chartView ,
[int]$chartType = -1 ,
[int]$meanAbove = -1 ,
[int]$medianAbove = -1 ,
[int]$modeAbove = -1 ,
[int]$slowestAbove = -1 ,
[string]$mailserver ,
[string[]]$recipients ,
[string]$subject = "Citrix PVS boot times from $env:COMPUTERNAME" ,
[string]$alertSubject = "Citrix PVS boot times alert from $env:COMPUTERNAME" ,
[string]$from = "$($env:COMPUTERNAME)@$($env:USERDNSDOMAIN)" ,
[switch]$useSSL ,
[string]$providerName = 'StreamProcess' ,
[int]$eventId = 10 ,
[string]$eventLog = 'Application' ,
[string]$search ,
[string]$replace
)
[array]$events = @()
[int]$slowest = 0
[int]$fastest = [int]::MaxValue
[long]$totalTime = 0
[int]$count = 1
[hashtable]$modes = @{}
[dateTime]$startDate = (Get-Date).AddYears( -20 ) ## Should be long enough ago!
if( ! [string]::IsNullOrEmpty( $last ) )
{
## see what last character is as will tell us what units to work with
[int]$multiplier = 0
switch( $last[-1] )
{
"s" { $multiplier = 1 }
"m" { $multiplier = 60 }
"h" { $multiplier = 3600 }
"d" { $multiplier = 86400 }
"w" { $multiplier = 86400 * 7 }
"y" { $multiplier = 86400 * 365 }
default { Write-Error "Unknown multiplier `"$($last[-1])`"" ; return }
}
$endDate = Get-Date
if( $last.Length -le 1 )
{
$startDate = $endDate.AddSeconds( -$multiplier )
}
else
{
$startDate = $endDate.AddSeconds( - ( ( $last.Substring( 0 ,$last.Length - 1 ) -as [int] ) * $multiplier ) )
}
}
$events = ForEach( $computer in $computers )
{
Write-Verbose "$count / $($computers.Count ) : processing $computer from $startDate"
@( Get-WinEvent -ComputerName $computer -FilterHashtable @{Logname=$eventLog;ID=$eventId;ProviderName=$providerName;StartTime=$startDate} | Where-Object { $_.Message -match 'boot time'}|select TimeCreated,Message | ForEach-Object `
{
## Message will be "Device xxxxx boot time: 2 minutes 50 seconds."
if( $_.Message -match '^Device (?<Target>[^\s]+) boot time: (?<minutes>\d+) minutes (?<seconds>\d+) seconds\.$' )
{
[int]$boottime = ( $matches[ 'minutes' ] -as [int] ) * 60 + ( $matches[ 'seconds' ] -as [int] )
New-Object -TypeName PSCustomObject -Property (@{ 'TimeCreated' = $_.TimeCreated ; 'Server' = $computer ; 'Target' = $matches[ 'Target' ] ; 'BootTime' = $boottime })
$totalTime += $boottime
if( $boottime -gt $slowest )
{
$slowest = $boottime
}
if( $boottime -lt $fastest )
{
$fastest = $boottime
}
## Add to hash table for mode calculation
try
{
$modes.Add( $boottime , 1 )
}
catch
{
$modes.Set_Item( $boottime , $modes[ $boottime ] + 1 )
}
}
})
$count++
}
if( $events.Count -gt 0 )
{
## See if we need to transmogrify names to protect sensitive information
if( ! [string]::IsNullOrEmpty( $search ) )
{
$events | ForEach-Object `
{
$_.Server = $_.Server -replace $search , $replace
}
$computers = $computers -replace $search , $replace
$subject = $subject -replace $search , $replace
}
## Now find median (middle) value
[array]$sorted = $events | select BootTime | sort BootTime
## Now find mode (commonest) value
[int]$mode = 0
[int]$lastHighestCount = 0
[int]$highestCount = 0
$modes.GetEnumerator() | ForEach-Object `
{
if( $_.Value -gt $highestCount )
{
$lastHighestCount = $highestCount
$highestCount = $_.Value
$mode = $_.Key
}
}
if( $highestCount -eq $lastHighestCount -or ( $highestCount -eq 1 -and $modes.Count -gt 1 ) )
{
$mode = 0 ## no single most common boot time
}
[int]$median = $sorted[$sorted.Count / 2].BootTime
[int]$mean = [math]::Round( $totalTime / $events.Count )
[string]$summary = "Got $($events.Count) events from $($computers.Count) machines : fastest $fastest s slowest $slowest s mean $mean s median $median s mode $mode s ($highestCount instances)"
Write-Output $summary
if( ! [string]::IsNullOrEmpty( $output ) )
{
$events | Export-Csv -Path $output -NoTypeInformation -NoClobber
}
[bool]$alert = $false
[string]$cause = $null
[bool]$alerting = $meanAbove -ge 0 -or $medianAbove -ge 0 -or $modeAbove -ge 0 -or $slowestAbove -ge 0
[int]$threshold = 0
if( $meanAbove -ge 0 -and $mean -gt $meanAbove )
{
$alert = $true
$cause = 'Mean of ' + $mean
$threshold = $meanAbove
}
elseif( $medianAbove -ge 0 -and $median -gt $medianAbove )
{
$alert = $true
$cause = 'Median of ' + $median
$threshold = $medianAbove
}
elseif( $modeAbove -ge 0 -and $mode -gt $modeAbove )
{
$alert = $true
$cause = 'Mode of ' + $mode
$threshold = $modeAbove
}
elseif( $slowestAbove -ge 0 -and $slowest -gt $slowestAbove )
{
$alert = $true
$cause = 'Slowest of ' + $slowest
$threshold = $slowestAbove
}
if( ! [string]::IsNullOrEmpty( $mailserver ) -And $recipients -And ( ! $alerting -or ( $alerting -and $alert ) ) )
{
## workaround for scheduled task not passing array through properly
if( $recipients.Count -eq 1 -And $recipients[0].IndexOf(",") -ge 0 )
{
$recipients = $recipients[0] -split ","
}
if( $recipients.Count -gt 0 )
{
[hashtable]$params = @{}
if( $alert )
{
$params.Add( 'Body' , "$cause seconds exceeds threshold of $threshold for Citrix PVS target boot times since $(Get-Date -Date $startDate -Format F) on $computers" + "`n`n" + $summary )
$params.Add( 'Subject' , $alertSubject )
}
else
{
$params.Add( 'Body' , "Citrix PVS target boot times since $(Get-Date -Date $startDate -Format F) on $computers" + "`n`n" + $summary )
$params.Add( 'Subject' , $subject )
}
if( ! [string]::IsNullOrEmpty( $output ) )
{
$params.Add( 'Attachments' , $output )
}
Send-MailMessage -SmtpServer $mailserver -To $recipients -From $from -UseSsl:$useSSL @params
}
}
if( $gridView )
{
$events | Out-GridView -Title $subject
}
if( $chartView )
{
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization
if( $chartType -lt 0 )
{
$chartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Range
}
$Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart
$chart.width = 900
$chart.Height = 600
[void]$chart.Titles.Add( ( $subject + " since $(Get-Date $startDate -Format 'G')" ) )
$ChartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
$Chart.ChartAreas.Add($ChartArea)
$ChartArea.AxisY.Title = "Boot time (seconds)"
ForEach( $computer in $computers )
{
[void]$Chart.Series.Add($computer)
$Chart.Series[$computer].ChartType = $chartType
$legend = New-Object system.Windows.Forms.DataVisualization.Charting.Legend
$legend.name = $computer
$Chart.Legends.Add($legend)
$Chart.Series[$computer].ToolTip = $computer
$events | Where-Object { $_.Server -eq $computer } | ForEach-Object `
{
$null = $Chart.Series[$computer].Points.AddXY( $_.TimeCreated , $_.BootTime )
}
}
$AnchorAll = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right -bor
[System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
$Form = New-Object Windows.Forms.Form
$Form.Width = $chart.Width
$Form.Height = $chart.Height + 50
$Form.controls.add($Chart)
$Chart.Anchor = $AnchorAll
$Form.Add_Shown({$Form.Activate()})
[void]$Form.ShowDialog()
}
}
else
{
Write-Output "Found no events"
}