Find out what your SYSVOL on DFSR is doing

(Updated 16-9-2016: reference to new post, updated the script with better error checking and a bugfix)

This is part 1; continue at part 2 here:

All of you out there should be running your SYSVOL on DFSR by now. This is the new default since Windows Server 2008 from the previous decade. If you started your domain on Windows Server 2003 or 2000 and you did not upgrade FRS to DFSR yet, check here on why you have to migrate, and how to do it.

Besides the regular monitoring of DFSR replication, it can be useful to have an idea of what is actually happening. When a replication conflict happens or a file is deleted, DFSR stores that file in case you want to retrieve it. Of course, it maintains an administration for this, and you can use Powershell to look at this -- if Powershell runs on Windows Server 2012 R2 for the required DFSR commandlets.

So I wrote a script to do this. The script (updated 16-9-2016) itself is on the TechNet Gallery, here: This is an example of its output:

As you can see, my SYSVOL here is pretty boring. As it should be.

I made some forward looking assumptions for this script:

  • SYSVOL is running in DFSR mode.
  • All DCs should be online and accessible (limited error checking)
  • At least one DC has AD web services running. In practice, this means one DC with 2008 R2 or higher.
  • The local machine should be 2012 R2 or higher; this enables the required PowerShell commandlets.
  • AD management tools installed on the local machine
  • DFS management tools installed on the local machine.

The interesting part of the script is this (cannot be run on its own, see the previous link to download the actual script)

Get-ADDomainController -Server $domainname -filter * |
Sort-Object -Property hostname |
Get-SysvolPreservedFiles |
ForEach-Object {
if (-not ($dcnames -contains $_.hostname )) { $dcnames += $_.hostname }
if (-not ($reasons -contains $_.preservedreason)) { $reasons += $_.preservedreason }
$stats["$($_.hostname)|$($_.preservedreason)"] += 1

# Powershell rule #2: output objects, not formatted strings.
Foreach ($hostname in $dcnames)
$objDC = New-Object -TypeName PSObject -Property @{dcname = [string]$hostname }
foreach ($preservedreason in $reasons)
$count = $stats["${hostname}|${preservedreason}"]
$objDC | Add-Member -MemberType NoteProperty -Name $preservedreason -Value $(if ($count) { $count } else { 0 })
# dump the statistics in the output pipeline

I needed a way to build statics for all DCs in the domain, and all possible file states -- both of which are not known beforehand. Logically, this means I need a dynamic two-dimensional hash, which does not exist in Powershell. I used the creative but ugly trick to concatenate the keys to maintain the hash. During the output phase I reconstruct this key to get back at the statistics, and make sure to output the data as an object and not as a formatted string.

Please see part 2 of this blog for real-world data, showing some interesting patterns: