· 5 years ago · Aug 10, 2020, 05:12 PM
1#Requires -Version 5
2
3######################################################################################################################################
4
5Param($ConfigurationFile = "$PSScriptRoot/10.15.12.1.xml")
6
7Set-StrictMode -Version 1.0
8
9#Script version
10$Global:ScriptVersion = "5.1.9"
11
12#Variable for storing handled errors
13$Global:LoggedErrors = @()
14
15#Variable used to calculate the time used to generate the report.
16$StartTime = Get-Date
17
18#No BOM Encoding in the log file
19$Global:Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
20
21################################################################################################################################################
22#
23# Load the configration file
24#
25################################################################################################################################################
26
27#Check if the configuration file exists
28if(Test-Path $ConfigurationFile){
29 #Read the file as xml
30 [xml]$Global:Bigipreportconfig = Get-Content $ConfigurationFile
31
32 #Verify that the file was succssfully loaded, otherwise exit
33 if($?){
34 $Outputlevel = $Global:Bigipreportconfig.Settings.Outputlevel
35 if($Outputlevel -eq "Verbose"){
36 "Successfully loaded the config file: $ConfigurationFile"
37 }
38 } else {
39 Write-Error "Can't read the config file: $ConfigurationFile, or config file corrupt. Aborting."
40 Exit
41 }
42} else {
43 Write-Error "Failed to load config file $ConfigurationFile from $PSScriptRoot. Aborting."
44 Exit
45}
46
47################################################################################################################################################
48#
49# Logs to console and file function
50#
51################################################################################################################################################
52
53Function log {
54 Param ([string]$LogType, [string]$Message)
55
56 #Initiate the log header with date and time
57 $CurrentTime = $(Get-Date -UFormat "%Y-%m-%d %H:%M:%S ")
58 $LogHeader = $CurrentTime + $($LogType.toUpper()) + ' '
59
60 if($LogType -eq "error"){
61 $Global:LoggedErrors += $Message
62 }
63
64 if($Global:Bigipreportconfig.Settings.LogSettings.Enabled -eq $true){
65 $LogFilePath = $Global:Bigipreportconfig.Settings.LogSettings.LogFilePath
66 $LogLevel = $Global:Bigipreportconfig.Settings.LogSettings.LogLevel
67
68 switch($Logtype) {
69 "error" { [System.IO.File]::AppendAllText($LogFilePath, "$LogHeader$Message`n", $Global:Utf8NoBomEncoding) }
70 "warning" { [System.IO.File]::AppendAllText($LogFilePath, "$LogHeader$Message`n", $Global:Utf8NoBomEncoding) }
71 "info" { if($LogLevel -eq "Verbose"){ [System.IO.File]::AppendAllText($LogFilePath, "$LogHeader$Message`n", $Global:Utf8NoBomEncoding) } }
72 "success" { if($LogLevel -eq "Verbose"){ [System.IO.File]::AppendAllText($LogFilePath, "$LogHeader$Message`n", $Global:Utf8NoBomEncoding) } }
73 "verbose" { if($LogLevel -eq "Verbose"){ [System.IO.File]::AppendAllText($LogFilePath, "$LogHeader$Message`n", $Global:Utf8NoBomEncoding) } }
74 default { if($LogLevel -eq "Verbose"){ [System.IO.File]::AppendAllText($LogFilePath, "$LogHeader$Message`n", $Global:Utf8NoBomEncoding) } }
75 }
76 }
77
78 $ConsoleHeader = $CurrentTime
79
80 switch($logtype) {
81 "error" { Write-Host $("$ConsoleHeader$Message") -ForegroundColor "Red" }
82 "warning" { Write-Host $("$ConsoleHeader$Message") -ForegroundColor "Yellow" }
83 "info" { if($OutputLevel -eq "Verbose"){ Write-Host $("$ConsoleHeader$Message") -ForegroundColor "Gray" } }
84 "success" { if($OutputLevel -eq "Verbose"){ Write-Host $("$ConsoleHeader$Message") -ForegroundColor "Green" } }
85 "verbose" { if($OutputLevel -eq "Verbose"){ Write-Host "$ConsoleHeader$Message" } }
86 default { if($OutputLevel -eq "Verbose"){ Write-Host "$ConsoleHeader$Message" } }
87 }
88}
89
90
91#Enable case sensitive dictionaries
92function c@ {
93 New-Object Collections.Hashtable ([StringComparer]::CurrentCulture)
94}
95
96################################################################################################################################################
97#
98# Function to send an error report if error reporting is configured
99#
100################################################################################################################################################
101Function Send-Errors {
102 #Check for errors when executing the script and send them
103 If($Error.Count -gt 0 -or $Global:LoggedErrors -gt 0){
104 log verbose "There were errors while generating the report"
105
106 if($Global:Bigipreportconfig.Settings.ErrorReporting.Enabled -eq $true){
107 $Errorsummary = @"
108<html>
109 <head>
110 <style type="text/css">
111 .errortable {
112 margin:0px;padding:0px;
113 box-shadow: 10px 10px 5px #888888;
114 border-collapse: collapse;
115 font-family:Ebrima;
116 }
117 .errortable table{
118 border-collapse: collapse;
119 border-spacing: 0;
120 height:100%;
121 margin:0px;
122 padding:0px;
123 border:1px solid #000000;
124 }
125 .errortable tr:nth-child(odd){
126 background-color:#ffffff;
127 border-collapse: collapse;
128 }
129 .oddrow{
130 background-color:#d3e9ff;
131 }
132 .errortable td{
133 vertical-align:middle;
134 border:1px solid #000000;
135 border-collapse: collapse;
136 text-align:left;
137 padding:7px;
138 font-size:12px;
139 }
140 .headerrow {
141 background-color:#024a91;
142 border:0px solid #000000;
143 border-collapse: collapse;
144 text-align:center;
145 font-size:14px;
146 font-weight:bold;
147 color:#ffffff;
148 }
149 .error {
150 color:red;
151 }
152 </style>
153
154 </head>
155 <body>
156"@
157
158 if($Global:LoggedErrors.Count -gt 0){
159 $Errorsummary += "<h4>The following handled errors was thrown during the execution</h4>"
160
161 Foreach($HandledError in $Global:LoggedErrors){
162 $Errorsummary += "<font class=""error"">" + $HandledError + "</font><br>"
163 }
164 }
165
166 if($Error.Count -gt 0){
167 $Errorsummary += "<h4>The following exceptions was thrown during script execution</h4>"
168
169 $Errorsummary += "<table class=""errortable""><thead><tr class=""headerrow""><th>Category</th><th>Linenumber</th><th>Line</th><th>Stacktrace</th></tr></thead><tbody>"
170
171 Foreach($erroritem in $error){
172 $Category = $Erroritem.Categoryinfo.Reason
173 $StackTrace = $Erroritem.ScriptStackTrace
174 $LineNumber = $Erroritem.InvocationInfo.ScriptLineNumber
175 $PositionMessage = $ErrorItem.InvocationInfo.PositionMessage
176
177 $Errorsummary += "<tr><td>$Category</td><td>$Linenumber</td><td>$PositionMessage</td><td>$Stacktrace</td></tr>"
178 }
179
180 $Errorsummary += "</tbody></table></body></html>"
181 }
182 log verbose "Sending report"
183 $Subject = "$(Get-Date -format d): BigIP Report generation encountered errors"
184 $Body = "$errorsummary"
185
186 Foreach($Recipient in $Global:Bigipreportconfig.Settings.ErrorReporting.Recipients.Recipient){
187 send-MailMessage -SmtpServer $Global:Bigipreportconfig.Settings.ErrorReporting.SMTPServer -To $Recipient -From $Global:Bigipreportconfig.Settings.ErrorReporting.Sender -Subject $Subject -Body $Body -BodyAsHtml
188 }
189 } else {
190 log error "No error mail reporting enabled/configured"
191 }
192 }
193}
194
195log verbose "Configuring the console window"
196
197#Make the console larger and increase the buffer
198$PShost = Get-Host
199$PSWindow = $PShost.ui.rawui
200$PSWindowSize = $PSWindow.buffersize
201#$PSWindowSize.height = 3000
202#$PSWindowSize.width = [math]::floor([decimal]$PSWindow.MaxPhysicalWindowSize.width)-5
203#$PSwindow.buffersize = $PSWindowSize
204$PSWindowSize = $PSWindow.windowsize
205#$PSWindowSize.height = 50
206#$PSWindowSize.width = $PSWindowSize.width = [math]::floor([decimal]$PSWindow.MaxPhysicalWindowSize.width)-5
207#$PSWindow.windowsize = $PSWindowSize
208
209################################################################################################################################################
210#
211# Pre-execution checks
212#
213################################################################################################################################################
214
215log verbose "Pre-execution checks"
216
217$SaneConfig = $true
218
219if($Global:Bigipreportconfig.Settings.Credentials.Username -eq $null -or $Global:Bigipreportconfig.Credentials.Username -eq ""){
220 log error "No username configured"
221 $SaneConfig = $false
222}
223
224if($Global:Bigipreportconfig.Settings.Credentials.Password -eq $null -or $Global:Bigipreportconfig.Credentials.Password -eq ""){
225 log error "No password configured"
226 $SaneConfig = $false
227}
228
229if($Global:Bigipreportconfig.Settings.DeviceGroups.DeviceGroup -eq $null -or $Global:Bigipreportconfig.Settings.DeviceGroups.DeviceGroup.Device.Count -eq 0 ){
230 log error "No load balancers configured"
231 $SaneConfig = $false
232}
233
234if($Global:Bigipreportconfig.Settings.DefaultDocument -eq $null -or $Global:Bigipreportconfig.Settings.DefaultDocument-eq ""){
235 log error "No default document cofigured"
236 $SaneConfig = $false
237}
238
239if($Global:Bigipreportconfig.Settings.LogSettings -eq $null -or $Global:Bigipreportconfig.Settings.LogSettings.Enabled -eq $null ){
240 log error "Mandatory fields from the LogSettings section has been removed"
241 $SaneConfig = $false
242}
243
244if($Global:Bigipreportconfig.Settings.LogSettings.Enabled -eq "true" ){
245 if($Global:Bigipreportconfig.Settings.LogSettings.LogFilePath -eq $null -or $Global:Bigipreportconfig.Settings.LogSettings.LogLevel -eq $null -or $Global:Bigipreportconfig.Settings.LogSettings.MaximumLines -eq $null) {
246 log error "Logging has been enabled but all logging fields has not been configured"
247 $SaneConfig = $false
248 }
249}
250
251if($Global:Bigipreportconfig.Settings.Outputlevel -eq $null -or $Global:Bigipreportconfig.Settings.Outputlevel -eq ""){
252 log error "No Outputlevel configured"
253 $SaneConfig = $false
254}
255
256Foreach($Share in $Global:Bigipreportconfig.Settings.Shares.Share){
257 log verbose "Mounting $($Share.Path)"
258
259 & net use $($Share.Path) /user:$($Share.Username) $($Share.Password) | Out-Null
260
261 if($?){
262 log success "Share $($Share.Path) was mounted successfully"
263 } else {
264 log error "Share $($Share.Path) could not be mounted"
265 $SaneConfig = $false
266 }
267}
268
269if($Global:Bigipreportconfig.Settings.iRules -eq $null -or $Global:Bigipreportconfig.Settings.iRules.Enabled -eq $null -or $Global:Bigipreportconfig.Settings.iRules.ShowiRuleLinks -eq $null){
270 log error "Missing options in the global iRule section defined in the configuration file. Old config version of the configuration file?"
271 $SaneConfig = $false
272}
273
274if($Global:Bigipreportconfig.Settings.iRules.ShowDataGroupLinks -eq $null){
275 log error "Missing options for showing data group links in the global irules section defined in the configuration file. Old config version of the configuration file?"
276 $SaneConfig = $false
277}
278
279if($Global:Bigipreportconfig.Settings.iRules.Enabled -eq $true -and $Global:Bigipreportconfig.Settings.iRules.ShowiRuleLinks -eq $false -and $Global:Bigipreportconfig.Settings.iRules.ShowDataGroupLinks -eq $true){
280 log error "You can't show data group links without showing irules in the current version."
281 $SaneConfig = $false
282}
283
284if($Global:Bigipreportconfig.Settings.RealTimeMemberStates -eq $null){
285 log error "Real time member states is missing from the configuration file. Update the the latest version of the file and try again."
286 $SaneConfig = $false
287}
288
289if($Global:Bigipreportconfig.Settings.ReportRoot -eq $null -or $Global:Bigipreportconfig.Settings.ReportRoot -eq ""){
290 log error "No report root configured"
291 $SaneConfig = $false
292} else {
293 #Make sure the site root ends with / or \
294 if(-not $Global:bigipreportconfig.Settings.ReportRoot.endswith("/") -and -not $Global:bigipreportconfig.Settings.ReportRoot.endswith("\")){
295 $Global:bigipreportconfig.Settings.ReportRoot += "/"
296 }
297
298 if(-not (Test-Path $Global:Bigipreportconfig.Settings.ReportRoot)){
299 log error "Can't access the site root $($Global:Bigipreportconfig.Settings.ReportRoot)"
300 $SaneConfig = $false
301 } else {
302 if(-not (Test-Path $($Global:Bigipreportconfig.Settings.ReportRoot + "json"))){
303 log error "The folder $($Global:Bigipreportconfig.Settings.ReportRoot + "json") does not exist in the report root directory. Did you forget to copy the html files from the zip file?"
304 $SaneConfig = $false
305 } elseif ( (Get-ChildItem -path $($Global:Bigipreportconfig.Settings.ReportRoot + "json")).count -eq 0){
306 log error "The folder $($Global:Bigipreportconfig.Settings.ReportRoot + "json") does not contain any files. Did you accidentally delete some files?"
307 $SaneConfig = $false
308 }
309
310 if(-not (Test-Path $($Global:Bigipreportconfig.Settings.ReportRoot + "js"))){
311 log error "The folder $($Global:Bigipreportconfig.Settings.ReportRoot + "js") does not exist in the report root directory. Did you forget to copy the html files from the zip file?"
312 $SaneConfig = $false
313 } elseif ( (Get-ChildItem -path $($Global:Bigipreportconfig.Settings.ReportRoot + "js")).count -eq 0){
314 log error "The folder $($Global:Bigipreportconfig.Settings.ReportRoot + "js") does not contain any files. Did you accidentally delete some files?"
315 $SaneConfig = $false
316 }
317
318 if(-not (Test-Path $($Global:Bigipreportconfig.Settings.ReportRoot + "images"))){
319 log error "The folder $($Global:Bigipreportconfig.Settings.ReportRoot + "images") does not exist in the report root directory. Did you forget to copy the html files from the zip file?"
320 $SaneConfig = $false
321 } elseif ( (Get-ChildItem -path $($Global:Bigipreportconfig.Settings.ReportRoot + "images")).count -eq 0){
322 log error "The folder $($Global:Bigipreportconfig.Settings.ReportRoot + "images") does not contain any files. Did you accidentally delete some files?"
323 $SaneConfig = $false
324 }
325
326 if(-not (Test-Path $($Global:Bigipreportconfig.Settings.ReportRoot + "css"))){
327 log error "The folder $($Global:Bigipreportconfig.Settings.ReportRoot + "css") does not exist in the report root directory. Did you forget to copy the html files from the zip file?"
328 $SaneConfig = $false
329 } elseif ( (Get-ChildItem -path $($Global:Bigipreportconfig.Settings.ReportRoot + "css")).count -eq 0){
330 log error "The folder $($Global:Bigipreportconfig.Settings.ReportRoot + "css") does not contain any files. Did you accidentally delete some files?"
331 $SaneConfig = $false
332 }
333 }
334}
335
336Foreach($DeviceGroup in $Global:Bigipreportconfig.Settings.DeviceGroups.DeviceGroup){
337 If ($DeviceGroup.name -eq $null -or $DeviceGroup.name -eq "") {
338 log error "A device group does not have any name. Please check the latest version of the configuration file."
339 $SaneConfig = $false
340 }
341
342 If ($DeviceGroup.Device -eq $null -or ($DeviceGroup.Device | Where-Object { $_ -ne "" } ).Count -eq 0) {
343 log error "A device group does not have any devices, please re-check your configuration"
344 $SaneConfig = $false
345 }
346}
347
348#Initialize iControlSnapin
349if(Get-PSSnapin -Registered | Where-Object { $_.Description.contains("iControl") }){
350 $SnapInInfo = Get-PSSnapin -Registered | Where-Object { $_.Description.contains("iControl") }
351
352 if($SnapInInfo.Version.Major -lt 13 -or ($SnapInInfo.Version.Major -eq 13 -and $SnapInInfo.Version.Minor -lt 1) ){
353 log error "The detected iControl SnapIn running on version $([string]$SnapInInfo.Version.Major + "." + [string]$SnapInInfo.Version.Minor) while the one required by this script is 13.1"
354 log error "Follow the steps to upgrade: https://loadbalancing.se/bigip-report/#Upgrading_the_iControl_Snap-in"
355 log error "If you have any issues, please report is in the BigIPReport thread on Devcentral"
356 $SaneConfig = $false
357 } else {
358 Add-PSSnapIn iControlSnapIn
359 if($?){
360 log success "Loaded F5 iControl snapin"
361 } else {
362 log error "Failed to load F5 iControl, aborting"
363 $SaneConfig = $false
364 }
365 }
366} else {
367 log error "iControl Snapin could not be found, aborting"
368 $SaneConfig = $false
369}
370
371
372if(-not $SaneConfig){
373 log verbose "There were errors during the config file sanity check"
374
375 if($Global:Bigipreportconfig.Settings.ErrorReporting.Enabled -eq $true){
376 log verbose "Attempting to send an error report via mail"
377 Send-Errors
378 }
379
380 log verbose "Exiting"
381 Exit
382} else {
383 log success "Pre execution checks was successful"
384}
385
386################################################################################################################################################
387#
388# Pre-execution checks
389#
390################################################################################################################################################
391
392#Declaring variables
393
394#Variables used for storing report data
395$Global:NATdict = c@{}
396
397$Global:ReportObjects = c@{};
398$Global:DeviceGroups = @();
399
400#Build the path to the default document
401$Global:reportpath = $Global:bigipreportconfig.Settings.ReportRoot + $Global:bigipreportconfig.Settings.Defaultdocument
402
403#Build the json object paths
404$Global:poolsjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/pools.json"
405$Global:monitorsjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/monitors.json"
406$Global:virtualserversjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/virtualservers.json"
407$Global:irulesjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/irules.json"
408$Global:datagroupjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/datagroups.json"
409$Global:devicegroupsjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/devicegroups.json"
410$Global:loadbalancersjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/loadbalancers.json"
411$Global:certificatesjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/certificates.json"
412$Global:loggederrorsjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/loggederrors.json"
413$Global:asmpoliciesjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/asmpolicies.json"
414$Global:natjsonpath = $Global:bigipreportconfig.Settings.ReportRoot + "json.hyd/nat.json"
415
416#Create types used to store the data gathered from the load balancers
417Add-Type @'
418
419 using System.Collections;
420 public class VirtualServer
421 {
422 public string name;
423 public string description;
424 public string ip;
425 public string port;
426 public string defaultpool;
427 public string[] sslprofileclient;
428 public string[] sslprofileserver;
429 public string compressionprofile;
430 public string persistence;
431 public string[] irules;
432 public string[] pools;
433 public string[] vlans;
434 public string trafficgroup;
435 public string vlanstate;
436 public string sourcexlatetype;
437 public string sourcexlatepool;
438 public string[] asmPolicies;
439 public string availability;
440 public string enabled;
441 public string currentconnections;
442 public string maximumconnections;
443 public string cpuavg5sec;
444 public string cpuavg1min;
445 public string cpuavg5min;
446 public string loadbalancer;
447 }
448
449 public class Member {
450 public string name;
451 public string ip;
452 public string port;
453 public string availability;
454 public string enabled;
455 public string status;
456 public long priority;
457 public string currentconnections;
458 public string maximumconnections;
459 }
460
461 public class Pool {
462 public string name;
463 public string description;
464 public string[] monitors;
465 public Member[] members;
466 public string loadbalancingmethod;
467 public string actiononservicedown;
468 public string allownat;
469 public string allowsnat;
470 public bool orphaned;
471 public string loadbalancer;
472 public string availability;
473 public string enabled;
474 public string status;
475 }
476
477 public class iRule {
478 public string name;
479 public string[] pools;
480 public string[] datagroups;
481 public string definition;
482 public string loadbalancer;
483 }
484
485 public class Node {
486 public string ip;
487 public string name;
488 public string description;
489 public string loadbalancer;
490 }
491
492 public class Monitor {
493 public string name;
494 public string type;
495 public string sendstring;
496 public string receivestring;
497 public string loadbalancer;
498 public string interval;
499 public string timeout;
500 }
501
502 public class Datagroup {
503 public string name;
504 public string type;
505 public Hashtable data;
506 public string[] pools;
507 public string loadbalancer;
508 }
509
510 public class PoolStatusVip {
511 public string url;
512 public string working;
513 public string state;
514 }
515
516 public class DeviceGroup {
517 public string name;
518 public string[] ips;
519 }
520
521 public class Loadbalancer {
522 public string name;
523 public string ip;
524 public string version;
525 public string build;
526 public string baseBuild;
527 public string model;
528 public string category;
529 public string serial;
530 public bool active;
531 public bool isonlydevice;
532 public string color;
533 public Hashtable modules;
534 public PoolStatusVip statusvip;
535 public bool success = true;
536 }
537
538 public class ASMPolicy {
539 public string name;
540 public string learningMode;
541 public string enforcementMode;
542 public string[] virtualServers;
543 public string loadbalancer;
544 }
545
546 public class CertificateDetails {
547 public string commonName;
548 public string countryName;
549 public string stateName;
550 public string localityName;
551 public string organizationName;
552 public string divisionName;
553 }
554
555 public class Certificate {
556 public string fileName;
557 public long expirationDate;
558 public CertificateDetails subject;
559 public CertificateDetails issuer;
560 public string loadbalancer;
561 }
562
563'@
564
565$Global:ModuleToShort = @{
566 "TMOS_MODULE_ASM" = "ASM";
567 "TMOS_MODULE_SAM" = "APM";
568 "TMOS_MODULE_WAM" = "WAM";
569 "TMOS_MODULE_WOM" = "WOM";
570 "TMOS_MODULE_LC" = "LC";
571 "TMOS_MODULE_LTM" = "LTM";
572 "TMOS_MODULE_GTM" = "GTM";
573 "TMOS_MODULE_WOML" = "WOML";
574 "TMOS_MODULE_APML" = "APML";
575 "TMOS_MODULE_EM" = "EM";
576 "TMOS_MODULE_VCMP" = "VCMP";
577 "TMOS_MODULE_UNKNOWN" = "UNKNOWN";
578 "TMOS_MODULE_TMOS" = "TMOS";
579 "TMOS_MODULE_HOST" = "HOST";
580 "TMOS_MODULE_UI" = "UI";
581 "TMOS_MODULE_MONITORS" = "MONITORS";
582 "TMOS_MODULE_AVR" = "AVR";
583 "TMOS_MODULE_ILX" = "ILX";
584 }
585
586$Global:ModuleToDescription = @{
587 "ASM" = "The Application Security Module.";
588 "APM" = "The Access Policy Module.";
589 "WAM" = "The Web Accelerator Module.";
590 "WOM" = "The WAN Optimization Module.";
591 "LC" = "The Link Controller Module.";
592 "LTM" = "The Local Traffic Manager Module.";
593 "GTM" = "The Global Traffic Manager Module.";
594 "UNKNOWN" = "The module is unknown (or unsupported by iControl).";
595 "WOML" = "The WAN Optimization Module (Lite).";
596 "APML" = "The Access Policy Module (Lite).";
597 "EM" = "The Enterprise Manager Module.";
598 "VCMP" = "The Virtual Clustered MultiProcessing Module.";
599 "TMOS" = "The Traffic Management part of the Core OS.";
600 "HOST" = "The non-Traffic Management = non-GUI part of the Core OS.";
601 "UI" = "The GUI part of the Core OS.";
602 "MONITORS" = "Represents the external monitors - used for stats only.";
603 "AVR" = "The Application Visualization and Reporting Module";
604 "ILX" = "iRulesLX"
605}
606
607$Global:LBMethodToString = @{
608 "LB_METHOD_ROUND_ROBIN" = "Round Robin";
609 "LB_METHOD_RATIO_Member" = "Ratio (Member)";
610 "LB_METHOD_LEAST_CONNECTION_Member" = "Least Connections (Member)";
611 "LB_METHOD_OBSERVED_Member" = "Observed (Member)";
612 "LB_METHOD_PREDICTIVE_Member" = "Predictive (Member)";
613 "LB_METHOD_RATIO_NODE_ADDRESS" = "Ratio (Node)";
614 "LB_METHOD_LEAST_CONNECTION_NODE_ADDRESS" = "Least Connection (Node)";
615 "LB_METHOD_FASTEST_NODE_ADDRESS" = "Fastest (node)";
616 "LB_METHOD_OBSERVED_NODE_ADDRESS" = "Observed (node)";
617 "LB_METHOD_PREDICTIVE_NODE_ADDRESS" = "Predictive (node)";
618 "LB_METHOD_DYNAMIC_RATIO" = "Dynamic Ratio";
619 "LB_METHOD_FASTEST_APP_RESPONSE" = "Fastest App Response";
620 "LB_METHOD_LEAST_SESSIONS" = "Least sessions";
621 "LB_METHOD_DYNAMIC_RATIO_Member" = "Dynamic Ratio (Member)";
622 "LB_METHOD_L3_ADDR" = "L3 Address";
623 "LB_METHOD_UNKNOWN" = "Unknown";
624 "LB_METHOD_WEIGHTED_LEAST_CONNECTION_Member" = "Weighted Least Connection (Member)";
625 "LB_METHOD_WEIGHTED_LEAST_CONNECTION_NODE_ADDRESS" = "Weighted Least Connection (Node)";
626 "LB_METHOD_RATIO_SESSION" = "Ratio Sessions";
627 "LB_METHOD_RATIO_LEAST_CONNECTION_Member" = "Ratio Least Connections (Member)";
628 "LB_METHOD_RATIO_LEAST_CONNECTION_NODE_ADDRESS" = "Least Connections (Node)";
629}
630
631$Global:ActionOnPoolFailureToString = @{
632 "SERVICE_DOWN_ACTION_NONE" = "None";
633 "SERVICE_DOWN_ACTION_RESET" = "Reject";
634 "SERVICE_DOWN_ACTION_DROP" = "Drop";
635 "SERVICE_DOWN_ACTION_RESELECT" = "Reselect";
636}
637
638$Global:StateToString = @{
639 "STATE_ENABLED" = "Yes";
640 "STATE_DISABLED" = "No";
641}
642
643
644#Enable of disable the use of TLS1.2
645if($Global:Bigipreportconfig.Settings.UseTLS12 -eq $true){
646 log verbose "Enabling TLS1.2"
647 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
648}
649
650#Make sure that the text is in UTF8
651[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
652
653#If configured, read the NAT rules from the specified NAT File
654
655if($Global:Bigipreportconfig.Settings.NATFilePath -ne ""){
656 log verbose "NAT File has been configured"
657
658 if(Test-Path -Path $Global:Bigipreportconfig.Settings.NATFilePath){
659 $NATContent = Get-Content $Global:Bigipreportconfig.Settings.NATFilePath
660
661 $NATContent | ForEach-Object {
662 $ArrLine = $_.split("=").Trim()
663 if($ArrLine.Count -eq 2){
664 $Global:NATdict[$arrLine[1]] = $arrLine[0]
665 } else {
666 log error "Malformed NAT file content detected: Check $_"
667 }
668 }
669
670 if($NATdict.count -gt 0){
671 log success "Loaded $($NATdict.count) NAT entries"
672 } else {
673 log error "No NAT entries loaded"
674 }
675 } else {
676 log error "NAT file could not be found in location $($Global:Bigipreportconfig.Settings.NATFilePath)"
677 }
678}
679
680#Region function Get-LTMInformation
681
682#Function used to gather data from the load balancers
683function Get-LTMInformation {
684 Param(
685 $F5,
686 $LoadBalancerObjects
687 )
688
689 $VersionInfo = $F5.SystemSystemInfo.get_product_information()
690
691 #Set some variables to make the code nicer to read
692 $LoadBalancerName = $LoadBalancerObjects.LoadBalancer.name
693 $LoadBalancerIP = $LoadBalancerObjects.LoadBalancer.ip
694
695 #Regexp for parsing monitors from iRule definitions
696 [regex]$Poolregexp = "pool\s+([a-zA-Z0-9_\-\./]+)"
697
698 $F5.SystemSession.set_active_folder("/");
699 $F5.SystemSession.set_recursive_query_state("STATE_ENABLED");
700
701 $MajorVersion = $LoadBalancerObjects.LoadBalancer.version.Split(".")[0]
702 $Minorversion = $LoadBalancerObjects.LoadBalancer.version.Split(".")[1]
703
704 $LoadBalancerObjects.ASMPolicies = c@{}
705
706 If($MajorVersion -gt 11){
707 #Check if ASM is enabled
708 if($ModuleDict.Keys -contains "ASM"){
709 $ErrorActionPreference = "SilentlyContinue"
710
711 Try {
712 log info "Version 12+ with ASM. Getting REST token for ASM information"
713 $AuthToken = Get-AuthToken -Loadbalancer $LoadBalancerIP
714
715 log verbose "Getting ASM Policy information"
716
717 $Headers = @{ "X-F5-Auth-Token" = $AuthToken; }
718
719 $Response = Invoke-RestMethod -Method "GET" -Headers $Headers -Uri "https://$LoadBalancerIP/mgmt/tm/asm/policies"
720 $Policies = $Response.items
721
722 Foreach($Policy in $Policies){
723 $ObjTempPolicy = New-Object -Type ASMPolicy
724
725 $ObjTempPolicy.name = $Policy.fullPath
726 $ObjTempPolicy.enforcementMode = $Policy.enforcementMode
727 $ObjTempPolicy.learningMode = $Policy.learningMode
728 $ObjTempPolicy.virtualServers = $Policy.virtualServers
729 $ObjTempPolicy.loadbalancer = $LoadBalancerName
730
731 $LoadBalancerObjects.ASMPolicies.add($ObjTempPolicy.name, $ObjTempPolicy)
732 }
733 } Catch {
734 log error "Unable to get a valid token from $LoadbalancerName."
735 }
736
737 $ErrorActionPreference = "Continue"
738 }
739 }
740
741 #EndRegion
742
743 #Region Cache Node and monitor data
744
745 #Cache information about iRules, nodes and monitors
746
747 #Region Cache certificate information
748
749 log verbose "Caching certificates"
750
751 $LoadBalancerObjects.Certificates = c@{}
752
753 $Certificates = $F5.ManagementKeyCertificate.get_certificate_list(0)
754
755 Foreach($Certificate in $Certificates){
756 $ObjSubject = New-Object -TypeName "CertificateDetails"
757
758 $ObjSubject.commonName = $Certificate.certificate.subject.common_name
759 $ObjSubject.countryName = $Certificate.certificate.subject.country_name
760 $ObjSubject.stateName = $Certificate.certificate.subject.state_name
761 $ObjSubject.localityName = $Certificate.certificate.subject.locality_name
762 $ObjSubject.organizationName = $Certificate.certificate.subject.organization_name
763 $ObjSubject.divisionName = $Certificate.certificate.subject.division_name
764
765 $ObjIssuer = New-Object -TypeName "CertificateDetails"
766
767 $ObjIssuer.commonName = $Certificate.certificate.issuer.common_name
768 $ObjIssuer.countryName = $Certificate.certificate.issuer.country_name
769 $ObjIssuer.stateName = $Certificate.certificate.issuer.state_name
770 $ObjIssuer.localityName = $Certificate.certificate.issuer.locality_name
771 $ObjIssuer.organizationName = $Certificate.certificate.issuer.organization_name
772 $ObjIssuer.divisionName = $Certificate.certificate.issuer.division_name
773
774 $ObjCertificate = New-Object -TypeName "Certificate"
775
776 $ObjCertificate.fileName = $Certificate.file_name
777 $ObjCertificate.expirationDate = $Certificate.certificate.expiration_date
778 $ObjCertificate.subject = $ObjSubject
779 $ObjCertificate.issuer = $ObjIssuer
780 $ObjCertificate.loadbalancer = $LoadbalancerName
781
782 $LoadBalancerObjects.Certificates.add($ObjCertificate.fileName, $ObjCertificate)
783 }
784
785 #Region Cache node data
786
787 $LoadBalancerObjects.Nodes = c@{}
788
789 [array]$NodeNames = $F5.LocalLBNodeAddressV2.get_list()
790 [array]$NodeAddresses = $F5.LocalLBNodeAddressV2.get_address($NodeNames)
791 [array]$NodeDescriptions = $F5.LocalLBNodeAddressV2.get_description($NodeNames)
792
793
794 for($i=0;$i -lt ($NodeAddresses.Count);$i++){
795 $ObjTempNode = New-Object Node
796
797 $ObjTempNode.ip = [string]$NodeAddresses[$i]
798 $ObjTempNode.name = [string]$NodeNames[$i]
799 $ObjTempNode.description = [string]$NodeDescriptions[$i]
800 $ObjTempNode.loadbalancer = $LoadBalancerName
801
802 if($ObjTempNode.name -eq ""){
803 $ObjTempNode.name = "Unnamed"
804 }
805
806 $LoadBalancerObjects.Nodes.add($ObjTempNode.name, $ObjTempNode)
807 }
808
809 #EndRegion
810
811 #Region Caching monitor data
812
813 $LoadBalancerObjects.Monitors = c@{}
814
815 log verbose "Caching monitors"
816
817 [array]$MonitorList = $F5.LocalLBMonitor.get_template_list()
818
819 #Save the HTTP monitors separately since they have different properties
820 [array]$HttpMonitors = $MonitorList | Where-Object { $_.template_type -eq "TTYPE_HTTP" -or $_.template_type -eq "TTYPE_HTTPS" }
821
822 if($HttpMonitors.Count -gt 0){
823 [array]$HttpmonitorsSendstrings = $F5.LocalLBMonitor.get_template_string_property($HttpMonitors.template_name, $($HttpMonitors | ForEach-Object { 1 }))
824 [array]$HttpmonitorsReceiveStrings = $F5.LocalLBMonitor.get_template_string_property($HttpMonitors.template_name, $($HttpMonitors | ForEach-Object { 3 }))
825 [array]$HttpmonitorsIntervals = $F5.LocalLBMonitor.get_template_integer_property($HttpMonitors.template_name,$($HttpMonitors | ForEach-Object { 1 }))
826 [array]$HttpmonitorsTimeOuts = $F5.LocalLBMonitor.get_template_integer_property($HttpMonitors.template_name,$($HttpMonitors | ForEach-Object { 2 }))
827 }
828
829 #Save the monitors which has interval and timeout properties
830 [array]$OtherMonitors = $MonitorList | Where-Object { @("TTYPE_ICMP", "TTYPE_GATEWAY_ICMP", "TTYPE_REAL_SERVER", "TTYPE_SNMP_DCA", "TTYPE_TCP_HALF_OPEN", "TTYPE_TCP", "TTYPE_UDP") -contains $_.template_type }
831
832 if($OtherMonitors.Count -gt 0){
833 [array]$OtherMonitorsIntervals = $F5.LocalLBMonitor.get_template_integer_property($OtherMonitors.template_name,$($OtherMonitors | ForEach-Object { 1 }))
834 [array]$OtherMonitorsTimeouts = $F5.LocalLBMonitor.get_template_integer_property($OtherMonitors.template_name,$($OtherMonitors | ForEach-Object { 2 }))
835 }
836
837 #Save the rest here
838 $NonCompatibleMonitors = $MonitorList | Where-Object { @("TTYPE_ICMP", "TTYPE_GATEWAY_ICMP", "TTYPE_REAL_SERVER", "TTYPE_SNMP_DCA", "TTYPE_TCP_HALF_OPEN", "TTYPE_TCP", "TTYPE_UDP", "TTYPE_HTTP", "TTYPE_HTTPS") -notcontains $_.template_type }
839
840 For($i = 0;$i -lt $HttpMonitors.Count;$i++){
841 $ObjTempMonitor = New-Object Monitor
842
843 $ObjTempMonitor.name = $HttpMonitors[$i].template_name
844 $ObjTempMonitor.sendstring = $HttpmonitorsSendstrings[$i].value
845 $ObjTempMonitor.receivestring = $HttpmonitorsReceiveStrings[$i].value
846 $ObjTempMonitor.interval = $HttpmonitorsIntervals[$i].value
847 $ObjTempMonitor.timeout = $HttpmonitorsTimeOuts[$i].value
848 $ObjTempMonitor.type = $HttpMonitors[$i].template_type
849
850 $ObjTempMonitor.loadbalancer = $LoadBalancerName
851
852 $LoadBalancerObjects.Monitors.add($ObjTempMonitor.name, $ObjTempMonitor)
853 }
854
855 For($i = 0;$i -lt $OtherMonitors.Count;$i++){
856 $ObjTempMonitor = New-Object Monitor
857
858 $ObjTempMonitor.name = $OtherMonitors[$i].template_name
859 $ObjTempMonitor.sendstring = "N/A"
860 $ObjTempMonitor.receivestring = "N/A"
861 $ObjTempMonitor.interval = $OtherMonitorsIntervals[$i].value
862 $ObjTempMonitor.timeout = $OtherMonitorsTimeouts[$i].value
863 $ObjTempMonitor.type = $OtherMonitors[$i].template_type
864 $ObjTempMonitor.loadbalancer = $LoadBalancerName
865
866 $LoadBalancerObjects.Monitors.add($ObjTempMonitor.name, $ObjTempMonitor)
867 }
868
869 Foreach($Monitor in $NonCompatibleMonitors){
870 $ObjTempMonitor = New-Object Monitor
871
872 $ObjTempMonitor.name = $Monitor.template_name
873 $ObjTempMonitor.sendstring = "N/A"
874 $ObjTempMonitor.receivestring = "N/A"
875 $ObjTempMonitor.interval = "N/A"
876 $ObjTempMonitor.timeout = "N/A"
877 $ObjTempMonitor.type = $Monitor.template_type
878
879 $ObjTempMonitor.loadbalancer = $LoadBalancerName
880
881 $LoadBalancerObjects.Monitors.add($ObjTempMonitor.name, $ObjTempMonitor)
882 }
883
884 #EndRegion
885
886 #Region Cache Data groups
887
888 log verbose "Caching data groups"
889
890 $LoadBalancerObjects.DataGroups = c@{}
891
892 [array]$AddressClassList = $F5.LocalLBClass.get_address_class_list()
893 [array]$AddressClassKeys = $F5.LocalLBClass.get_address_class($AddressClassList)
894 [array]$AddressClassValues = $F5.LocalLBClass.get_address_class_member_data_value($AddressClassKeys)
895 [array]$ExternalClassList = $F5.LocalLBClass.get_external_class_list_v2()
896
897 #Get address type data groups data
898 For($i = 0;$i -lt $AddressClassList.Count;$i++){
899 $ObjTempDataGroup = New-Object -Type DataGroup
900 $ObjTempDataGroup.name = $AddressClassList[$i]
901 If ($ExternalClassList -Contains $ObjTempDataGroup.name){
902 $ObjTempDataGroup.type = "External Address"
903 } else {
904 $ObjTempDataGroup.type = "Address"
905 }
906
907 $Dgdata = New-Object System.Collections.Hashtable
908
909 for($x=0;$x -lt $AddressClassKeys[$i].members.Count;$x++){
910 $Key = [string]$AddressClassKeys[$i].members[$x].Address + " " + [string]$AddressClassKeys[$i].members[$x].Netmask
911 $Value = [string]$AddressClassValues[$i][$x]
912
913 $Dgdata.add($Key, $Value)
914 }
915
916 $ObjTempDataGroup.data = $Dgdata
917 $objTempDataGroup.loadbalancer = $LoadBalancerName
918
919 $LoadBalancerObjects.DataGroups.add($ObjTempDataGroup.name, $ObjTempDataGroup)
920 }
921
922 $StringClassList = $F5.LocalLBClass.get_string_class_list()
923 $StringClassKeys = $F5.LocalLBClass.get_string_class($StringClassList)
924 $StringClassValues = $F5.LocalLBClass.get_string_class_member_data_value($StringClassKeys)
925
926 For($i = 0;$i -lt $StringClassList.Count;$i++){
927 $ObjTempDataGroup = New-Object -Type DataGroup
928 $ObjTempDataGroup.name = $StringClassList[$i]
929 If ($ExternalClassList -Contains $ObjTempDataGroup.name){
930 $ObjTempDataGroup.type = "External String"
931 } else {
932 $ObjTempDataGroup.type = "String"
933 }
934
935 $Dgdata = New-Object System.Collections.Hashtable
936
937 for($x=0;$x -lt $StringClassKeys[$i].members.Count;$x++){
938 $Key = [string]$StringClassKeys[$i].members[$x]
939 $Value = [string]$StringClassValues[$i][$x]
940
941 $Dgdata.add($Key, $Value)
942 }
943
944 $ObjTempDataGroup.data = $Dgdata
945 $ObjTempDataGroup.loadbalancer = $LoadBalancerName
946
947 $LoadBalancerObjects.DataGroups.add($ObjTempDataGroup.name, $ObjTempDataGroup)
948 }
949
950 $ValueClassList = $F5.LocalLBClass.get_value_class_list()
951 $ValueClassKeys = $F5.LocalLBClass.get_value_class($ValueClassList)
952 $ValueClassValues = $F5.LocalLBClass.get_value_class_member_data_value($ValueClassKeys)
953
954 For($i = 0;$i -lt $ValueClassList.Count;$i++){
955 $ObjTempDataGroup = New-Object -Type DataGroup
956 $ObjTempDataGroup.name = $ValueClassList[$i]
957 If ($ExternalClassList -Contains $ObjTempDataGroup.name){
958 $ObjTempDataGroup.type = "External Integer"
959 } else {
960 $ObjTempDataGroup.type = "Integer"
961 }
962
963 $Dgdata = New-Object System.Collections.Hashtable
964
965 for($x=0;$x -lt $ValueClassKeys[$i].members.Count;$x++){
966 $Key = [string]$ValueClassKeys[$i].members[$x]
967 $Value = [string]$ValueClassValues[$i][$x]
968
969 $Dgdata.add($Key, $Value)
970 }
971
972 $ObjTempDataGroup.data = $Dgdata
973 $ObjTempDataGroup.loadbalancer = $LoadBalancerName
974
975 $LoadBalancerObjects.DataGroups.add($ObjTempDataGroup.name, $ObjTempDataGroup)
976 }
977
978 #EndRegion
979 #EndRegion
980
981 #Region Caching Pool information
982
983 log verbose "Caching Pools"
984
985 $LoadBalancerObjects.Pools = c@{}
986
987 [array]$Poollist = $F5.LocalLBPool.get_list()
988 [array]$PoolStatus = $F5.LocalLBPool.get_object_status($PoolList)
989 [array]$PoolMonitors = $F5.LocalLBPool.get_monitor_association($PoolList)
990 [array]$PoolMembers = $F5.LocalLBPool.get_member_v2($PoolList)
991 [array]$PoolMemberstatuses = $F5.LocalLBPool.get_member_object_status($PoolList, $Poolmembers)
992 [array]$PoolMemberpriorities = $F5.LocalLBPool.get_member_priority($Poollist, $PoolMembers)
993 [array]$PoolLBMethods = $F5.LocalLBPool.get_lb_method($PoolList)
994 [array]$PoolActionOnServiceDown = $F5.LocalLBPool.get_action_on_service_down($PoolList)
995 [array]$PoolAllowNAT = $F5.LocalLBPool.get_allow_nat_state($PoolList)
996 [array]$PoolAllowSNAT = $F5.LocalLBPool.get_allow_snat_state($PoolList)
997 [array]$PoolMemberStatistics = $F5.LocalLBPool.get_all_member_statistics($PoolList)
998 [array]$PoolDescriptions = $F5.LocalLBPool.get_description($PoolList)
999
1000 for($i=0;$i -lt ($PoolList.Count);$i++){
1001 $ObjTempPool = New-Object -Type Pool
1002 $ObjTempPool.name = [string]$Poollist[$i]
1003
1004 $PoolMonitors[$i].monitor_rule.monitor_templates | ForEach-Object {
1005 $ObjTempPool.monitors += $_
1006 }
1007
1008 $PoolMemberStatisticsDict = Get-PoolMemberStatisticsDictionary -PoolMemberStatsObjArray $PoolMemberStatistics[$i]
1009
1010 For($x=0;$x -lt $PoolMembers[$i].count;$x++){
1011 #Create a new temporary object of the member class
1012 $ObjTempMember = New-Object Member
1013
1014 #Populate the object
1015 $ObjTempMember.Name = $PoolMembers[$i][$x].address
1016
1017 $ObjTempMember.ip = ($LoadBalancerObjects.Nodes[$ObjTempMember.Name]).ip
1018 $ObjTempMember.Port = $PoolMembers[$i][$x].port
1019 $ObjTempMember.Availability = $PoolMemberstatuses[$i][$x].availability_status
1020 $ObjTempMember.Enabled = $PoolMemberstatuses[$i][$x].enabled_status
1021 $ObjTempMember.Status = $PoolMemberstatuses[$i][$x].status_description
1022 $ObjTempMember.Priority = $PoolMemberpriorities[$i][$x]
1023
1024 Try {
1025 $Statistics = $PoolMemberStatisticsDict[$ObjTempMember.Name + ":" + [string]$ObjTempMember.port]
1026 $ObjTempMember.currentconnections = $Statistics["currentconnections"]
1027 $ObjTempMember.maximumconnections = $Statistics["maximumconnections"]
1028 } Catch {
1029 log "error" "Unable to get statistics for member $(objTempMember.Name):$(objTempMember.Port) in pool $($ObjTempPool.name)"
1030 }
1031
1032 #Add the object to a list
1033 $ObjTempPool.members += $ObjTempMember
1034 }
1035
1036 $ObjTempPool.loadbalancingmethod = $Global:LBMethodToString[[string]($PoolLBMethods[$i])]
1037 $ObjTempPool.actiononservicedown = $Global:ActionOnPoolFailureToString[[string]($PoolActionOnServiceDown[$i])]
1038 $ObjTempPool.allownat = $StateToString[[string]($PoolAllowNAT[$i])]
1039 $ObjTempPool.allowsnat = $StateToString[[string]($PoolAllowSNAT[$i])]
1040 $ObjTempPool.description = $PoolDescriptions[$i]
1041 $ObjTempPool.availability = $PoolStatus[$i].availability_status
1042 $ObjTempPool.enabled = $PoolStatus[$i].enabled_status
1043 $ObjTempPool.status = $PoolStatus[$i].status_description
1044 $ObjTempPool.loadbalancer = $LoadBalancerName
1045
1046 $LoadBalancerObjects.Pools.add($ObjTempPool.name, $ObjTempPool)
1047 }
1048
1049 #EndRegion
1050
1051 #Region Get Datagroup Pools
1052 log verbose "Detecting pools referenced by datagroups"
1053
1054 $Pools = $LoadBalancerObjects.Pools.Keys | Sort-Object -Unique
1055
1056 Foreach($DataGroup in $LoadBalancerObjects.DataGroups.Values){
1057
1058 $TempPools = @()
1059
1060 $Partition = $DataGroup.name.split("/")[1]
1061
1062 Foreach($TempPool in $DataGroup.data.Values) {
1063
1064 if(-not $TempPool.contains("/")){
1065 $TempPool = "/$Partition/$TempPool"
1066 }
1067
1068 if ($Pools -contains $TempPool) {
1069 $TempPools += $TempPool
1070 }
1071 }
1072
1073 if ($TempPools.Count -gt 0) {
1074 $LoadBalancerObjects.DataGroups[$DataGroup.name].pools = @($TempPools | Sort-Object -Unique)
1075 $LoadBalancerObjects.DataGroups[$DataGroup.name].type = "Pools"
1076 }
1077 }
1078 #EndRegion
1079
1080 #Region Cache information about irules
1081
1082 log verbose "Caching iRules"
1083
1084 $DataGroups = $LoadBalancerObjects.DataGroups.Keys | Sort-Object -Unique
1085
1086 $LoadBalancerObjects.iRules = c@{}
1087
1088 $LastPartition = ''
1089
1090 $F5.LocalLBRule.query_all_rules() | ForEach-Object {
1091 $ObjiRule = New-Object iRule
1092
1093 $ObjiRule.name = $_.rule_name
1094
1095 $Partition = $ObjiRule.name.split("/")[1]
1096
1097 if ($Partition -ne $LastPartition) {
1098 $SearchPools = $Pools -replace "/$Partition/",""
1099 $SearchDataGroups = $DataGroups -replace "/$Partition/",""
1100 }
1101
1102 $LastPartition = $Partition
1103
1104 $ObjiRule.loadbalancer = $LoadBalancerName
1105
1106 $Definition = $($_.rule_definition)
1107 $ObjiRule.definition = $Definition
1108
1109 $MatchedPools = @($SearchPools | Where-Object {$Definition -match '\b' + [regex]::Escape($_) + '\b'} | Sort-Object -Unique)
1110 $MatchedPools = $MatchedPools -replace "^([^/])","/$Partition/`$1"
1111 $ObjiRule.pools = $MatchedPools
1112
1113 $MatchedDataGroups = @($SearchDataGroups | Where-Object {$Definition -match '\b' + [regex]::Escape($_) + '\b'} | Sort-Object -Unique)
1114 $MatchedDataGroups = $MatchedDataGroups -replace "^([^/])","/$Partition/`$1"
1115 $ObjiRule.datagroups = $MatchedDataGroups
1116
1117 $LoadBalancerObjects.iRules.add($ObjiRule.name, $ObjiRule)
1118 }
1119
1120 #EndRegion
1121
1122 #Region Cache virtual address information
1123
1124 $TrafficGroupDict = c@{}
1125
1126 [array]$VirtualAddressList = $F5.LocalLBVirtualAddressV2.get_list()
1127 [array]$VirtualAddressTrafficGroups = $F5.LocalLBVirtualAddressV2.get_traffic_group($VirtualAddressList)
1128
1129 for($i=0;$i -lt ($VirtualAddressList.Count);$i++){
1130 $VirtualAddress = $VirtualAddressList[$i]
1131 $TrafficGroup = $VirtualAddressTrafficGroups[$i]
1132
1133 $TrafficGroupDict.add($VirtualAddress, $TrafficGroup)
1134 }
1135
1136 #EndRegion
1137
1138 #Region Cache Virtual Server information
1139
1140 log verbose "Caching Virtual servers"
1141
1142 $LoadBalancerObjects.VirtualServers = c@{}
1143
1144 [array]$VirtualServers = $F5.LocalLBVirtualServer.get_list()
1145 [array]$VirtualServerDestinations = $F5.LocalLBVirtualServer.get_destination($VirtualServers)
1146 [array]$VirtualServerDefaultPools = $F5.LocalLBVirtualServer.get_default_pool_name($VirtualServers)
1147 [array]$VirtualServerProfiles = $F5.LocalLBVirtualServer.get_profile($VirtualServers)
1148 [array]$VirtualServeriRules = $F5.LocalLBVirtualServer.get_rule($VirtualServers)
1149 [array]$VirtualServerPersistenceProfiles = $F5.LocalLBVirtualServer.get_persistence_profile($VirtualServers)
1150 [array]$VirtualServerVlans = $F5.LocalLBVirtualServer.get_vlan($VirtualServers);
1151 [array]$VirtualServerStates = $F5.LocalLBVirtualServer.get_object_status($VirtualServers)
1152 [array]$VirtualServerStatistics = $F5.LocalLBVirtualServer.get_statistics($VirtualServers)
1153 [array]$VirtualServerDescriptions = $F5.LocalLBVirtualServer.get_description($VirtualServers)
1154
1155 #Only supported since version 11.3
1156 Try {
1157 $VirtualServerSourceAddressTranslationTypes = $F5.LocalLBVirtualServer.get_source_address_translation_type($VirtualServers)
1158 $VirtualServerSourceAddressSnatPool = $F5.LocalLBVirtualServer.get_source_address_translation_snat_pool($VirtualServers)
1159 } Catch {
1160 log verbose "Unable to get address translationlist"
1161 }
1162
1163 for($i=0;$i -lt ($VirtualServers.Count);$i++){
1164 $VirtualServerName = $VirtualServers[$i]
1165
1166 $ObjTempVirtualServer = New-Object VirtualServer
1167
1168 #Set the name of the Virtual server
1169 $ObjTempVirtualServer.name = $VirtualServerName
1170
1171 #Set the description
1172 $ObjTempVirtualServer.description = $VirtualServerDescriptions[$i]
1173
1174 #Get the IP and port of the destination
1175 $VirtualServerDestination = $VirtualServerDestinations[$i]
1176
1177 $ObjTempVirtualServer.ip = [string]($VirtualServerDestination.Address)
1178
1179 #Set the port to "Any" if it's 0
1180 if(($VirtualServerDestination.port) -eq 0){
1181 $ObjTempVirtualServer.port = "Any"
1182 } else {
1183 $ObjTempVirtualServer.port = [string]($VirtualServerDestination.port)
1184 }
1185
1186 #Set the default pool
1187 $ObjTempVirtualServer.defaultpool = [string]$VirtualServerDefaultPools[$i]
1188
1189 #Set the ssl profile to None by default, then check if there's an SSL profile and
1190
1191 $ObjTempVirtualServer.compressionprofile = "None";
1192
1193 $VirtualServerProfiles[$i] | ForEach-Object {
1194 if([string]($_.profile_type) -eq "PROFILE_TYPE_CLIENT_SSL"){
1195 $ObjTempVirtualServer.sslprofileclient += $_.profile_name;
1196 } elseif([string]($_.profile_type) -eq "PROFILE_TYPE_SERVER_SSL"){
1197 $ObjTempVirtualServer.sslprofileserver += $_.profile_name;
1198 } elseif([string]($_.profile_type) -eq "PROFILE_TYPE_HTTPCOMPRESSION"){
1199 $ObjTempVirtualServer.compressionprofile = $_.profile_name;
1200 }
1201 }
1202
1203 if ($null -eq $ObjTempVirtualServer.sslprofileclient) {
1204 $ObjTempVirtualServer.sslprofileclient += "None";
1205 }
1206 if ($null -eq $ObjTempVirtualServer.sslprofileserver) {
1207 $ObjTempVirtualServer.sslprofileserver += "None";
1208 }
1209
1210 #Get the iRules of the Virtual server
1211 $VirtualServeriRules[$i] | Sort-Object -Property priority | ForEach-Object {
1212 $tempName = $_.rule_name
1213 $ObjTempVirtualServer.irules += [string]$tempName
1214 }
1215
1216 if([string]($ObjTempVirtualServer.irules) -eq ""){
1217 $ObjTempVirtualServer.irules = @();
1218 }
1219
1220 $ObjTempVirtualServer.loadbalancer = $LoadBalancerName
1221
1222 #Get the persistence profile of the Virtual server
1223
1224 if($VirtualServerPersistenceProfiles[$i] -ne $null){
1225 $ObjTempVirtualServer.persistence = [string]($VirtualServerPersistenceProfiles[$i].profile_name)
1226 } else {
1227 $ObjTempVirtualServer.persistence = "None"
1228 }
1229
1230 $ObjTempVirtualServer.irules | ForEach-Object {
1231 $iRule = $LoadBalancerObjects.iRules[$_]
1232
1233 if($iRule){
1234 if($iRule.pools.Count -gt 0){
1235 $ObjTempVirtualServer.pools += [array]$iRule.pools | Sort-Object -Unique
1236 }
1237 Foreach($DatagroupName in $iRule.datagroups ) {
1238 $Datagroup = $LoadBalancerObjects.DataGroups[$DatagroupName]
1239 if ($Datagroup -and $Datagroup.pools.Count -gt 0) {
1240 $ObjTempVirtualServer.pools += [array]$Datagroup.pools | Sort-Object -Unique
1241 }
1242 }
1243 }
1244 }
1245
1246 if($ObjTempVirtualServer.defaultpool -ne ""){
1247 $ObjTempVirtualServer.pools += $ObjTempVirtualServer.defaultpool
1248 }
1249
1250 $ObjTempVirtualServer.pools = $ObjTempVirtualServer.pools | Sort-Object -Unique
1251
1252 Try{
1253 $ObjTempVirtualServer.sourcexlatetype = [string]$VirtualServerSourceAddressTranslationTypes[$i]
1254 $ObjTempVirtualServer.sourcexlatepool = [string]$VirtualServerSourceAddressSnatPool[$i]
1255 } Catch {
1256 $ObjTempVirtualServer.sourcexlatetype = "OLDVERSION"
1257 $ObjTempVirtualServer.sourcexlatepool = "OLDVERSION"
1258 }
1259
1260
1261 if($Global:Bigipreportconfig.Settings.iRules.enabled -eq $false){
1262 #Hiding iRules to the users
1263 $ObjTempVirtualServer.irules = @();
1264 }
1265
1266 if($VirtualServerVlans[$i].state -eq "STATE_DISABLED" -and $VirtualServerVlans[$i].vlans.count -eq 0){
1267 $ObjTempVirtualServer.vlanstate = "enabled"
1268 } elseif ($VirtualServerVlans[$i].state -eq "STATE_DISABLED") {
1269 $ObjTempVirtualServer.vlanstate = "disabled"
1270 $ObjTempVirtualServer.vlans = $VirtualServerVlans[$i].vlans
1271 } elseif ($VirtualServerVlans[$i].state -eq "STATE_ENABLED") {
1272 $ObjTempVirtualServer.vlanstate = "enabled"
1273 $ObjTempVirtualServer.vlans = $VirtualServerVlans[$i].vlans
1274 }
1275
1276 $VirtualServerSASMPolicies = $LoadBalancerObjects.ASMPolicies.values | Where-Object { $_.virtualServers -contains $VirtualServerName }
1277
1278 if($VirtualServerSASMPolicies -ne $null){
1279 $ObjTempVirtualServer.asmPolicies = $VirtualServerSASMPolicies.name
1280 }
1281
1282 $Partition = $VirtualServerName.split("/")[1]
1283 $Destination = $VirtualServerDestinations[$i].address
1284
1285 $ObjTempVirtualServer.trafficgroup = $TrafficGroupDict["/$Partition/$Destination"]
1286
1287 $ObjTempVirtualServer.availability = $VirtualServerStates[$i].availability_status
1288 $ObjTempVirtualServer.enabled = $VirtualServerStates[$i].enabled_status
1289
1290 $VipStatistics = $VirtualServerStatistics.statistics[$i].statistics
1291
1292 #Connection statistics
1293 $ObjTempVirtualServer.currentconnections = Get-Int64 -High $($VipStatistics[8].value.high) -Low $($VipStatistics[8].value.low)
1294 $ObjTempVirtualServer.maximumconnections = Get-Int64 -High $($VipStatistics[9].value.high) -Low $($VipStatistics[9].value.low)
1295
1296 #I don't remember seeing these in the older versions so I'll take the safe bet here
1297 Try {
1298 #CPU statistics
1299 $ObjTempVirtualServer.cpuavg5sec = Get-Int64 -High $($VipStatistics[38].value.high) -Low $($VipStatistics[38].value.low)
1300 $ObjTempVirtualServer.cpuavg1min = Get-Int64 -High $($VipStatistics[39].value.high) -Low $($VipStatistics[39].value.low)
1301 $ObjTempVirtualServer.cpuavg5min = Get-Int64 -High $($VipStatistics[40].value.high) -Low $($VipStatistics[40].value.low)
1302 } Catch {
1303 log "error" "Unable to get virtual server CPU statistics for $VirtualServerName"
1304 }
1305
1306 $LoadBalancerObjects.VirtualServers.add($ObjTempVirtualServer.name, $ObjTempVirtualServer)
1307 }
1308
1309 #EndRegion
1310
1311 #Region Get Orphaned Pools
1312 log verbose "Detecting orphaned pools"
1313
1314 $LoadBalancerObjects.OrphanPools = @()
1315
1316 $VirtualServerPools = $LoadBalancerObjects.VirtualServers.Values.Pools | Sort-Object -Unique
1317 $DataGroupPools = $LoadBalancerObjects.DataGroups.Values.pools | Sort-Object -Unique
1318
1319 Foreach($PoolName in $LoadBalancerObjects.Pools.Keys){
1320 If ($VirtualServerPools -NotContains $PoolName -and
1321 $DataGroupPools -NotContains $PoolName){
1322 $LoadBalancerObjects.Pools[$PoolName].orphaned = $true
1323
1324 $ObjTempVirtualServer = New-Object -TypeName "VirtualServer"
1325
1326 $ObjTempVirtualServer.name = $PoolName + "(Orphan)"
1327 $ObjTempVirtualServer.ip = "N"
1328 $ObjTempVirtualServer.port = "A"
1329 $ObjTempVirtualServer.sslprofileclient += "None"
1330 $ObjTempVirtualServer.sslprofileserver += "None"
1331 $ObjTempVirtualServer.compressionprofile = "None"
1332 $ObjTempVirtualServer.persistence = "None"
1333 $ObjTempVirtualServer.irules = @()
1334 $ObjTempVirtualServer.pools += $PoolName
1335 $ObjTempVirtualServer.loadbalancer = $LoadBalancerName
1336
1337 $LoadBalancerObjects.OrphanPools += $ObjTempVirtualServer
1338 }
1339 }
1340 #EndRegion
1341}
1342#EndRegion
1343
1344#Region Function Get-StatisticsDictionary
1345
1346#Converts an F5 statistics object to a more accessible format
1347
1348Function Get-PoolMemberStatisticsDictionary {
1349 Param($PoolMemberStatsObjArray)
1350
1351 $StatisticsDictionary = c@{}
1352
1353 Foreach($PoolMemberStatsObj in $PoolMemberStatsObjArray.Statistics){
1354 $Member = $PoolMemberStatsObj.member.address + ":" + $PoolMemberStatsObj.member.port
1355
1356 $Statistics = c@{}
1357
1358 $CurrentConnections = Get-Int64 -High $($PoolMemberStatsObj.statistics[4].value.high) -Low $($PoolMemberStatsObj.statistics[4].value.low)
1359 $MaximumConnections = Get-Int64 -High $($PoolMemberStatsObj.statistics[5].value.high) -Low $($PoolMemberStatsObj.statistics[5].value.low)
1360
1361 $Statistics.add("currentconnections", $CurrentConnections)
1362 $Statistics.add("maximumconnections", $MaximumConnections)
1363
1364 $StatisticsDictionary.add($Member, $Statistics)
1365 }
1366
1367 Return $StatisticsDictionary
1368}
1369
1370#EndRegion
1371
1372#Region Function Get-Int64
1373
1374Function Get-Int64 {
1375 Param($High, $Low)
1376
1377 Return ([math]::Pow($High, 32) + $Low)
1378}
1379
1380#EndRegion
1381
1382#Region Function Get-AuthToken
1383
1384Function Get-AuthToken {
1385 Param($LoadBalancer)
1386
1387 $User = $Global:Bigipreportconfig.Settings.Credentials.Username
1388 $Password = $Global:Bigipreportconfig.Settings.Credentials.Password
1389
1390 #Create the string that is converted to Base64
1391 $Credentials = $User + ":" + $Password
1392
1393 #Encode the string to base64
1394 $EncodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($Credentials))
1395
1396 #Add the "Basic prefix"
1397 $BasicAuthValue = "Basic $EncodedCreds"
1398
1399 #Prepare the headers
1400 $Headers = @{
1401 "Authorization" = $BasicAuthValue
1402 "Content-Type" = "application/json"
1403 }
1404
1405 #Create the body of the post
1406 $Body = @{"username" = $User; "password" = $Password; "loginProviderName" = "tmos" }
1407
1408 #Convert the body to Json
1409 $Body = $Body | ConvertTo-Json
1410
1411 $Response = Invoke-RestMethod -Method "POST" -Headers $Headers -Body $Body -Uri "https://$LoadBalancer/mgmt/shared/authn/login"
1412
1413 return $Response.token.token
1414}
1415
1416#EndRegion
1417
1418#Region Function Get-iRules
1419Function Get-DefinedRules {
1420 $DefinedRules = $Bigipreportconfig.Settings.iRules.iRule
1421
1422 $Rules = @()
1423
1424 Foreach($Rule in ( $DefinedRules | Where-Object { $_.LoadBalancer -and $_.iRuleName } )){
1425 $TempRule = c@{}
1426
1427 $TempRule.add("loadBalancer", $Rule.loadBalancer)
1428 $TempRule.add("iRuleName", $Rule.iRuleName)
1429
1430 $Rules += $TempRule
1431 }
1432
1433 ConvertTo-Json $Rules
1434}
1435#EndRegion
1436
1437
1438#Region Call Cache LTM information
1439Foreach($DeviceGroup in $Global:Bigipreportconfig.Settings.DeviceGroups.DeviceGroup) {
1440 $IsOnlyDevice = $DeviceGroup.Device.Count -eq 1
1441 $StatusVIP = $DeviceGroup.StatusVip
1442
1443 $ObjDeviceGroup = New-Object -TypeName "DeviceGroup"
1444 $ObjDeviceGroup.name = $DeviceGroup.name
1445
1446 Foreach($Device in $DeviceGroup.Device){
1447 log verbose "Getting data from $Device"
1448
1449 $ObjDeviceGroup.ips += $Device
1450
1451 $ErrorActionPreference = "SilentlyContinue"
1452
1453 $success = Initialize-F5.iControl -Username $Global:Bigipreportconfig.Settings.Credentials.Username -Password $Global:Bigipreportconfig.Settings.Credentials.Password -HostName $Device
1454
1455 if($?){
1456 log success "iControl session successfully established"
1457 $ErrorActionPreference = "Continue"
1458 } Else {
1459 $F5 = Get-F5.iControl
1460
1461 log error "The script failed to connect to $Device, run the report manually to determine if this was due to a timeout of bad credentials"
1462 log errro $F5.LastException.Message
1463
1464 $ObjLoadBalancer = New-Object -TypeName "Loadbalancer"
1465
1466 $ObjLoadBalancer.ip = $Device
1467 $ObjLoadBalancer.success = $false
1468
1469 $ObjStatusVIP = New-Object -TypeName "PoolStatusVip"
1470 $ObjLoadBalancer.statusvip = $ObjStatusVIP
1471
1472 $LoadBalancerObjects = c@{}
1473 $LoadBalancerObjects.LoadBalancer = $ObjLoadBalancer
1474
1475 $Global:ReportObjects.add($ObjLoadBalancer.ip, $LoadBalancerObjects)
1476
1477 Continue
1478 }
1479
1480 $F5 = Get-F5.iControl
1481
1482 $ObjLoadBalancer = New-Object -TypeName "Loadbalancer"
1483
1484 $ObjLoadBalancer.isonlydevice = $IsOnlyDevice
1485
1486 log verbose "Getting hostname"
1487
1488 $BigIPHostname = $F5.SystemInet.get_hostname()
1489
1490 if($?){
1491 log verbose "Hostname is $BigipHostname"
1492 } else {
1493 log error "Failed to get hostname"
1494 }
1495
1496 #Get information about ip, name, model and category
1497 $SystemInfo = $F5.SystemSystemInfo.get_system_information()
1498
1499 $ObjLoadBalancer.ip = $Device
1500 $ObjLoadBalancer.name = $BigIPHostname
1501 $ObjLoadBalancer.model = $SystemInfo.platform
1502 $ObjLoadBalancer.category = $SystemInfo.product_category
1503
1504 If($ObjLoadBalancer.category -eq "Virtual Edition"){
1505 # Virtual Editions is using the base registration keys as serial numbers
1506 $RegistrationKeys = $F5.ManagementLicenseAdministration.get_registration_keys();
1507 $BaseRegistrationKey = $RegistrationKeys[0]
1508
1509 $Serial = $BaseRegistrationKey.split("-")[-1]
1510 } else {
1511 $Serial = $SystemInfo.chassis_serial
1512 }
1513
1514 $ObjLoadBalancer.serial = $Serial
1515
1516 If($ObjLoadBalancer.category -eq "VCMP"){
1517 $HostHardwareInfo = $F5.SystemSystemInfo.get_hardware_information() | Where-Object { $_.name -eq "host_platform" }
1518
1519 if ($HostHardwareInfo.Count -eq 1){
1520 $Platform = $HostHardwareInfo.versions | Where-Object { $_.name -eq "Host platform name" }
1521
1522 if($Platform.Count -gt 0){
1523 # Some models includes the disk type for some reason: "C119-SSD". Removing it.
1524 $ObjLoadBalancer.model = $Platform.value -replace "-.+", ""
1525 }
1526 }
1527 }
1528
1529 $ObjStatusVIP = New-Object -TypeName "PoolStatusVip"
1530 $ObjStatusVIP.url = $StatusVIP
1531 $ObjLoadBalancer.statusvip = $ObjStatusVIP
1532
1533 #Region Cache Load balancer information
1534 log verbose "Fetching information about the device"
1535
1536 #Get the version information
1537 $VersionInformation = ($F5.SystemSoftwareManagement.get_all_software_status()) | Where-Object { $_.active -eq "True" }
1538
1539 #Get provisioned modules
1540 $Modules = $F5.ManagementProvision.get_provisioned_list()
1541
1542 $ObjLoadBalancer.version = $VersionInformation.version
1543 $ObjLoadBalancer.build = $VersionInformation.build
1544 $ObjLoadBalancer.baseBuild = $VersionInformation.baseBuild
1545
1546 #Get failover status to determine if the load balancer is active
1547 $FailoverStatus = $F5.ManagementDeviceGroup.get_failover_status()
1548
1549 $ObjLoadBalancer.active = $FailoverStatus.status -eq "ACTIVE"
1550 $ObjLoadBalancer.color = ($FailoverStatus.color -replace "COLOR_", "").toLower()
1551
1552 $ModuleDict = c@{}
1553
1554 foreach($Module in $Modules){
1555 $ModuleCode = [string]$Module
1556
1557 if($ModuleToShort.keys -contains $ModuleCode){
1558 $ModuleShortName = $ModuleToShort[$ModuleCode]
1559 } else {
1560 $ModuleShortName = $ModuleCode.replace("TMOS_MODULE_", "")
1561 }
1562
1563 if($ModuleToDescription.keys -contains $ModuleShortName){
1564 $ModuleDescription = $ModuleToDescription[$ModuleShortName]
1565 } else {
1566 $ModuleDescription = "No description found"
1567 }
1568
1569 if(!($ModuleDict.keys -contains $ModuleShortName)){
1570 $ModuleDict.add($ModuleShortName, $ModuleDescription)
1571 }
1572 }
1573
1574 $ObjLoadBalancer.modules = $ModuleDict
1575
1576 $ObjLoadBalancer.success = $true
1577
1578 $LoadBalancerObjects = c@{}
1579 $LoadBalancerObjects.LoadBalancer = $ObjLoadBalancer
1580
1581 $Global:ReportObjects.add($ObjLoadBalancer.ip, $LoadBalancerObjects)
1582
1583 #Don't continue if this loabalancer is not active
1584 If($ObjLoadBalancer.active -or $IsOnlyDevice){
1585 log verbose "Caching LTM information from $BigIPHostname"
1586 Get-LTMInformation -f5 $F5 -LoadBalancer $LoadBalancerObjects
1587 } else {
1588 log info "This load balancer is not active, and won't be indexed"
1589 Continue
1590 }
1591 }
1592 $Global:DeviceGroups += $ObjDeviceGroup
1593}
1594#EndRegion
1595
1596#Region Function Test-ReportData
1597
1598#Verify that data from all the load balancers has been indexed by checking the pools variable
1599function Test-ReportData {
1600 $NoneMissing = $true
1601 log verbose "Verifying load balancer data to make sure that no load balancer is missing"
1602 #For every load balancer IP we will check that no pools or virtual servers are missing
1603 Foreach($DeviceGroup in $Global:Bigipreportconfig.Settings.DeviceGroups.DeviceGroup) {
1604 $DeviceGroupHasData = $False
1605 ForEach($Device in $DeviceGroup.Device){
1606 $LoadBalancerObjects = $Global:ReportObjects[$Device]
1607 If ($LoadBalancerObjects) {
1608 $LoadBalancer = $LoadBalancerObjects.LoadBalancer
1609 $LoadBalancerName = $LoadBalancer.name
1610 # Only check for load balancers that is alone in a device group, or active
1611 if($LoadBalancer.active -or $LoadBalancer.isonlydevice){
1612 $DeviceGroupHasData = $True
1613 #Verify that the $Global:virtualservers contains the $LoadBalancerName
1614 If ($LoadBalancerObjects.VirtualServers.Count -eq 0) {
1615 log error "$LoadBalancerName does not have any Virtual Server data"
1616 $NoneMissing = $false
1617 }
1618 #Verify that the $Global:pools contains the $LoadBalancerName
1619 If ($LoadBalancerObjects.Pools.Count -eq 0) {
1620 log error "$LoadBalancerName does not have any Pool data"
1621 }
1622 #Verify that the $Global:monitors contains the $LoadBalancerName
1623 If ($LoadBalancerObjects.Monitors.Count -eq 0){
1624 log error "$LoadBalancerName does not have any Monitor data"
1625 $NoneMissing = $false
1626 }
1627 #Verify that the $Global:irules contains the $LoadBalancerName
1628 If ($LoadBalancerObjects.iRules.Count -eq 0) {
1629 log error "$LoadBalancerName does not have any iRule data"
1630 $NoneMissing = $false
1631 }
1632 #Verify that the $Global:nodes contains the $LoadBalancerName
1633 if($LoadBalancerObjects.Nodes.Count -eq 0){
1634 log error "$LoadBalancerName does not have any Node data"
1635 $NoneMissing = $false
1636 }
1637 #Verify that the $Global:DataGroups contains the $LoadBalancerName
1638 if($LoadBalancerObjects.DataGroups.Count -eq 0){
1639 log error "$LoadBalancerName does not have any Data group data"
1640 $NoneMissing = $false
1641 }
1642 #Verify that the $Global:Certificates contains the $LoadBalancerName
1643 if($LoadBalancerObjects.Certificates.Count -eq 0){
1644 log error "$LoadBalancerName does not have any Certificate data"
1645 $NoneMissing = $false
1646 }
1647 }
1648 } Else {
1649 log error "$Device does not seem to have been indexed"
1650 $NoneMissing = $false
1651 }
1652 }
1653 If (-Not $DeviceGroupHasData){
1654 log error "Missing data from device group containing $($DeviceGroup.Device -Join ", ")."
1655 $NoneMissing = $false
1656 }
1657 }
1658 Return $NoneMissing
1659}
1660#EndRegion
1661
1662#Region Function Update-ReportData
1663Function Update-ReportData {
1664 [bool]$Status = $true
1665
1666 #Move the temp files to the actual report files
1667 log verbose "Updating the report with the new data"
1668
1669 Move-Item -Force $($Global:reportpath + ".tmp") $Global:reportpath
1670
1671 if(!$?){
1672 log error "Failed to update the report file"
1673 $Status = $false
1674 }
1675
1676 Move-Item -Force $($Global:poolsjsonpath + ".tmp") $Global:poolsjsonpath
1677
1678 if(!$?){
1679 log error "Failed to update the pools json file"
1680 $Status = $false
1681 }
1682
1683 Move-Item -Force $($Global:monitorsjsonpath + ".tmp") $Global:monitorsjsonpath
1684
1685 if(!$?){
1686 log error "Failed to update the monitor json file"
1687 $Status = $false
1688 }
1689
1690 Move-Item -Force $($Global:virtualserversjsonpath + ".tmp") $Global:virtualserversjsonpath
1691
1692 if(!$?){
1693 log error "Failed to update the virtual server json file"
1694 $Status = $false
1695 }
1696
1697 Move-Item -Force $($Global:irulesjsonpath + ".tmp") $Global:irulesjsonpath
1698
1699 if(!$?){
1700 log error "Failed to update the irules json file"
1701 $Status = $false
1702 }
1703
1704 Move-Item -Force $($Global:datagroupjsonpath + ".tmp") $Global:datagroupjsonpath
1705
1706 if(!$?){
1707 log error "Failed to update the data groups json file"
1708 $Status = $false
1709 }
1710
1711 Move-Item -Force $($Global:loadbalancersjsonpath + ".tmp") $Global:loadbalancersjsonpath
1712
1713 if(!$?){
1714 log error "Failed to update the load balancers json file"
1715 $Status = $false
1716 }
1717
1718 Move-Item -Force $($Global:certificatesjsonpath + ".tmp") $Global:certificatesjsonpath
1719
1720 if(!$?){
1721 log error "Failed to update the certificates json file"
1722 $Status = $false
1723 }
1724
1725 Move-Item -Force $($Global:devicegroupsjsonpath + ".tmp") $Global:devicegroupsjsonpath
1726
1727 if(!$?){
1728 log error "Failed to update the device groups json file"
1729 $Status = $false
1730 }
1731
1732 Move-Item -Force $($Global:loggederrorsjsonpath + ".tmp") $Global:loggederrorsjsonpath
1733
1734 if(!$?){
1735 log error "Failed to update the logged errors json file"
1736 $Status = $false
1737 }
1738
1739 Move-Item -Force $($Global:asmpoliciesjsonpath + ".tmp") $Global:asmpoliciesjsonpath
1740
1741 if(!$?){
1742 log error "Failed to update the asm json file"
1743 $Status = $false
1744 }
1745
1746 Move-Item -Force $($Global:natjsonpath + ".tmp") $Global:natjsonpath
1747
1748 if(!$?){
1749 log error "Failed to update the nat json file"
1750 $Status = $false
1751 }
1752
1753 Return $Status
1754}
1755#EndRegion
1756
1757#This function converts a list of objects to an array
1758function ConvertTo-Array
1759{
1760 begin {
1761 $Output = @();
1762 }
1763 process {
1764 $Output += $_;
1765 }
1766 end {
1767 return ,$Output;
1768 }
1769}
1770
1771Function Write-JSONFile {
1772 Param($Data, $DestinationFile)
1773
1774 $DestinationTempFile = $DestinationFile + ".tmp"
1775
1776 log verbose "Writing temporary file $DestinationTempFile"
1777
1778 $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
1779
1780 if ($Outputlevel -ne "Verbose") {
1781 $JSONData = ConvertTo-Json -Compress -Depth 5 $Data
1782 } else {
1783 $JSONData = ConvertTo-Json -Depth 5 $Data
1784 }
1785
1786 $StreamWriter = New-Object System.IO.StreamWriter($DestinationTempFile, $false, $Utf8NoBomEncoding,0x10000)
1787 $StreamWriter.Write($JSONData)
1788
1789 if(!$?){
1790 log error "Failed to update the temporary pool json file"
1791 $Success = $false
1792 } else {
1793 $Success = $true
1794 }
1795
1796 $StreamWriter.dispose()
1797
1798 Return $Success
1799}
1800
1801#Region Function Write-TemporaryFiles
1802Function Write-TemporaryFiles {
1803 #This is done to save some downtime if writing the report over a slow connection
1804 #or if the report is really big.
1805
1806 $WriteStatuses = @()
1807
1808 $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
1809
1810 log verbose "Writing temporary report file to $($Global:reportpath + ".tmp")"
1811
1812 $HTMLContent = $Global:HTML.ToString();
1813
1814 # remove whitespace from output
1815 if($Outputlevel -ne "Verbose"){
1816 $HTMLContent = $HTMLContent.Split("`n").Trim() -Join "`n"
1817 }
1818
1819 $StreamWriter = New-Object System.IO.StreamWriter($($Global:reportpath + ".tmp"), $false, $Utf8NoBomEncoding,0x10000)
1820 $StreamWriter.Write($HTMLContent)
1821
1822 if(!$?){
1823 log error "Failed to update the temporary report file"
1824 $WriteStatuses += $false
1825 }
1826
1827 $StreamWriter.dispose()
1828
1829 $WriteStatuses += Write-JSONFile -DestinationFile $Global:poolsjsonpath -Data @( $Global:ReportObjects.Values.Pools.Values | Sort-Object loadbalancer, name )
1830 $WriteStatuses += Write-JSONFile -DestinationFile $Global:monitorsjsonpath -Data @( $Global:ReportObjects.Values.Monitors.Values | Sort-Object loadbalancer, name )
1831 $WriteStatuses += Write-JSONFile -DestinationFile $Global:loadbalancersjsonpath -Data @( $Global:ReportObjects.Values.LoadBalancer | Sort-Object name )
1832 $WriteStatuses += Write-JSONFile -DestinationFile $Global:virtualserversjsonpath -Data @( $Global:ReportObjects.Values.VirtualServers.Values | Sort-Object loadbalancer,name )
1833 $WriteStatuses += Write-JSONFile -DestinationFile $Global:certificatesjsonpath -Data @( $Global:ReportObjects.Values.Certificates.Values | Sort-Object loadbalancer, fileName )
1834 $WriteStatuses += Write-JSONFile -DestinationFile $Global:devicegroupsjsonpath -Data @( $Global:DeviceGroups | Sort-Object name )
1835 $WriteStatuses += Write-JSONFile -DestinationFile $Global:loggederrorsjsonpath -Data @( $Global:ReportObjects.LoggedErrors )
1836 If ($Global:ReportObjects.Values.ASMPolicies.Keys.Count -gt 0) {
1837 $WriteStatuses += Write-JSONFile -DestinationFile $Global:asmpoliciesjsonpath -Data @( $Global:ReportObjects.Values.ASMPolicies.Values | Sort-Object loadbalancer, name )
1838 } else {
1839 $WriteStatuses += Write-JSONFile -DestinationFile $Global:asmpoliciesjsonpath -Data @()
1840 }
1841 $WriteStatuses += Write-JSONFile -DestinationFile $Global:natjsonpath -Data $Global:NATdict
1842
1843 if($Global:Bigipreportconfig.Settings.iRules.Enabled -eq $true){
1844 $WriteStatuses += Write-JSONFile -DestinationFile $Global:irulesjsonpath -Data @($Global:ReportObjects.Values.iRules.Values | Sort-Object loadbalancer, name )
1845 } else {
1846 log verbose "iRule links disabled in config. Writing empty json object to $($Global:irulesjsonpath + ".tmp")"
1847
1848 $StreamWriter = New-Object System.IO.StreamWriter($($Global:irulesjsonpath + ".tmp"), $false, $Utf8NoBomEncoding,0x10000)
1849
1850 #Since rules has been disabled, only write those defined
1851 $RuleScope = $Global:ReportObjects.Values.iRules.Values | Where-Object { $_.name -in $Bigipreportconfig.Settings.iRules.iRule.iRuleName -and $_.loadbalancer -in $Bigipreportconfig.Settings.iRules.iRule.loadbalancer }
1852
1853 if($RuleScope.count -eq 0){
1854 $StreamWriter.Write("[]")
1855 } else {
1856 $StreamWriter.Write($(ConvertTo-Json -Compress -Depth 5 [array]$RuleScope))
1857 }
1858
1859 if(!$?){
1860 log error "Failed to update the temporary irules json file"
1861 $WriteStatuses += $false
1862 }
1863 }
1864
1865 $StreamWriter.dispose()
1866
1867 if($Global:Bigipreportconfig.Settings.iRules.ShowDataGroupLinks -eq $true){
1868 $WriteStatuses += Write-JSONFile -DestinationFile $Global:datagroupjsonpath -Data @( $Global:ReportObjects.Values.DataGroups.Values | Sort-Object loadbalancer, name )
1869 } else {
1870 $WriteStatuses += Write-JSONFile -DestinationFile $Global:datagroupjsonpath -Data @()
1871 }
1872 $StreamWriter.dispose()
1873 Return -not $( $WriteStatuses -Contains $false)
1874}
1875
1876#EndRegion
1877
1878#Region Check for missing data and if the report contains ASM profiles
1879if(-not (Test-ReportData)){
1880 if(-not $Global:Bigipreportconfig.Settings.ErrorReportAnyway -eq $true){
1881 log error "Missing load balancer data, no report will be written"
1882 Send-Errors
1883 Exit
1884 }
1885 log error "Missing load balancer data, writing report anyway"
1886 Send-Errors
1887} else {
1888 log success "No missing loadbalancer data was detected, compiling the report"
1889}
1890
1891#EndRegion
1892
1893#Global:html contains the report html data
1894#Add links to style sheets, jquery and datatables and some embedded javascripts
1895
1896$Global:HTML = [System.Text.StringBuilder]::new()
1897
1898[void]$Global:HTML.AppendLine(@'
1899<!DOCTYPE html>
1900<html lang="en">
1901 <head>
1902 <title>BIG-IP Report</title>
1903 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
1904
1905 <link href="css/pace.css" rel="stylesheet" type="text/css"/>
1906 <link href="css/jquery.dataTables.css" rel="stylesheet" type="text/css">
1907 <link href="css/buttons.dataTables.min.css" rel="stylesheet" type="text/css">
1908 <link href="css/bigipreportstyle.css" rel="stylesheet" type="text/css">
1909 <link href="css/sh_style.css" rel="stylesheet" type="text/css">
1910
1911 <script src="js/pace.js" data-pace-options="{ "restartOnRequestAfter": false }"></script>
1912 <script src="js/jquery.min.js"></script>
1913 <script src="js/jquery.dataTables.min.js"></script>
1914 <script src="js/dataTables.buttons.min.js"></script>
1915 <script src="js/buttons.colVis.min.js"></script>
1916 <script src="js/buttons.html5.min.js"></script>
1917 <script src="js/buttons.print.min.js"></script>
1918 <script src="js/jquery.highlight.js"></script>
1919 <script src="js/bigipreport.js"></script>
1920 <script src="js/sh_main.js"></script>
1921 <script>
1922'@
1923)
1924
1925 # Transfer some settings from the config file onto the Javascript
1926 # Todo: All global variables should be located in a single object
1927 # to minimize the polution of the global namespace.
1928 $DefinediRules = Get-DefinedRules
1929 [void]$Global:HTML.AppendLine("var definedRules = " + $DefinediRules + ";`n")
1930
1931 if($Global:Bigipreportconfig.Settings.iRules.enabled -eq $true){
1932 [void]$Global:HTML.AppendLine("const ShowiRules = true;")
1933 } else {
1934 [void]$Global:HTML.AppendLine("const ShowiRules = false;")
1935 }
1936 if($Global:Bigipreportconfig.Settings.iRules.ShowiRuleLinks -eq $true){
1937 [void]$Global:HTML.AppendLine("const ShowiRuleLinks = true;")
1938 } else {
1939 [void]$Global:HTML.AppendLine("const ShowiRuleLinks = false;")
1940 }
1941 if($Global:Bigipreportconfig.Settings.iRules.ShowDataGroupLinks -eq $true){
1942 [void]$Global:HTML.AppendLine("const ShowDataGroupLinks = true;")
1943 } else {
1944 [void]$Global:HTML.AppendLine("const ShowDataGroupLinks = false;")
1945 }
1946 if($Global:Bigipreportconfig.Settings.ExportLink.Enabled -eq $true){
1947 [void]$Global:HTML.AppendLine("const ShowExportLink = true;")
1948 } else {
1949 [void]$Global:HTML.AppendLine("const ShowExportLink = false;")
1950 }
1951 if($Global:Bigipreportconfig.Settings.HideLoadBalancerFQDN -eq $true){
1952 [void]$Global:HTML.AppendLine("const HideLoadBalancerFQDN = true;")
1953 } else {
1954 [void]$Global:HTML.AppendLine("const HideLoadBalancerFQDN = false;")
1955 }
1956 [void]$Global:HTML.AppendLine("const AJAXMAXPOOLS = " + $Global:Bigipreportconfig.Settings.RealTimeMemberStates.MaxPools + ";")
1957 [void]$Global:HTML.AppendLine("const AJAXMAXQUEUE = " + $Global:Bigipreportconfig.Settings.RealTimeMemberStates.MaxQueue + ";")
1958 [void]$Global:HTML.AppendLine("const AJAXREFRESHRATE = " + $Global:Bigipreportconfig.Settings.RealTimeMemberStates.RefreshRate + ";")
1959
1960 [void]$Global:HTML.AppendLine(@'
1961 </script>
1962 </head>
1963 <body>
1964 <div class="beforedocumentready"></div>
1965 <div class="bigipreportheader"><img src="images/bigipreportlogo.png" alt="bigipreportlogo"/></div>
1966 <div class="realtimestatusdiv">
1967 <table>
1968 <tr>
1969 <td><span class="topleftheader">Status VIPs:</span></td><td><span id="realtimetestsuccess">0</span> working, <span id="realtimetestfailed">0</span> failed, <span id="realtimenotconfigured">0</span> not configured</td>
1970 </tr>
1971 <tr>
1972 <td><span class="topleftheader">Polling state:</span></td><td id="pollingstatecell"><span id="ajaxqueue">0</span> queued<span id="realtimenextrefresh"></span></td>
1973 </tr>
1974 </table>
1975 </div>
1976 <div id="updateavailablediv"></div>
1977 <div id="mainholder">
1978 <div class="sidemenu">
1979 <div class="menuitem" id="virtualserversbutton" onclick="Javascript:showVirtualServers();"><img id="virtualserverviewicon" src="images/virtualservericon.png" alt="virtual servers"/> Virtual Servers</div>
1980 <div class="menuitem" id="poolsbutton" onclick="Javascript:showPools();"><img id="poolsicon" src="images/poolsicon.png" alt="pools"/> Pools</div>
1981 <div class="menuitem" id="irulesbutton" onclick="Javascript:showiRules();"><img id="irulesicon" src="images/irulesicon.png" alt="irules"/> iRules</div>
1982 <div class="menuitem" id="datagroupbutton" onclick="Javascript:showDataGroups();"><img id="datagroupsicon" src="images/datagroupicon.png" alt="logs"/> Data Groups</div>
1983 <div class="menuitem" id="deviceoverviewbutton" onclick="Javascript:showDeviceOverview();"><img id="devicesoverviewicon" src="images/devicesicon.png" alt="overview"/> Device overview</div>
1984 <div class="menuitem" id="certificatebutton" onclick="Javascript:showCertificateDetails();"><img id="certificateicon" src="images/certificates.png" alt="certificates"/> Certificates<span id="certificatenotification"></span></div>
1985 <div class="menuitem" id="logsbutton" onclick="Javascript:showReportLogs();"><img id="logsicon" src="images/logsicon.png" alt="logs"/> Logs</div>
1986 <div class="menuitem" id="preferencesbutton" onclick="Javascript:showPreferences();"><img id="preferencesicon" src="images/preferences.png" alt="preferences"/> Preferences</div>
1987 <div class="menuitem" id="helpbutton" onclick="Javascript:showHelp();"><img id="helpicon" src="images/help.png" alt="help"/> Help</div>
1988 </div>
1989
1990 <div class="mainsection" id="virtualservers" style="display: none;"></div>
1991 <div class="mainsection" id="pools" style="display: none;"></div>
1992 <div class="mainsection" id="irules" style="display: none;"></div>
1993 <div class="mainsection" id="deviceoverview" style="display: none;"></div>
1994 <div class="mainsection" id="certificatedetails" style="display: none;"></div>
1995 <div class="mainsection" id="datagroups" style="display: none;"></div>
1996 <div class="mainsection" id="preferences" style="display: none;"></div>
1997
1998 <div class="mainsection" id="reportlogs" style="display: none;">
1999 <table id="reportlogstable" class="bigiptable">
2000 <thead>
2001 <tr><th>Date</th><th>Time</th><th>Severity</th><th>Log content</th></tr>
2002 </thead>
2003 <tbody>
2004 </tbody>
2005 </table>
2006 </div>
2007
2008 <div class="mainsection" id="helpcontent" style="display: none;">
2009 <h3>Filtering for pool members being down</h3>
2010 <p>This one is a bit of a hidden feature. In the Pool/Members column you can filter on "<span style="color:red"><b>DOWN</b></span>", "<span style="color:green"><b>UP</b></span>" and "<b>DISABLED</b>".</p>
2011 <p>It's not perfect though since pools or members with any of these words in the name will also end up as results.</p>
2012 <h3>Column filtering</h3>
2013 <p>Clicking on any column header allows you to filter data within that column. This has been more clear in the later versions but worth mentioning in case you've missed it.</p>
2014 <h3>Pool member tests</h3>
2015 <p>If you click on any pool name to bring up the details you have a table at the bottom containing tests for each configured monitor. The tests is generating HTTP links, CURL links and netcat commands for HTTP based monitors and can be used to troubleshoot why a monitor is failing.</p>
2016 <h3>Feature requests</h3>
2017 <p>Please add any feature requests or suggestions here:</p>
2018 <p><a href="https://devcentral.f5.com/codeshare/bigip-report">https://devcentral.f5.com/codeshare/bigip-report</a></p>
2019 <p>And if you like the project, please set aside some of your time to leave a <a href="https://devcentral.f5.com/codeshare/bigip-report#rating">review/rating</a>.</p>
2020 <h3>Troubleshooting</h3>
2021 <p>If the report does not work as you'd expect or you're getting error messages, please read the <a href="https://loadbalancing.se/bigip-report/#FAQ">FAQ</a> first. If you can't find anything there, please add a comment in the project over at <a href="https://devcentral.f5.com/codeshare/bigip-report">Devcentral</a>.</p>
2022 <p>To collect and download anonymized data for submitting a device overview bug report, <a href="javascript:exportDeviceData()">Export Device Data</a>.</p>
2023 <h3>Contact</h3>
2024 <p>If you need to get hold of the author, then see <a href="https://loadbalancing.se/about/">contact information</a>.</p>
2025 </div>
2026 </div>
2027'@)
2028
2029[void]$Global:HTML.AppendLine(@"
2030 <div class="footer">
2031 The report was generated on $($env:computername) using BigIP Report version $($Global:ScriptVersion).
2032 Script started at <span id="Generationtime">$StartTime</span> and took $([int]($(Get-Date)-$StartTime).totalminutes) minutes to finish.<br>
2033 BigIPReport is written and maintained by <a href="http://loadbalancing.se/about/">Patrik Jonsson</a>.
2034 </div>
2035"@)
2036
2037#Initiate variables used for showing progress (in case the debug is set)
2038
2039#Initiate variables to give unique id's to pools and members
2040$RealTimeStatusDetected = ($Global:ReportObjects.Values.LoadBalancer | Where-Object { $_.statusvip.url -ne "" }).Count -gt 0
2041if($RealTimeStatusDetected){
2042 log verbose "Status vips detected in the configuration, simplified icons will be used for the whole report"
2043}
2044
2045[void]$Global:HTML.AppendLine(@"
2046 <div class="lightbox" id="firstlayerdiv">
2047 <div class="innerLightbox">
2048 <div class="lightboxcontent" id="firstlayerdetailscontentdiv">
2049 </div>
2050 </div>
2051 <div id="firstlayerdetailsfooter" class="firstlayerdetailsfooter"><a class="lightboxbutton" id="closefirstlayerbutton" href="javascript:void(0);">Close div</a></div>
2052 </div>
2053
2054 <div class="lightbox" id="secondlayerdiv">
2055 <div class="innerLightbox">
2056 <div class="lightboxcontent" id="secondlayerdetailscontentdiv">
2057 </div>
2058 </div>
2059 <div class="secondlayerdetailsfooter" id="secondlayerdetailsfooter"><a class="lightboxbutton" id="closesecondlayerbutton" href="javascript:void(0);">Close div</a></div>
2060 </div>
2061 </body>
2062</html>
2063"@)
2064
2065$ErrorLog = @()
2066
2067ForEach ( $e in $Global:LoggedErrors) {
2068 $LogLineDict = @{}
2069
2070 $LogLineDict["date"] = $(Get-Date -UFormat %Y-%m-%d)
2071 $LogLineDict["time"] = $(Get-Date -UFormat %H:%M:%S)
2072 $logLineDict["severity"] = "ERROR"
2073 $LogLineDict["message"] = $e
2074
2075 $ErrorLog += $LogLineDict
2076}
2077
2078$ReportObjects.LoggedErrors = $ErrorLog
2079
2080# Time to write temporary files and then update the report
2081
2082$TemporaryFilesWritten = $false
2083
2084if(-not (Write-TemporaryFiles)){
2085 #Failed to write the temporary files
2086 log error "Failed to write the temporary files, waiting 2 minutes and trying again"
2087 Start-Sleep 120
2088
2089 if(Write-TemporaryFiles){
2090 $TemporaryFilesWritten = $true
2091 log success "Successfully wrote the temporary files"
2092 } else {
2093 log error "Failed to write the temporary files. No report has been created/updated"
2094 }
2095} else {
2096 $TemporaryFilesWritten = $true
2097 log success "Successfully wrote the temporary files"
2098}
2099
2100if($TemporaryFilesWritten){
2101 #Had some problems with the move of the temporary files
2102 #Adding a sleep to allow the script to finish writing
2103 Start-Sleep 10
2104
2105 if(Update-ReportData){
2106 log success "The report has been successfully been updated"
2107 } else {
2108 #Failed, trying again after two minutes
2109 Start-Sleep 120
2110
2111 if(Update-ReportData){
2112 log success "The report has been successfully been updated"
2113 } else {
2114 log error "Failed to create/update the report"
2115 }
2116 }
2117} else {
2118 log error "The writing of the temporary files failed, no report files will be updated"
2119}
2120
2121Send-Errors
2122
2123if($Global:Bigipreportconfig.Settings.LogSettings.Enabled -eq $true){
2124 $LogFile = $Global:Bigipreportconfig.Settings.LogSettings.LogFilePath
2125
2126 if(Test-Path $LogFile){
2127 log verbose "Pruning logfile $LogFile"
2128
2129 $MaximumLines = $Global:Bigipreportconfig.Settings.LogSettings.MaximumLines
2130
2131 $LogContent = Get-Content $LogFile -Encoding UTF8
2132 $LogContent | Select-Object -Last $MaximumLines | Out-File $LogFile -Encoding UTF8
2133 }
2134}
2135
2136# Record some stats
2137
2138$DoneMsg = "Done."
2139$DoneMsg += " G:" + $Global:DeviceGroups.Count
2140$DoneMsg += " LB:" + $Global:ReportObjects.Values.LoadBalancer.Count
2141$DoneMsg += " VS:" + $Global:ReportObjects.Values.VirtualServers.Keys.Count
2142$DoneMsg += " R:" + $Global:ReportObjects.Values.iRules.Keys.Count
2143$DoneMsg += " DG:" + $Global:ReportObjects.Values.DataGroups.Keys.Count
2144$DoneMsg += " P:" + $Global:ReportObjects.Values.Pools.Keys.Count
2145$DoneMsg += " M:" + $Global:ReportObjects.Values.Monitors.Keys.Count
2146$DoneMsg += " C:" + $Global:ReportObjects.Values.Certificates.Keys.Count
2147$DoneMsg += " ASM:" + $Global:ReportObjects.Values.ASMPolicies.Keys.Count
2148
2149log verbose $DoneMsg