Update Workflows/Windows/Windows Server/Roles/DFS/Creating and Configuring DFS Namespaces with Replication.md
All checks were successful
GitOps Automatic Deployment / GitOps Automatic Deployment (push) Successful in 8s

This commit is contained in:
2025-10-14 06:27:12 -06:00
parent 5a166255f2
commit f854330de7

View File

@@ -124,263 +124,266 @@ In the Replication wizard that appears after about a minute, you can configure t
### Checking DFS Status ### Checking DFS Status
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. 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.
```powershell ??? example "Powershell Reporting Script"
[CmdletBinding()] ```powershell
param( # Automatically detect current AD domain and use it as DFS prefix
[string]$DomainPrefix = "\\bunny-lab.io" # Adjust if Different try {
) $Domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name
$DomainPrefix = "\\$Domain"
Import-Module DFSN -ErrorAction Stop } catch {
Import-Module DFSR -ErrorAction Stop Write-Warning "Unable to detect domain automatically. Falling back to manual value."
$DomainPrefix = "\\bunny-lab.io"
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 Import-Module DFSN -ErrorAction Stop
$targetLines = @() Import-Module DFSR -ErrorAction Stop
$replLines = @()
foreach ($t in $targets) { function Get-ServerNameFromPath {
$tServer = Get-ServerNameFromPath $t.TargetPath param([string]$Path)
$targetLines += $t.TargetPath 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)
}
$msForServer = $null # Build: GroupName (lower) -> memberships[]
if ($msForFolder.Count -gt 0) { $allGroups = Get-DfsReplicationGroup -ErrorAction SilentlyContinue
$msForServer = $msForFolder | Where-Object { $_.ComputerName -ieq $tServer } | Select-Object -First 1 $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 }
} }
if ($msForServer -and $msForServer.ContentPath) { $replLines += $msForServer.ContentPath } else { $replLines += '' }
}
# Max line count for row expansion (PS 5.1 safe) # Folders under this root
$maxLines = Get-Max3 @($targetLines.Count, $replLines.Count, $nsServers.Count) $folders = Get-DfsnFolder -Path "$rootPath\*" -ErrorAction SilentlyContinue | Sort-Object Path
for ($i = 0; $i -lt $maxLines; $i++) { foreach ($f in $folders) {
$namespaceFull = $f.Path
$leaf = ($f.Path -split '\\')[-1]
# Precompute values (PS 5.1: no inline-if in hashtables) # DFSN folder targets
$nsVal = '' $targets = Get-DfsnFolderTarget -Path $f.Path -ErrorAction SilentlyContinue
if ($i -eq 0) { $nsVal = $namespaceFull } $targets = @($targets | Sort-Object { Get-ServerNameFromPath $_.TargetPath }) # ensure array
$targetVal = '' # Map to DFSR group by naming; fallback to regex on ContentPath
if ($i -lt $targetLines.Count) { $targetVal = $targetLines[$i] } $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
$replVal = '' # Build aligned rows: one per target
if ($i -lt $replLines.Count) { $replVal = $replLines[$i] } $targetLines = @()
$replLines = @()
$nsServerVal = '' foreach ($t in $targets) {
if ($i -lt $nsServers.Count) { $nsServerVal = $nsServers[$i] } $tServer = Get-ServerNameFromPath $t.TargetPath
$targetLines += $t.TargetPath
$row = [PSCustomObject]@{ $msForServer = $null
'Namespace' = $nsVal if ($msForFolder.Count -gt 0) {
'Member Folder Target(s)' = $targetVal $msForServer = $msForFolder | Where-Object { $_.ComputerName -ieq $tServer } | Select-Object -First 1
'Replication Locations' = $replVal
'Namespace Servers' = $nsServerVal
}
$rows.Add($row) | Out-Null
}
}
}
# Render as a PowerShell table (multi-line cells wrap)
#$rows | Format-Table -AutoSize -Wrap
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
[int[]]$MaxWidths = @(50, 60, 50, 28),
[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
$rows = @($Data | ForEach-Object {
# Build a string-only hashtable per row for consistent measurement
$o = @{}
foreach ($c in $Columns) { $o[$c] = [string]($_.$c) }
[pscustomobject]$o
})
$widths = @()
for ($i=0; $i -lt $Columns.Count; $i++) {
$col = $Columns[$i]
$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
} }
} if ($msForServer -and $msForServer.ContentPath) { $replLines += $msForServer.ContentPath } else { $replLines += '' }
$line }
}
function DrawMid() { # Max line count for row expansion (PS 5.1 safe)
$line = $H.vt $maxLines = Get-Max3 @($targetLines.Count, $replLines.Count, $nsServers.Count)
for ($i=0; $i -lt $widths.Count; $i++) {
$line += TruncPad $Columns[$i] $widths[$i] for ($i = 0; $i -lt $maxLines; $i++) {
$line += $H.vt
} # Precompute values (PS 5.1: no inline-if in hashtables)
$line $nsVal = ''
} if ($i -eq 0) { $nsVal = $namespaceFull }
function DrawSep() {
$line = $H.vt $targetVal = ''
for ($i=0; $i -lt $widths.Count; $i++) { if ($i -lt $targetLines.Count) { $targetVal = $targetLines[$i] }
$line += ($H.hz * $widths[$i])
$line += $H.vt $replVal = ''
} if ($i -lt $replLines.Count) { $replVal = $replLines[$i] }
$line
} $nsServerVal = ''
function DrawHeaderSep() { if ($i -lt $nsServers.Count) { $nsServerVal = $nsServers[$i] }
$line = $H.vt
for ($i=0; $i -lt $widths.Count; $i++) { $row = [PSCustomObject]@{
$line += ($H.hz * $widths[$i]) 'Namespace' = $nsVal
$line += $H.vt 'Member Folder Target(s)' = $targetVal
} 'Replication Locations' = $replVal
$line 'Namespace Servers' = $nsServerVal
}
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
} }
$rows.Add($row) | Out-Null
}
} }
$line
}
function DrawRow($r) {
$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) # Render as a PowerShell bordered grid with one-space left/right padding in every cell
Write-Host (DrawTop) function Write-DfsGrid {
Write-Host (DrawMid) [CmdletBinding()]
Write-Host (DrawHeaderSep) param(
[Parameter(Mandatory)]
[System.Collections.IEnumerable]$Data,
$first = $true [string[]]$Columns = @('Namespace','Member Folder Target(s)','Replication Locations','Namespace Servers'),
foreach ($r in $rows) {
if (-not $first -and ([string]$r.$($Columns[0])) ) { # Reasonable max widths; tune to your console (these are content+padding widths)
# Namespace changed → draw a heavy-ish separator [int[]]$MaxWidths = @(70, 70, 52, 30),
Write-Host (DrawSep)
[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)
} }
$first = $false
Write-Host (DrawRow $r)
}
Write-Host (DrawBottom) Write-DfsGrid -Data $rows
} ```
Write-DfsGrid -Data $rows
```