--- tags: - DFS - Active Directory - PowerShell --- ## Purpose Report the DFS namespaces, folder targets, and replication configuration in the current Active Directory domain. Run the script in PowerShell with the DFSN and DFSR modules and permission to query the domain. ## Script You may want to put together a simple table report of the DFS namespaces, replication info, and target folders. You can run the following powershell script to generate a nice table-based report of the current structure of the DFS namespaces in your domain. ??? example "Powershell Reporting Script" ```powershell # Automatically detect current AD domain and use it as DFS prefix try { $Domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name $DomainPrefix = "\\$Domain" } catch { Write-Warning "Unable to detect domain automatically. Falling back to manual value." $DomainPrefix = "\\bunny-lab.io" } Import-Module DFSN -ErrorAction Stop Import-Module DFSR -ErrorAction Stop function Get-ServerNameFromPath { param([string]$Path) if ([string]::IsNullOrWhiteSpace($Path)) { return $null } if ($Path -like "\\*") { return ($Path -split '\\')[2] } return $null } function Get-Max3 { param([int[]]$Values) if (-not $Values) { return 0 } return (($Values | Measure-Object -Maximum).Maximum) } # Build: GroupName (lower) -> memberships[] $allGroups = Get-DfsReplicationGroup -ErrorAction SilentlyContinue $groupMembershipMap = @{} foreach ($g in $allGroups) { $ms = Get-DfsrMembership -GroupName $g.GroupName -ErrorAction SilentlyContinue $groupMembershipMap[$g.GroupName.ToLower()] = $ms } # Flatten all memberships for regex fallback $allMemberships = @() foreach ($arr in $groupMembershipMap.Values) { if ($arr) { $allMemberships += $arr } } $rows = New-Object System.Collections.Generic.List[psobject] # Enumerate namespace roots $roots = Get-DfsnRoot -ErrorAction Stop | Where-Object { $_.Path -like "$DomainPrefix\*" } Write-Host "DFS Namespace and Replication Overview" -ForegroundColor Cyan Write-Host "------------------------------------------------------`n" foreach ($root in $roots) { $rootPath = $root.Path $rootLeaf = ($rootPath -split '\\')[-1] $nsServers = @() $rootTargets = Get-DfsnRootTarget -Path $rootPath -ErrorAction SilentlyContinue foreach ($rt in $rootTargets) { $srv = Get-ServerNameFromPath $rt.TargetPath if ($srv) { $nsServers += $srv } } # Folders under this root $folders = Get-DfsnFolder -Path "$rootPath\*" -ErrorAction SilentlyContinue | Sort-Object Path foreach ($f in $folders) { $namespaceFull = $f.Path $leaf = ($f.Path -split '\\')[-1] # DFSN folder targets $targets = Get-DfsnFolderTarget -Path $f.Path -ErrorAction SilentlyContinue $targets = @($targets | Sort-Object { Get-ServerNameFromPath $_.TargetPath }) # ensure array # Map to DFSR group by naming; fallback to regex on ContentPath $candidateGroup = ((($rootPath -replace '^\\\\','') + '\' + $leaf).ToLower()) if ($groupMembershipMap.ContainsKey($candidateGroup)) { $msForFolder = $groupMembershipMap[$candidateGroup] } else { $escapedRootLeaf = [regex]::Escape($rootLeaf) $escapedLeaf = [regex]::Escape($leaf) $regex = "\\$escapedRootLeaf\\$escapedLeaf($|\\)" $msForFolder = $allMemberships | Where-Object { $_.ContentPath -imatch $regex } } $msForFolder = @($msForFolder) # normalize to array # Build aligned rows: one per target $targetLines = @() $replLines = @() foreach ($t in $targets) { $tServer = Get-ServerNameFromPath $t.TargetPath $targetLines += $t.TargetPath $msForServer = $null if ($msForFolder.Count -gt 0) { $msForServer = $msForFolder | Where-Object { $_.ComputerName -ieq $tServer } | Select-Object -First 1 } if ($msForServer -and $msForServer.ContentPath) { $replLines += $msForServer.ContentPath } else { $replLines += '' } } # Max line count for row expansion (PS 5.1 safe) $maxLines = Get-Max3 @($targetLines.Count, $replLines.Count, $nsServers.Count) for ($i = 0; $i -lt $maxLines; $i++) { # Precompute values (PS 5.1: no inline-if in hashtables) $nsVal = '' if ($i -eq 0) { $nsVal = $namespaceFull } $targetVal = '' if ($i -lt $targetLines.Count) { $targetVal = $targetLines[$i] } $replVal = '' if ($i -lt $replLines.Count) { $replVal = $replLines[$i] } $nsServerVal = '' if ($i -lt $nsServers.Count) { $nsServerVal = $nsServers[$i] } $row = [PSCustomObject]@{ 'Namespace' = $nsVal 'Member Folder Target(s)' = $targetVal 'Replication Locations' = $replVal 'Namespace Servers' = $nsServerVal } $rows.Add($row) | Out-Null } } } # Render as a PowerShell bordered grid with one-space left/right padding in every cell function Write-DfsGrid { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.IEnumerable]$Data, [string[]]$Columns = @('Namespace','Member Folder Target(s)','Replication Locations','Namespace Servers'), # Reasonable max widths; tune to your console (these are content+padding widths) [int[]]$MaxWidths = @(70, 70, 52, 30), [switch]$Ascii # use +-| instead of box-drawing if your console garbles Unicode ) # Ensure arrays align if ($MaxWidths.Count -lt $Columns.Count) { $pad = New-Object System.Collections.Generic.List[int] $pad.AddRange($MaxWidths) for ($i=$MaxWidths.Count; $i -lt $Columns.Count; $i++) { $pad.Add(40) } $MaxWidths = $pad.ToArray() } # Characters if ($Ascii) { $H = @{ tl='+'; tr='+'; bl='+'; br='+'; hz='-'; vt='|'; tj='+'; mj='+'; bj='+' } } else { # Box-drawing $H = @{ tl='┌'; tr='┐'; bl='└'; br='┘'; hz='─'; vt='│'; tj='┬'; mj='┼'; bj='┴' } try { [Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8 } catch {} } function TruncPad([string]$s, [int]$w) { if ($null -eq $s) { $s = '' } $s = $s -replace '\r','' -replace '\t',' ' if ($s.Length -le $w) { return $s.PadRight($w, ' ') } if ($w -le 1) { return $s.Substring(0, $w) } return ($s.Substring(0, $w-1) + '…') } # Materialize and compute widths (include one-space left/right padding for header and data) $rows = @($Data | ForEach-Object { $o = @{} foreach ($c in $Columns) { $o[$c] = [string]($_.$c) } [pscustomobject]$o }) $widths = @() for ($i=0; $i -lt $Columns.Count; $i++) { $col = $Columns[$i] # Start with header length including padding $max = (" " + $col + " ").Length foreach ($r in $rows) { $len = (" " + [string]$r.$col + " ").Length if ($len -gt $max) { $max = $len } } $widths += [Math]::Min($max, $MaxWidths[$i]) } # Line builders function DrawTop() { $line = $H.tl for ($i = 0; $i -lt $widths.Count; $i++) { $line += ($H.hz * $widths[$i]) if ($i -lt ($widths.Count - 1)) { $line += $H.tj } else { $line += $H.tr } } $line } function DrawMid([string[]]$Columns, [int[]]$widths, $H) { $line = $H.vt for ($i=0; $i -lt $widths.Count; $i++) { $line += TruncPad (" " + $Columns[$i] + " ") $widths[$i] $line += $H.vt } $line } function DrawSep() { $line = $H.vt for ($i=0; $i -lt $widths.Count; $i++) { $line += ($H.hz * $widths[$i]) $line += $H.vt } $line } function DrawHeaderSep() { $line = $H.vt for ($i=0; $i -lt $widths.Count; $i++) { $line += ($H.hz * $widths[$i]) $line += $H.vt } $line } function DrawBottom() { $line = $H.bl for ($i = 0; $i -lt $widths.Count; $i++) { $line += ($H.hz * $widths[$i]) if ($i -lt ($widths.Count - 1)) { $line += $H.bj } else { $line += $H.br } } $line } function DrawRow($r, [string[]]$Columns, [int[]]$widths, $H) { $line = $H.vt for ($i=0; $i -lt $widths.Count; $i++) { $val = [string]$r.($Columns[$i]) $line += TruncPad (" " + $val + " ") $widths[$i] $line += $H.vt } $line } # Render with group separators between namespaces (when the Namespace cell is non-empty) Write-Host (DrawTop) Write-Host (DrawMid -Columns $Columns -widths $widths -H $H) Write-Host (DrawHeaderSep) $first = $true foreach ($r in $rows) { if (-not $first -and ([string]$r.$($Columns[0])) ) { # Namespace changed → draw a separator Write-Host (DrawSep) } $first = $false Write-Host (DrawRow -r $r -Columns $Columns -widths $widths -H $H) } Write-Host (DrawBottom) } Write-DfsGrid -Data $rows ``` ## Related Documentation - [DFS Deployment](<../../../../deployments/Applications/Files and Collaboration/Windows Server/DFS Namespaces with Replication.md>) — Review the namespace and replication structure that this report inspects. - [Related Files and Collaboration Documentation](<../../../../reference/Applications/Files and Collaboration/index.md>) — Find the connected deployments, procedures, and references for this subject.