├── .gitattributes
├── .gitignore
├── New-DFSMonitor.ps1
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear on external disk
35 | .Spotlight-V100
36 | .Trashes
37 |
38 | # Directories potentially created on remote AFP share
39 | .AppleDB
40 | .AppleDesktop
41 | Network Trash Folder
42 | Temporary Items
43 | .apdisk
44 |
--------------------------------------------------------------------------------
/New-DFSMonitor.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Monitor your DFS Replication Backlog with a graphical history.
4 | .DESCRIPTION
5 | Use the DFSMonitorWithHistory.ps1 script to monitor your DFS backlog. It
6 | contains a graphical history chart as well as detailed information from
7 | each run. I recommend running from a scheduled task every hour to get the
8 | best information.
9 |
10 | Make sure to edit the PARAM section and update it for your environment.
11 |
12 | Graphical history chart is a Google visualization that requires Flash to run
13 | (it's the same chart you see on Google Finance). Historical data is saved
14 | in an XML file.
15 | .PARAMETER DFSServers
16 | Array of computers to be processed. For backwards compatibility this parameter
17 | will also accept a comma seperated list, i.e.: "server1,server2,server3" and
18 | will automatically split it into an array. Otherwise you can use the
19 | standard array syntax: server1,server2,server3 (without quotes), or
20 | "server1","server2","server3"
21 | .PARAMETER DaysToSaveData
22 | How much of a history do you want the script to keep. I recommend no more then 2
23 | weeks, but 1 week is probably better for larger DFS implementations.
24 | .PARAMETER DataLocation
25 | The full path, or UNC, to where you want the script to keep the historic
26 | data. Logs kept by the script will also be stored here.
27 | .PARAMETER OutputLocation
28 | The full path, or UNC, to where you want the script to save the HTML files
29 | the script creates. I recommend you make this the root folder for a web
30 | server. For IIS, the default location would be: \\servername\c$\inetpub\wwwroot
31 | .PARAMETER MaxThreads
32 | This scripts uses multithreading to improve performance for gathering the
33 | DFS information. You can alter the number of threads that it uses based on
34 | the server you have. In testing I found 10 to about the sweet spot for my
35 | server (less or more was slower). You should not need to modify this
36 | amount.
37 | .INPUTS
38 | Historical data file: $DataLocation\DFSData.XML
39 | .OUTPUTS
40 | Log: $DataLocation\debugMMddyyyyHHmm.log
41 | Data File: $DataLocation\DFSData.XML
42 | Primary HTML Page (use this): $OutputLocation\DFSMonitorGrid.HTML
43 | Placeholder HTML for Detailed Reports (contains iFrame): $OutputLocation\DFSDetailIndex.HTML
44 | Detailed Reports (displayed in iFrame): $OutputLocation\DFSDetailsMMddyyyyHHmm.HTML
45 | .EXAMPLE
46 | .\pathtoscript\DFSMonitorWithHistory.PS1
47 | Runs the script while accepting all default values. Edit PARAM section
48 | of this script to match your environment.
49 | .EXAMPLE
50 | .\pathtoscript\DFSMonitorWithHistory.PS1 -DFSServes server1,server2 -DaysToSave 7 -DataLocation c:\scripts\dfs -OutputLocation \\webserver\c$\inetpub\wwwroot
51 | Runs the script using the servers server1 and server2, it will save the data for 7 days and keep
52 | the logs and datafile (DFSData.XML) at c:\scripts\dfs. All of the HTML pages will be stored on
53 | a server called webserver on the c: drive in \inetpub\wwwroot (the default web root directory
54 | on an IIS server). It will accept the default of 10 threads.
55 | .NOTES
56 | Author: Martin Pugh
57 | Twitter: @thesurlyadm1n
58 | Spiceworks: Martin9700
59 | Blog: www.thesurlyadmin.com
60 |
61 | Change Log:
62 | 2.64 - Renamed to New-DFSMonitor and published on github
63 | 2.63 - Improved error reporting in primary script and in multithreaded sub-scripts. Converted
64 | sub-script output to object for better data handling, and changed the "All Groups" variable
65 | from a string array to hashtable for better performance. Fixed some debug messages that worked
66 | fine in ISE but not on command line. Fixed bug with Alerts not showing up on the
67 | DFSMonitorGrid.HTML start page.
68 | 2.62 - Bug when using $DupeCheck to make sure script doesn't check the same group/folder pair
69 | more then once. Added Cmdletbinding to support -Verbose output.
70 | 2.61 - Bug found by Bhopper577 where test-path was still looking for the old CSV data file.
71 | 2.6 - Changed saving from a CSV file to an XML which will preserve date/time without having
72 | to convert in script.
73 | 2.51 - Increased the # of PING's to 4 so remote server detection over WAN will be a little
74 | more reliable. This required a change in the string detection so 0% loss, 25% loss and
75 | 75% loss would all be acceptable. I fudged on the RegEx a bit so technically 20%, 55%
76 | and 70% are OK too, but if you only ping 4 times you can't get those percentages!
77 | .LINK
78 | http://community.spiceworks.com/scripts/show/1536-dfs-monitor-with-history
79 | .LINK
80 | http://www.thesurlyadmin.com/2012/08/03/dfs-replication-monitoring/
81 | #>
82 | [CmdletBinding()]
83 | Param (
84 | [array]$DFSServers = "mffile1,fdfile2,gbfile1",
85 | [int]$DaysToSaveData = 5,
86 | [string]$DataLocation = "d:\blah",
87 | [string]$OutputLocation = "C:\Dropbox\Scripts\PS Scripts\DFS Replication Monitor",
88 | [int]$MaxThreads = 10
89 | )
90 |
91 | #region Functions
92 | Function Test-ServerAvailability
93 | { [CmdletBinding()]
94 | Param (
95 | [string]$Name
96 | )
97 |
98 | Write-Verbose "Test-ServerAvailability: Validating $Name..."
99 | $Ping = $false
100 | $DFS = $false
101 | $DFSr = $false
102 | $Good = $false
103 |
104 | $Result = PING $Name -n 4
105 | Switch -regex ($Result)
106 | { "\(0% loss" { $Ping = $true; Break }
107 | "\(25% loss" { $Err = "Ping response 25%, too low"; Break }
108 | "\((50|75)% loss" { $Ping = $true; Break }
109 | "100% loss" { $Err = "Failed to respond to PING"; Break }
110 | "Destination host unreachable" { $Err = "Destination host unreachable"; Break }
111 | "could not find host" { $Err = "Server Not Found"; Break }
112 | default { $Err = "Unknown Error" }
113 | }
114 | If ($Ping)
115 | { $Err = ""
116 | If ((Get-Service dfsr -ComputerName $Name -ErrorAction SilentlyContinue).Status -eq "Running")
117 | { $DFSr = $true
118 | }
119 | ElseIf ((Get-Service dfs -ComputerName $Name -ErrorAction SilentlyContinue).Status -eq "Running")
120 | { $Err = "DFSr service not running"
121 | $DFS = $true
122 | }
123 | Else
124 | { $Err = "Neither DFSr or DFS services running on server"
125 | Write-Verbose "Test-ServerAvailability: $Err"
126 | }
127 | If ($DFS -or $DFSr)
128 | { Write-Verbose "Test-ServerAvailability: Test WMI communications protocol for $Name"
129 | $Protocol = "WSMAN"
130 |
131 | For ($Loop = 1;$Loop -le 3;$Loop ++)
132 | { $SessionParams = @{
133 | ComputerName = $Name
134 | SessionOption = New-CimSessionOption -Protocol $Protocol
135 | }
136 | Try {
137 | Write-Verbose "Test-ServerAvailability: Try #$($Loop): Attempting to connect using $Protocol..."
138 | $CimSession = New-CimSession @SessionParams -ErrorAction Stop
139 | Write-Verbose "Test-ServerAvailability: Try #$($Loop): Connected succesfully using $Protocol"
140 | $Good = $true
141 | Break
142 | }
143 | Catch {
144 | If ($Protocol -eq "WSMAN")
145 | { Write-Verbose "Test-ServerAvailability: Try #$($Loop): Unable to connect with WSMAN, attempting DCOM..."
146 | $Protocol = "DCOM"
147 | $Loop --
148 | Continue
149 | }
150 | If ($Loop -eq 3)
151 | { $Protocol = ""
152 | $Err = "Unable to connect to WMI using WSMAN or DCOM"
153 | Write-Verbose "Test-ServerAvailability: $Err"
154 | }
155 | Else
156 | { $Protocol = "WSMAN"
157 | }
158 | }
159 | }
160 | }
161 | }
162 | Else
163 | { Write-Verbose "Test-ServerAvailability: Did not respond to ping"
164 | }
165 |
166 | $Result = [PSCustomObject]@{
167 | Name = $Name
168 | Good = $Good
169 | Ping = $Ping
170 | DFS = $DFS
171 | DFSr = $DFSr
172 | Protocol = $Protocol
173 | Error = $Err
174 | }
175 |
176 | Write-Verbose "Test-ServerAvailability: Done with $Name"
177 | Return $Result
178 | }
179 |
180 | Function Get-WQLQuery
181 | { [CmdletBinding()]
182 | Param (
183 | [string]$ComputerName,
184 | [string]$Query,
185 | #[string]$NameSpace = "root\MicrosoftDFS",
186 | [string]$NameSpace = "root\CIMV2",
187 | [ValidateSet("WSMAN","DCOM")]
188 | [string]$Protocol
189 | )
190 |
191 | $SessionParams = @{
192 | ComputerName = $ComputerName
193 | SessionOption = New-CimSessionOption -Protocol $Protocol
194 | }
195 |
196 | For ($Loop = 1;$Loop -le 3;$Loop ++)
197 | { Try {
198 | Write-Verbose "Get-WQLQuery: Try #$Loop - Connecting to $ComputerName using $Protocol"
199 | $CimSession = New-CimSession @SessionParams -ErrorAction Stop
200 | $WMI = Get-CimInstance -CimSession $CimSession -Query $Query -ErrorAction Stop -Namespace $NameSpace
201 | Break
202 | }
203 | Catch {
204 | Write-Verbose "Get-WQLQuery: Failed to connect because $($Error[0])"
205 | Throw $Error[0].ToString()
206 | }
207 | }
208 | Return $WMI
209 | }
210 |
211 | Function Get-WMI
212 | { Param ([String]$WMIQuery,
213 | [String]$Computer)
214 |
215 | $ErrorCount = 0
216 | Do
217 | { $WMIObject = Get-WQLQueryObject -computerName $Computer -Namespace "root\MicrosoftDFS" -Query $WMIQuery -Debug
218 | If ($WMIObject -eq $null)
219 | { $ErrorCount ++
220 | }
221 | Else
222 | { Return $WMIObject
223 | }
224 | }
225 | While ($ErrorCount -le 2)
226 | $WMIObject = "WMI Error on $Computer"
227 | Return $WMIObject
228 | }
229 | #endregion
230 |
231 | #Here we go!
232 | cls
233 |
234 | #Validate Data path exists, since this is where the log file exists we'll use Write-Host to notify.
235 | If ($DataLocation)
236 | { If (-not (Test-Path $DataLocation -PathType Container))
237 | { Throw "Data Path: $DataLocation does not exist! Stopping script."
238 | }
239 | }
240 | Else
241 | { $DataLocation = Split-Path $MyInvocation.MyCommand.Path
242 | }
243 |
244 | #Setup the log
245 | $ScriptRunDate = (Get-Date).DateTime
246 | $SaveFormatDate = Get-Date $ScriptRunDate -format yyyyMMddHHmm
247 | Start-Transcript -Path $DataLocation\debug$SaveFormatDate.log
248 |
249 | #Validate Output/Report location exists
250 | If (-not (Test-Path $OutputLocation -PathType Container))
251 | { Write-Verbose "Output Path: $OutputLocation does not exist! Stopping script."
252 | Exit
253 | }
254 |
255 | #Set some global variables
256 | $NewData = @()
257 | $AllGroupNames = @{}
258 | $DupeCheck = @()
259 | $Servers = @()
260 | $ServerList = @{}
261 | $AlertReport = @()
262 |
263 | #Display parameters
264 | Write-Verbose "Servers to be scanned: $DFSServers"
265 | Write-Verbose "Days to Save Data: $DaysToSaveData"
266 | Write-Verbose "Data Path: $DataLocation"
267 | Write-Verbose "HTML Path: $OutputLocation"
268 | Write-Verbose "Maximum Threads: $MaxThreads"
269 |
270 | Write-Verbose "Loading data..."
271 |
272 | #Parse DFSServers and make a good array
273 | ForEach ($Item in $DFSServers)
274 | { If ($Item.Contains(","))
275 | { $Servers += $Item.Split(",")
276 | }
277 | Else
278 | { $Servers += $Item
279 | }
280 | }
281 | $SaveDate = (Get-Date).Date.AddDays(-$DaysToSaveData)
282 |
283 | #Check if Data file exists, if so import it, if not create it.
284 | If ((Test-Path $DataLocation\DFSData.xml) -eq $False)
285 | { $Data = @()
286 | }
287 | Else
288 | { $Data = Import-Clixml $DataLocation\DFSData.xml
289 | If ($Data.Count -gt 0)
290 | { $Data = $Data | Where {$_.RunDate -ge $SaveDate}
291 | }
292 | Else
293 | { $Data = @()
294 | }
295 | }
296 |
297 | #Start the main loop.
298 | ForEach ($FileServer in $Servers)
299 | { Write-Verbose "Now working on $FileServer..."
300 | If (-not ($ServerList.ContainsKey($FileServer)))
301 | { $ServerList.Add($FileServer,(Test-ServerAvailability -Name $FileServer))
302 | }
303 |
304 | If (-not $ServerList[$FileServer].Good)
305 | { Continue
306 | }
307 |
308 | Try {
309 | $WMIQuery = "SELECT * FROM DfsrReplicationGroupConfig"
310 | $GroupGUIDs = Get-WQLQuery -Query $WMIQuery -ComputerName $FileServer -Protocol $ServerList[$FileServer].Protocol
311 |
312 | $WMIQuery = "SELECT * FROM DfsrConnectionConfig WHERE InBound=True"
313 | $RGConnections = Get-WQLQuery -Query $WMIQuery -ComputerName $FileServer -Protocol $ServerList[$FileServer].Protocol
314 |
315 | $WMIQuery = "SELECT * FROM DfsrReplicatedFolderConfig"
316 | $RGFolders = Get-WQLQuery -Query $WMIQuery -ComputerName $FileServer -Protocol $ServerList[$FileServer].Protocol
317 | }
318 | Catch {
319 | $AlertReport += [PSCustomObject]@{
320 | Server = $FileServer
321 | Query = $WMIQuery
322 | Error = $Error[0]
323 | }
324 | Continue
325 | }
326 |
327 | ForEach ($Group in $GroupGUIDs)
328 | { If (-not $AllGroupNames.ContainsKey($Group.ReplicationGroupGuid))
329 | { $AllGroupNames.Add($Group.ReplicationGroupGUID,$Group.ReplicationGroupName)
330 | }
331 | $GFolders = $RGFolders | Where {$_.ReplicationGroupGUID -eq $Group.ReplicationGroupGUID}
332 | ForEach ($Folder in $GFolders)
333 | { $GConnection = $RGConnections | Where {$_.ReplicationGroupGUID -eq $Group.ReplicationGroupGUID}
334 | ForEach ($Connection in $GConnection)
335 | { $InServer = $FileServer
336 | $OutServer = $Connection.PartnerName
337 |
338 | #Check if we've already done this
339 | $Found = "No"
340 | ForEach ($Line in $DupeCheck)
341 | { If ($Line[0].ToUpper() -eq $Group.ReplicationGroupName.ToUpper() -and
342 | $Line[1].ToUpper() -eq $Folder.ReplicatedFolderName.ToUpper() -and
343 | $Line[2].ToUpper() -eq $InServer.ToUpper() -and
344 | $Line[3].ToUpper() -eq $OutServer.ToUpper())
345 | { $Found = "Yes"
346 | Break
347 | }
348 | }
349 | If ($Found -eq "No")
350 | { $DupeCheck += ,@($Group.ReplicationGroupName,$Folder.ReplicatedFolderName,$InServer,$OutServer)
351 | $DupeCheck += ,@($Group.ReplicationGroupName,$Folder.ReplicatedFolderName,$OutServer,$InServer)
352 | }
353 | Else
354 | { Continue
355 | }
356 |
357 | #Now check if partner server is available
358 | If (-not (Test-ServerAvailable $OutServer))
359 | { Continue
360 | }
361 |
362 | For ($i = 1; $i -le 2; $i++)
363 | { While ($(Get-Job -state "Running").count -ge $MaxThreads)
364 | { Write-Verbose "Thread count hit max of $MaxThreads, waiting for threads to finish..."
365 | Start-Sleep -Milliseconds 5000
366 | }
367 | Start-Job -ArgumentList $InServer,$OutServer,$Group,$Folder.ReplicatedFolderName -ScriptBlock {
368 | Param (
369 | [string]$InServer,
370 | [string]$OutServer,
371 | [object]$Group,
372 | [string]$ReplicationFolder
373 | )
374 |
375 | Function Get-WQLQuery
376 | { Param (
377 | [String]$WMIQuery,
378 | [String]$Computer
379 | )
380 |
381 | $ErrorCount = 0
382 | While ($ErrorCount -le 2)
383 | { $WMIObject = Get-WQLQueryObject -Namespace "root\MicrosoftDFS" -Query $WMIQuery -ComputerNameName $Computer
384 | If ($WMIObject)
385 | { $Status = "Success"
386 | $ErrorDetail = ""
387 | Break
388 | }
389 | Else
390 | { $ErrorCount ++
391 | $Status = "Error"
392 | $ErrorDetail = $Error[0]
393 | }
394 | }
395 | New-Object PSObject -Property @{
396 | Status = $Status
397 | Object = $WMIObject
398 | Error = $ErrorDetail
399 | }
400 | }
401 | $ErrorCount = 0
402 | $BacklogConnCount = 0
403 | $ErrorDetail = ""
404 |
405 | $WMIQuery = "SELECT * FROM DfsrReplicatedFolderConfig WHERE ReplicationGroupGUID = '" + $Group.ReplicationGroupGUID + "' AND ReplicatedFolderName = '" + $ReplicationFolder + "'"
406 | $WMIObject = Get-WQLQuery -Query $WMIQuery -ComputerName $InServer
407 | If ($WMIObject.Status -eq "Error")
408 | { $Status = "Error"
409 | $BacklogFiles = "WMI Error"
410 | $ErrorReport = "WMI Error on $InServer"
411 | $ErrorDetail = $WMIObject.Error
412 | }
413 | ElseIf ($WMIObject.Object.Enabled)
414 | { $WMIQuery = "SELECT * FROM DfsrReplicatedFolderConfig WHERE ReplicationGroupGUID = '" + $Group.ReplicationGroupGUID + "' AND ReplicatedFolderName = '" + $ReplicationFolder + "'"
415 | $WMIObject = Get-WQLQuery -Query $WMIQuery -ComputerName $OutServer
416 | If ($WMIObject.Status -eq "Error")
417 | { $Status = "Error"
418 | $BacklogFiles = "WMI Error"
419 | $ErrorReport = "WMI Error on $OutServer"
420 | $ErrorDetail = $WMIObject.Error
421 | }
422 | ElseIf ($WMIObject.Object.Enabled)
423 | { #Get the version vector of the partner
424 | $WMIQuery = "SELECT * FROM DfsrReplicatedFolderInfo WHERE ReplicationGroupGUID = '" + $Group.ReplicationGroupGUID + "' AND ReplicatedFolderName = '" + $ReplicationFolder + "'"
425 | $WMIObject = Get-WQLQuery -Query $WMIQuery -ComputerName $OutServer
426 | If ($WMIObject.Status -eq "Error")
427 | { $Status = "Error"
428 | $BacklogFiles = "WMI Error"
429 | $ErrorReport = "WMI Error occurred on $OutServer"
430 | $ErrorDetail = $WMIObject.Error
431 | }
432 | Else
433 | { $Vv = $WMIObject.Object.GetVersionVector().VersionVector
434 | #Get the backlog count from the partner
435 | $WMIQuery = "SELECT * FROM DfsrReplicatedFolderInfo WHERE ReplicationGroupGUID = '" + $Group.ReplicationGroupGUID + "' AND ReplicatedFolderName = '" + $ReplicationFolder + "'"
436 | $WMIObject = Get-WQLQuery -Query $WMIQuery -ComputerNameName $InServer
437 | If ($WMIObject.Status -eq "Error")
438 | { $Status = "WMI Error"
439 | $BacklogFiles = "WMI Error"
440 | $ErrorReport = "WMI Error occurred on $OutServer"
441 | $ErrorDetail = $WMIObject.Error
442 | }
443 | Else
444 | { $BacklogConnCount = $WMIObject.Object.GetOutboundBacklogFileCount($Vv).BacklogFileCount
445 | $arrFiles = $WMIObject.Object.GetOutboundBacklogFileIDRecords($Vv).BacklogIdRecords
446 | If ($BacklogConnCount -eq 0)
447 | { $Files = " "
448 | }
449 | Else
450 | { $Files = ""
451 | ForEach ($FileLine in $arrFiles)
452 | { $Files += $FileLine.FileName + "
"
453 | }
454 | }
455 | $Status = "Success"
456 | $BacklogFiles = $Files
457 | $ErrorReport = ""
458 | }
459 | }
460 | }
461 | Else
462 | { $Status = "Disabled"
463 | $BacklogFiles = "Disabled"
464 | $ErrorReport = "Folder $($Group.ReplicationGroupName)/$ReplicationFolder disabled on $OutServer"
465 | }
466 | }
467 | Else
468 | { $Status = "Disabled"
469 | $BacklogFiles = "Disabled"
470 | $ErrorReport = "Folder $($Group.ReplicationGroupName)/$ReplicationFolder disabled on $InServer"
471 | }
472 | New-Object PSObject -Property @{
473 | Status = $Status
474 | BacklogFiles = $BacklogFiles
475 | ErrorReport = $ErrorReport
476 | ErrorDetail = $ErrorDetail
477 | GroupObject = $Group.ReplicationGroupGUID
478 | Folder = $ReplicationFolder
479 | InServer = $InServer
480 | OutServer = $OutServer
481 | BacklogCount = $BacklogConnCount
482 | }
483 | } | Out-Null
484 | $InServer = $Connection.PartnerName
485 | $OutServer = $FileServer
486 | }
487 | }
488 | }
489 | }
490 | }
491 |
492 | #Wait for all the jobs to finish
493 | While (@(Get-Job -State "Running").count -gt 0)
494 | { Write-Verbose "All threads submitted, waiting for them to finish..."
495 | Start-Sleep -Milliseconds 5000
496 | }
497 |
498 | #Now read the job data into data
499 | Write-Verbose "All threads completed. Threads run: $(@(Get-Job).Count)"
500 | $Output = @()
501 | ForEach ($Job in Get-Job)
502 | { $ErrorCount = 0
503 | Do
504 | { Write-Verbose "Receiving job number: $($Job.Id)"
505 | $Result = Receive-Job $Job
506 | If ($Result -eq $null)
507 | { $ErrorCount ++
508 | If ($ErrorCount -eq 4)
509 | { Write-Verbose "Unable to retrieve job: $($Job.id)"
510 | $Result = "Fail"
511 | }
512 | Else
513 | { Write-Verbose "Problem retrieving job: $($Job.id), Retry: $ErrorCount"
514 | Start-Sleep -Seconds 3
515 | }
516 | }
517 | Else
518 | { $ErrorCount = 4
519 | }
520 | } While ($ErrorCount -lt 4)
521 | If ($Result -eq "Fail")
522 | { Remove-Job $Job
523 | Continue
524 | }
525 | #$GroupName = ($AllGroupNames | Where {$_ -match $Result.Group}).Split(":")
526 | $NewData += ,@($AllGroupNames[$Result.GroupObject],$Result.Folder,$Result.InServer,$Result.OutServer,$Result.BacklogCount,$Result.BacklogFiles)
527 | $Output += New-Object PSCustomObject -Property @{
528 | GroupName = $AllGroupNames[$Result.GroupObject]
529 | GroupGUID = $Result.GroupObject
530 | Folder = $Result.Folder
531 | InServer = $Result.InServer
532 | OutServer = $Result.OutServer
533 | Backlog = $Result.BacklogCount
534 | BackLogFiles = $Result.BacklogFiles
535 | }
536 | If ($Result.Status -ne "Success")
537 | { If ($AlertReport -notcontains $Result.ErrorReport)
538 | { $AlertReport += $Result.ErrorReport
539 | Write-Verbose $Result.ErrorDetail
540 | }
541 | }
542 | Remove-Job $Job
543 | }
544 |
545 | #Now add the new data
546 | ForEach ($GroupName in $AllGroupNames.Values)
547 | { #$GroupName = ($Group.Split(":"))[1]
548 | $UniqueReplFolders = $Output | Where {$_.GroupName -eq $GroupName} | Select Folder -Unique
549 | ForEach ($Folder in $UniqueReplFolders)
550 | { $BacklogCount = ($Output | Where {$_.GroupName -eq $GroupName -and $_.Folder -eq $Folder.Folder} | Measure-Object Backlog -sum).Sum
551 | $NewRGName = $Folder.Folder + ":" + $GroupName
552 | $Data += New-Object PSCustomObject -Property @{
553 | RFName = $Folder.Folder
554 | RGGUID = $NewRGName
555 | BacklogCount = $BacklogCount
556 | RunDate = $ScriptRunDate
557 | }
558 | }
559 | }
560 |
561 | If ($Data -eq $Null)
562 | { #Something went horribly wrong!
563 | Write-Verbose "No data found!"
564 | Throw
565 | }
566 | Else
567 | { #Delete oldest detail and debug files
568 | Get-ChildItem $OutputLocation\dfsdetails*.html | Where {$_.CreationTime -lt $SaveDate} | Remove-Item
569 | Get-ChildItem $DataLocation\debug*.log | Where {$_.CreationTime -lt $SaveDate} | Remove-Item
570 |
571 | ##
572 | ## Now build the detailed DFS monitor page
573 | ##
574 | Write-Verbose "--Creating detailed monitoring page..."
575 | $html = @()
576 | $html = "
Replication Group | Replication Folder | Sending Partner | Receiving Partner | Backlog | Files | `n" 593 | $TRDomain = "d1" 594 | $NewData = $NewData | Sort 595 | ForEach ($Line in $NewData) 596 | { If ($TRDomain -eq "d1") 597 | {$TRDomain = "d0" 598 | } 599 | Else 600 | { $TRDomain = "d1" 601 | } 602 | $html += "||
---|---|---|---|---|---|---|---|
" + $Line[0] + " | " 604 | $html += "" + $Line[1] + " | " 605 | $html += "" + $Line[2] + " | " 606 | $html += "" + $Line[3] + " | " 607 | If ($Line[4] -eq 0) 608 | { $html += "0 | " 609 | } 610 | Else 611 | { $html += "" + $Line[4] + " | " 612 | } 613 | If ($Line[5] -like "*Disabled*" -or $Line[5] -like "*WMI Error*") 614 | { $html += "" + $Line[5] + " | " 615 | } 616 | Else 617 | { $html += "" + $Line[5] + " | " 618 | } 619 | $html += "