I decided to write Windows PowerShell Cmdlet (Advanced Function) to get report of configuration, status and health of Distributed File System Replication (DFS-R) in large enterprise environment (of course it is possible to use it for small deployment). I created this script because there are no native tools for such report and I did not find a single PowerShell script that would satisfy my needs.
Key concepts
- The script is in PowerShell but the script does not use native DFSR module so it is possible to use it on system with any version of the DFSR module or even without this module.
- The script was designed to never stop. That means that when there is an error on a single server or on a single replication group, folder or connection then script will log this error and continue with gathering data from other servers, connections, groups or folders.
- It is possible to target script to a single main (hub) DFS-R server and count backlogs in both directions (from source to destination and from destination to source). Of course it is possible to target all servers and do not count these reverse backlogs.
Examples
Show complete status in GUI
Get-RvDfsrReport -ComputerName fs0.ad.contoso.com, fs1.ad.contoso.com, fs2.ad.contoso.com | Out-GridView
Get full DFS-R report for one defined server and for one defined replication group and replicated folder
Get-RvDfsrReport ` -ComputerName BIGTARGET ` -GroupName 'USA\FILESERV44*' ` -FolderName 'Da*' ` -BacklogCount BothDirections ` -BacklogCountError 10000 ` -Remoting CIM -Verbose <# Output: DestinationComputerName : BIGTARGET SourceComputerName : FILESERV44 GroupName : USA\FILESERV44\D GroupGuid : AAAAACCC-4444-5555-1100-1111100000AA GroupDn : CN=USA\FILESERV44\D,CN=DFSR-GlobalSettings,CN=System,DC=contoso,DC=com GroupIsClustered : False GroupScheduleInUtc : True FolderGuid : 99999999-1111-2222-0011-2222266CCCCC FolderName : Data FolderRootPath : X:\FILESERV44\Data FolderEnabled : True FolderReadOnly : True FolderConflictSizeInMB : 4096 FolderStagingSizeInMB : 8192 FolderFileFilter : ~*, *.bak, *.tmp FolderState : Initial Sync BacklogCount : 286917 BacklogReverseCount : 53603 BacklogCountSummary : 340520 ReplicationStatus : False Status : True Error : False ErrorDescription : #>
Get DFS-R groups, folders and connections in a second (skip backlog calculations) for the local server and export it to CSV
Get-RvDfsrReport -BacklogCount Skip -Remoting CIM | Export-Csv ` -Path 'C:\Temp\My report.csv' ` -Delimiter "`t" ` -Encoding UTF8 ` -NoTypeInformation
Display DFS-R report for multiple servers in a console
Get-RvDfsrReport -ComputerName fs0.ad.contoso.com, fs1.ad.contoso.com, fs2.ad.contoso.com | Format-Table -Property GroupName, FolderName, FolderState, BacklogCount, Status
Code
Function Get-RvDfsrReport { <# .SYNOPSIS Get DFS-R status, health and backlogs. Cmdlet (advanced function) is designed to never stop even when it hit an error so the cmdlet will always produce full report with logged errors. .DESCRIPTION Developer Developer: Rudolf Vesely, http://rudolfvesely.com/ Copyright (c) Rudolf Vesely. All rights reserved License: Free for private use only "RV" are initials of the developer's name Rudolf Vesely and distingue names of Rudolf Vesely's cmdlets from the other cmdlets. Description Get DFS-R status, health and backlogs. Cmdlet (advanced function) is designed to never stop even when it hit an error so the cmdlet will always produce full report with logged errors. Requirements Developed and tested using PowerShell 4.0. .PARAMETER ComputerName Name of the remote server to get report. If not set then all groups are queried .PARAMETER GroupName Name of the group to get report. It is possible to use wildcards (for example MyGroup*). If not set then all groups will be queried. .PARAMETER FolderName Name of the group to get report. It is possible to use wildcards (for example MyGroup*). If not set then all folders will be queried. .PARAMETER BacklogCount Possibilities Skip Fastest method of generating report. Get Backlogs are calculated for every folder. BothDirections Backlogs are calculated for every folder in both directorions. .PARAMETER BacklogCountError Maximum number of backlogs that is evaluated as normal state. .PARAMETER Remoting Possibilities WMI Old method using Get-WmiObject that is compatible with PowerShell 2.0 on Windows Server 2012 R2. CIM More efficient method using Get-CimInstance that is compatible with PowerShell 3.0 on Windows Server 2012 RTM and with later versions. .EXAMPLE 'Get full DFS-R report for one defined server and for one defined replication group and replicated folder' Get-RvDfsrReport ` -ComputerName BIGTARGET ` -GroupName 'USA\FILESERV44*' ` -FolderName 'Da*' ` -BacklogCount BothDirections ` -BacklogCountError 10000 ` -Remoting CIM -Verbose Output: DestinationComputerName : BIGTARGET SourceComputerName : FILESERV44 GroupName : USA\FILESERV44\D GroupGuid : AAAAACCC-4444-5555-1100-1111100000AA GroupDn : CN=USA\FILESERV44\D,CN=DFSR-GlobalSettings,CN=System,DC=contoso,DC=com GroupIsClustered : False GroupScheduleInUtc : True FolderGuid : 99999999-1111-2222-0011-2222266CCCCC FolderName : Data FolderRootPath : X:\FILESERV44\Data FolderEnabled : True FolderReadOnly : True FolderConflictSizeInMB : 4096 FolderStagingSizeInMB : 8192 FolderFileFilter : ~*, *.bak, *.tmp FolderState : Initial Sync BacklogCount : 286917 BacklogReverseCount : 53603 BacklogCountSummary : 340520 ReplicationStatus : False Status : True Error : False ErrorDescription : .EXAMPLE 'Get DFS-R groups, folders and connections in a second (skip backlog calculations) for the local server and export it to CSV' Get-RvDfsrReport -BacklogCount Skip -Remoting CIM | Export-Csv ` -Path 'C:\Temp\My report.csv' ` -Delimiter "`t" ` -Encoding UTF8 ` -NoTypeInformation .EXAMPLE 'Display DFS-R report for multiple servers in a console' Get-RvDfsrReport -ComputerName fs0.ad.contoso.com, fs1.ad.contoso.com, fs2.ad.contoso.com | Format-Table -Property GroupName, FolderName, FolderState, BacklogCount, Status .INPUTS .OUTPUTS System.Management.Automation.PSCustomObject .LINK https://techstronghold.com/ #> [CmdletBinding( DefaultParametersetName = 'ComputerName', SupportsShouldProcess = $true, PositionalBinding = $false, HelpURI = 'https://techstronghold.com/', ConfirmImpact = 'Medium' )] Param ( [Parameter( Mandatory = $false, Position = 0, ParameterSetName = 'ComputerName' )] [ValidateLength(1, 255)] [string[]]$ComputerName = '.', [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [ValidateLength(1, 255)] [string]$GroupName = '*', [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [ValidateLength(1, 255)] [string]$FolderName = '*', [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [ValidateSet( 'Skip', 'Get', 'BothDirections' )] [string]$BacklogCount = 'Get', [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [int64]$BacklogCountError = 100, [Parameter( Mandatory = $false # Position = , # ParameterSetName = '' )] [ValidateSet( 'WMI', 'CIM' )] [string]$Remoting = 'WMI' ) Begin { # Configurations $ErrorActionPreference = 'Stop' if ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' } Set-PSDebug -Strict Set-StrictMode -Version Latest #region Functions Function Get-RvDfsrWmiOrCim { [CmdletBinding( DefaultParametersetName = 'ClassName', SupportsShouldProcess = $true, PositionalBinding = $false, HelpURI = 'https://techstronghold.com/', ConfirmImpact = 'Medium' )] Param ( [Parameter( Mandatory = $true, Position = 0, ParameterSetName = 'ClassName' )] [AllowNull()] [string]$ClassName, [Parameter( Mandatory = $false # Position = 0, # ParameterSetName = '' )] [AllowNull()] [string]$Filter, [Parameter( Mandatory = $false # Position = 0, # ParameterSetName = '' )] [AllowNull()] [string]$ComputerName = '.' ) Begin { $error = $false $errorDescription = '' $parametersAndArguments = @{} # Local (NetBIOS or FQDN) or remote device if ($ComputerName -and $ComputerName -ne '.' -and ($ComputerName -replace '\..*', '') -ne $env:COMPUTERNAME) { $parametersAndArguments.Add('ComputerName', $ComputerName) } # Filter if ($Filter) { $parametersAndArguments.Add('Filter', $Filter) } } Process { try { if ($Remoting -eq 'WMI') { $output = Get-WmiObject ` -Namespace ROOT\MicrosoftDfs ` -Class $ClassName ` -ErrorAction Stop ` @parametersAndArguments } else { $output = Get-CimInstance ` -Namespace root/MicrosoftDfs ` -ClassName $ClassName ` -ErrorAction Stop ` @parametersAndArguments } } catch { $output = $null $error = $true $errorDescription = 'Device: {0}; WMI class {0}: Exception: {1}' -f $computerNameItem, $ClassName, $_.Exception.Message Write-Warning -Message $errorDescription } if (!$output) { $output = $null $error = $true $errorDescription = 'Device: {0}; WMI class {0}: No data' -f $computerNameItem, $ClassName Write-Warning -Message $errorDescription } # Return [PsCustomObject]@{ Output = $output Error = $error ErrorDescription = $errorDescription } } End { } } Function Get-RvDfsrReportOutput { [CmdletBinding( DefaultParametersetName = 'Data', SupportsShouldProcess = $true, PositionalBinding = $false, HelpURI = 'https://techstronghold.com/', ConfirmImpact = 'Medium' )] Param ( [Parameter( Mandatory = $true, Position = 0, ParameterSetName = 'Data' )] $Data ) Begin { } Process { $output = $Data if ($Data.ErrorDescription) { $output.Status = $false $output.Error = $true $output.ErrorDescription = $Data.ErrorDescription -join '; ' } else { $output.ErrorDescription = $null } # Return $output } End { } } #endregion # Modify wildcards to WQL if ($GroupName -and $GroupName -ne '*') { $filterGroupName = $GroupName.Replace('*', '%').Replace('?', '_').Replace('\', '\\') } else { $filterGroupName = $null } if ($FolderName -and $FolderName -ne '*') { $filterFolderName = $FolderName.Replace('*', '%').Replace('?', '_').Replace('\', '\\') } else { $filterFolderName = $null } } Process { foreach ($computerNameItem in $ComputerName) { <# Variables #> $output = [PsCustomObject]@{ DestinationComputerName = $computerNameItem SourceComputerName = $null GroupName = $null GroupGuid = $null GroupDn = $null GroupIsClustered = $null GroupScheduleInUtc = $null FolderGuid = $null FolderName = $null FolderRootPath = $null FolderEnabled = $null FolderReadOnly = $null FolderConflictSizeInMB = $null FolderStagingSizeInMB = $null FolderFileFilter = $null # Normal, Initial replication, etc. FolderState = $null # Backlog: Source -> Destination BacklogCount = $null # Backlog: Destination -> Source BacklogReverseCount = $null # Backlog: Sum of all directions BacklogCountSummary = $null # If $false then replication is not in normal state or backlog is high ReplicationStatus = $null # $false on any error during gtrial to get and process data Status = $true # $true on any error during gtrial to get and process data Error = $false ErrorDescription = @() } <# Groups #> $parametersAndArguments = @{} if ($filterGroupName) { $parametersAndArguments.Add('Filter', ("ReplicationGroupName LIKE '{0}'" -f $filterGroupName)) } $groupConfigurationItems = Get-RvDfsrWmiOrCim ` -ClassName 'DfsrReplicationGroupConfig' ` -ComputerName $computerNameItem ` @parametersAndArguments if ($groupConfigurationItems.Error) { Write-warning -Message 'Continue with another device' $output.ErrorDescription += $groupConfigurationItems.ErrorDescription # Return Get-RvDfsrReportOutput -Data $output } else { foreach ($groupConfigurationItem in $groupConfigurationItems.Output) { <# Group configurations #> try { Write-Verbose -Message (' - Group name: {0}' -f $groupConfigurationItem.ReplicationGroupName) $output.GroupName = $groupConfigurationItem.ReplicationGroupName $output.GroupGuid = $groupConfigurationItem.ReplicationGroupGuid $output.GroupDn = $groupConfigurationItem.ReplicationGroupDn.Replace('\\', '\') $output.GroupIsClustered = $groupConfigurationItem.IsClustered $output.GroupScheduleInUtc = $groupConfigurationItem.DefaultScheduleInUtc } catch { $message = 'Cannot get group configuration' Write-warning -Message $message $output.ErrorDescription += $message } <# Folders #> $parametersAndArguments = @{} if ($filterFolderName) { $filterAdd = " AND ReplicatedFolderName LIKE '{0}'" -f $filterFolderName } else { $filterAdd = '' } $folderConfigurationItems = Get-RvDfsrWmiOrCim ` -ClassName 'DfsrReplicatedFolderConfig' ` -Filter ("ReplicationGroupGuid = '{0}'{1}" -f $groupConfigurationItem.ReplicationGroupGuid, $filterAdd) ` -ComputerName $computerNameItem $connectionConfigurationItems = Get-RvDfsrWmiOrCim ` -ClassName 'DfsrConnectionConfig' ` -Filter ("Inbound = 'True' AND ReplicationGroupGuid = '{0}'" -f $groupConfigurationItem.ReplicationGroupGuid) ` -ComputerName $computerNameItem if ($folderConfigurationItems.Error) { Write-warning -Message 'Continue with another group' $output.ErrorDescription += $folderConfigurationItems.Error # Return Get-RvDfsrReportOutput -Data $output } else { foreach ($folderConfigurationItem in $folderConfigurationItems.Output) { <# Folder configurations #> try { Write-Verbose -Message (' - Folder name: {0}' -f $folderConfigurationItem.ReplicatedFolderName) $output.FolderName = $folderConfigurationItem.ReplicatedFolderName $output.FolderGuid = $folderConfigurationItem.ReplicatedFolderGuid $output.FolderRootPath = $folderConfigurationItem.RootPath $output.FolderEnabled = $folderConfigurationItem.Enabled $output.FolderReadOnly = $folderConfigurationItem.ReadOnly $output.FolderConflictSizeInMB = $folderConfigurationItem.ConflictSizeInMb $output.FolderStagingSizeInMB = $folderConfigurationItem.StagingSizeInMb $output.FolderFileFilter = $folderConfigurationItem.FileFilter } catch { $message = 'Cannot get folder configuration' Write-Warning -Message $message $output.ErrorDescription += $message } <# Folder information #> $folderInformationItem = Get-RvDfsrWmiOrCim ` -ClassName 'DfsrReplicatedFolderInfo' ` -Filter ("ReplicationGroupGUID = '{0}' AND ReplicatedFolderGuid = '{1}'" -f $groupConfigurationItem.ReplicationGroupGuid, $folderConfigurationItem.ReplicatedFolderGuid) ` -ComputerName $computerNameItem if ($folderInformationItem.Error) { $message = 'Cannot get folder information' Write-Warning -Message $message $output.ErrorDescription += $message } else { try { switch ($folderInformationItem.Output.State) { 0 { $output.FolderState = 'Uninitialized'; break } 1 { $output.FolderState = 'Initialized'; break } 2 { $output.FolderState = 'Initial Sync'; break } 3 { $output.FolderState = 'Auto Recovery'; break } 4 { $output.FolderState = 'Normal'; break } 5 { $output.FolderState = 'In Error'; break } Default { $output.FolderState = 'Unknown' } } if ($folderInformationItem.Output.State -eq 4) { $output.ReplicationStatus = $true } else { $output.ReplicationStatus = $false } } catch { $message = 'Cannot get folder information' Write-warning -Message $message $output.ErrorDescription += $message } } <# Connections #> if ($connectionConfigurationItems.Error) { Write-warning -Message 'Continue with another group' $output.ErrorDescription += $connectionConfigurationItems.ErrorDescription # Return Get-RvDfsrReportOutput -Data $output } else { foreach ($connectionConfigurationItem in $connectionConfigurationItems.Output) { $partnerComputerName = $connectionConfigurationItem.PartnerName Write-Verbose -Message (' - Connection: {0} < - {1}' -f $computerNameItem, $partnerComputerName) $output.SourceComputerName = $partnerComputerName <# Backlog count #> if ($BacklogCount -ne 'Skip') { $folderInformationPartnerItem = Get-RvDfsrWmiOrCim ` -ClassName 'DfsrReplicatedFolderInfo' ` -Filter ("ReplicationGroupGUID = '{0}' AND ReplicatedFolderName = '{1}'" -f $groupConfigurationItem.ReplicationGroupGuid, $folderConfigurationItem.ReplicatedFolderName) ` -ComputerName $partnerComputerName if ($folderInformationPartnerItem.Error) { Write-warning -Message 'Continue with another connection' $output.ErrorDescription += $folderInformationPartnerItem.ErrorDescription # Return Get-RvDfsrReportOutput -Data $output } else { <# Direction: Normal #> $errorBacklogDescription = $null try { $versionVector = $folderInformationPartnerItem.Output.GetVersionVector().VersionVector } catch { $errorBacklogDescription = 'Exception during trial to get version vector to get number of backlogs: {0}' -f $_.Exception.Message Write-Warning -Message $errorBacklogDescription } if (!$errorBacklogDescription) { try { $output.BacklogCount = [int64]$folderInformationItem.Output.GetOutboundBacklogFileCount($versionVector).BacklogFileCount } catch { $errorBacklogDescription = 'Exception during trial to get number of backlogs: {0}' -f $_.Exception.Message Write-Warning -Message $errorBacklogDescription } } if ($errorBacklogDescription) { $output.ErrorDescription += $errorBacklogDescription } <# Direction: Reverse #> if ($BacklogCount -eq 'BothDirections') { $errorBacklogDescription = $null try { $versionVector = $folderInformationItem.Output.GetVersionVector().VersionVector } catch { $errorBacklogDescription = 'Exception during trial to get version vector to get number of backlogs in reverse direction: {0}' -f $_.Exception.Message Write-Warning -Message $errorBacklogDescription } if (!$errorBacklogDescription) { try { $output.BacklogReverseCount = [int64]$folderInformationPartnerItem.Output.GetOutboundBacklogFileCount($versionVector).BacklogFileCount } catch { $errorBacklogDescription = 'Exception during trial to get number of backlogs in reverse direction: {0}' -f $_.Exception.Message Write-Warning -Message $errorBacklogDescription } } if ($errorBacklogDescription) { $output.ErrorDescription += $errorBacklogDescription } } <# Direction: Summary #> if ($output.BacklogCount -ne $null -and $output.BacklogReverseCount -ne $null) { $output.BacklogCountSummary = $output.BacklogCount + $output.BacklogReverseCount } <# Replication status #> if ($output.BacklogCount -gt $BacklogCountError -or $output.BacklogReverseCount -gt $BacklogCountError) { $output.ReplicationStatus = $false } } } # Return Get-RvDfsrReportOutput -Data $output } } } } } } } } End { } }
5 responses to “PowerShell advanced function (cmdlet) to get configuration, health, state and backlogs report for DFS-R”
Rudolf – I’m testing this script against a Server 2012 box and get no output (Nor any errors.) Just immediately takes me back to the powershell prompt. I’m running this under PS 3.0. I tried your example against the local server and nothing – Get-RvDfsrReport -ComputerName fs0.ad.contoso.com
Hi Rudolf, excellent script thank you very much for sharing. @Pat this is function use it as module if want to use from any PS prompt
Steve, thanks – yes, finally figured that out. A bit new to PS!
Hi, I just tested the script and it works fine … But I tested it on a DFSR that I know was in trouble and it still gave a Status = True although the dsfrdiag told me it was in trouble. How can we amend the script to reflect this ?
Just trying out the script on a Windows 2012 R2 server and I’m getting back errors where it seems to be mapping the server name to the WMI class : Device: servername; WMI class servername: No data Any ideas ?