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
All checks were successful
GitOps Automatic Deployment / GitOps Automatic Deployment (push) Successful in 8s
This commit is contained in:
@@ -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
|
|
||||||
```
|
|
||||||
Reference in New Issue
Block a user