sdo sdo - 3 months ago 42
PowerShell Question

Powershell script to monitor DFSR backlog

Not a question. Just sharing. Apologies if this is wrong venue and/or method, but I've taken a lot from these forums and just wanted to give a little bit back in case anyone else like is trying to learn PS and do something even basically useful in PS. Apologies, but I had to run the script through a redactor. Anyway here it is:

###################################################################################################
# File: DFSR-check-3-show-backlog.ps1
# Desc: Simple monitor of DFSR backlog.
#
# Vers Date Who Description
# ---- ---- --- -----------
# v0.01 11-Aug-2016 sdo Initial draft, based on commands from BM.
# v0.02 12-Aug-2016 sdo Use "Out-String" to trim the blank lines of the table.
# v0.03 12-Aug-2016 sdo Extract count from verbose output if "100" items are returned.
# v0.04 12-Aug-2016 sdo Write to a log file.
# v0.05 12-Aug-2016 sdo Only display when different or every 100 entries.
# v0.06 12-Aug-2016 sdo Same layout and counter as other two scripts.
# v0.07 12-Aug-2016 sdo If the return backlog value is "", make it "0".
# v0.08 12-Aug-2016 sdo If display is "0,0", make it "-", which is easier to see activity.
# v0.09 12-Aug-2016 sdo Round anything > 100 to units of 100.
# v0.10 12-Aug-2016 sdo Use a function so that display updates less often.
###################################################################################################


###################################################################################################
# Functions...

Function fn_count( $p1 ) {
$return = [string]$p1
if ( $return -eq "" ) {Return "0"}
if ( fn_is_numeric( $return ) ) {
$number = [int]$return
switch ($number) {
{$_ -ge 100} {$return = $(([math]::Round($_ / 100)) * 100) ; $return=[string]$return+"+" ; Return $return }
{$_ -ge 1} {Return "<100" }
{$_ -eq 0} {Return "0" }
}
}
Return $return
}

Function fn_display_header {
""
" Disp Cnt AppAxx AppBxxxWorking AppCxxxx AppKxx AppTxxxFiles"
" ===== ===== ====== ============== ======== ====== ============"
}

Function fn_is_numeric( $p1 ) {
Return ( $( $p1.Trim() ) -Match "^[-+]?([0-9]*\.[0-9]+|[0-9]+\.?)$" )
}


###################################################################################################
# Main code...

$script_spec = $PSCommandPath
$script_path = [System.IO.Path]::GetDirectoryName( $script_spec )
$script_name = [System.IO.Path]::GetFileNameWithoutExtension( $script_spec )
$script_log = [System.IO.Path]::ChangeExtension( $script_spec, ".log" )

$Host.UI.RawUI.WindowTitle = $script_name

$line = ""
$z_count = 0
$z_lines = 0


fn_display_header
fn_display_header | Out-File $script_log -Append

while ($true) {
$prev = $line

$z_count++
if ( ($z_count % 100) -eq 0) {$prev = ""}


###################################################################################################
# Establish whether DFSR is up/enabled/available, or unknown...

$set = $( DFSRDiag backlog /rgname:AppAxx /rfname:AppAxx /sendingmember:ZZZPAAACSFT001 /receivingmember:ZZZPAAACSFT002 )
if ($LastExitCode -eq 0) {$AppAxx_Diag = "ok"} else {$AppAxx_Diag = "UNKNOWN"}

$set = $( DFSRDiag backlog /rgname:AppBxxxWorking /rfname:AppBxxxWorking /sendingmember:ZZZPAAACSFT001 /receivingmember:ZZZPAAACSFT002 )
if ($LastExitCode -eq 0) {$AppBxxxWorking_Diag = "ok"} else {$AppBxxxWorking_Diag = "UNKNOWN"}

$set = $( DFSRDiag backlog /rgname:AppCxxxx /rfname:AppCxxxx /sendingmember:ZZZPAAACSFT001 /receivingmember:ZZZPAAACSFT002 )
if ($LastExitCode -eq 0) {$AppCxxxx_Diag = "ok"} else {$AppCxxxx_Diag = "UNKNOWN"}

$set = $( DFSRDiag backlog /rgname:AppKxx /rfname:AppKxx /sendingmember:ZZZPAAACSFT001 /receivingmember:ZZZPAAACSFT002 )
if ($LastExitCode -eq 0) {$AppKxx_Diag = "ok"} else {$AppKxx_Diag = "UNKNOWN"}

$set = $( DFSRDiag backlog /rgname:AppTxxxFiles /rfname:AppTxxxFiles /sendingmember:ZZZPAAACSTA003 /receivingmember:ZZZPAAACSTA004 )
if ($LastExitCode -eq 0) {$AppTxxxFiles_Diag = "ok"} else {$AppTxxxFiles_Diag = "UNKNOWN"}


###################################################################################################
# Get DFSR back-log counts, from both sides... or report the "unknown" from the diagnostics...

if ($AppAxx_Diag -eq "ok") {
$verbose = $( $set = $(Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSFT001 -DestinationComputerName ZZZPAAACSFT002 -GroupName AppAxx -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppAxx_Display = $count

$verbose = $( $set = $(Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSFT002 -DestinationComputerName ZZZPAAACSFT001 -GroupName AppAxx -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppAxx_Display = $AppAxx_Display + "," + $count
} else {
$AppAxx_Display = $AppAxx_Diag
}


if ($AppBxxxWorking_Diag -eq "ok") {
$verbose = $( $set = $( Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSFT001 -DestinationComputerName ZZZPAAACSFT002 -GroupName AppBxxxWorking -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppBxxxWorking_Display = $count

$verbose = $( $set = $( Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSFT002 -DestinationComputerName ZZZPAAACSFT001 -GroupName AppBxxxWorking -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppBxxxWorking_Display = $AppBxxxWorking_Display + "," + $count
} else {
$AppBxxxWorking_Display = $AppBxxxWorking_Diag
}


if ($AppCxxxx_Diag -eq "ok") {
$verbose = $( $set = $( Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSFT001 -DestinationComputerName ZZZPAAACSFT002 -GroupName AppCxxxx -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppCxxxx_Display = $count

$verbose = $( $set = $( Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSFT002 -DestinationComputerName ZZZPAAACSFT001 -GroupName AppCxxxx -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppCxxxx_Display = $AppCxxxx_Display + "," + $count
} else {
$AppCxxxx_Display = $AppCxxxx_Diag
}


if ($AppKxx_Diag -eq "ok") {
$verbose = $( $set = $( Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSFT001 -DestinationComputerName ZZZPAAACSFT002 -GroupName AppKxx -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppKxx_Display = $count

$verbose = $( $set = $( Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSFT002 -DestinationComputerName ZZZPAAACSFT001 -GroupName AppKxx -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppKxx_Display = $AppKxx_Display + "," + $count
} else {
$AppKxx_Display = $AppKxx_Diag
}


if ($AppTxxxFiles_Diag -eq "ok") {
$verbose = $( $set = $( Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSTA003 -DestinationComputerName ZZZPAAACSTA004 -GroupName AppTxxxFiles -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppTxxxFiles_Display = $count

$verbose = $( $set = $( Get-DFSRBackLog -Verbose -SourceComputerName ZZZPAAACSTA004 -DestinationComputerName ZZZPAAACSTA003 -GroupName AppTxxxFiles -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
if ($count -ne "100") { $count = fn_count( $count ) } else {
$verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppTxxxFiles_Display = $AppTxxxFiles_Display + "," + $count
} else {
$AppTxxxFiles_Display = $AppTxxxFiles_Diag
}


###################################################################################################
# Build the table for display...

if ($AppAxx_Display -eq "0,0") {$AppAxx_Display = "-"}
if ($AppBxxxWorking_Display -eq "0,0") {$AppBxxxWorking_Display = "-"}
if ($AppCxxxx_Display -eq "0,0") {$AppCxxxx_Display = "-"}
if ($AppKxx_Display -eq "0,0") {$AppKxx_Display = "-"}
if ($AppTxxxFiles_Display -eq "0,0") {$AppTxxxFiles_Display = "-"}

$table = @()
$table = New-Object PSObject -Property @{
AppAxx = $AppAxx_Display
AppBxxxWorking = $AppBxxxWorking_Display
AppCxxxx = $AppCxxxx_Display
AppKxx = $AppKxx_Display
AppTxxxFiles = $AppTxxxFiles_Display
}

$line = ( $table | Format-Table -HideTableHeaders `
@{ expression = { $_.AppAxx } ; width = 15 } `
,@{ expression = { $_.AppBxxxWorking } ; width = 15 } `
,@{ expression = { $_.AppCxxxx } ; width = 15 } `
,@{ expression = { $_.AppKxx } ; width = 15 } `
,@{ expression = { $_.AppTxxxFiles } ; width = 15 } `
| Out-String ).Trim()


if ($line -ne $prev) {
$z_lines++

if ( ($z_lines % 10) -eq 0 ) {
fn_display_header
fn_display_header | Out-File $script_log -Append
}

$display = $(Get-Date -Format T) + " " + $("{0:F0}" -f $z_lines).PadLeft(5) + " " + $("{0:F0}" -f $z_count).PadLeft(5) + " " + $line

$display
$display | Out-File $script_log -Append
}

Start-Sleep -Seconds 10
}

exit

Answer

That's not really a "specific question", so I'm going to act as if this was CodeReview.StackExchange instead.

  • $count = fn_count( $count ) - function calls in PowerShell don't use () - unless you want to pass arrays as parameters - they just act like cmdlets. $count = fn_count $count
  • fn_is_numeric( $p1 ) - use [int]::TryParse() or cast to [int] and catch errors.
  • fn_count( $p1 ) - questionable Visual Basic style redundant naming style, old style parameter declaration, unhelpful parameter name
  • fn_count - what. This function has the word return 14 times in 11 lines. It casts to string, which you also do at call time, and it does a whole bunch of stuff to just squash the last two digits to 00, or say <100 or 0. And it's buggy - put 170 in and it will say 200+.
  • [System.IO.Path]::GetDirectoryName() and friends -> [IO.FileInfo] and .Directory
  • Masses of copy-pasted code -> 5 lines of setup and a ForEach loop
  • Use Hashtables for repeat data instead of lots of individually named variables
  • $("{0:F0}" -f $z_lines).PadLeft(5) appears to be doing "$z_lines".PadLeft(5)
  • The DFSRDiag calls where you have 10 lines of copy-paste and 5 custom variable names just to carry the text 'OK' or 'UNKNOWN' onto the next part of the script. Ditch that and merge with the next chunks of copy-pasta. They're bloated already but that can be cleaned up because...

This:

if ($?) {$count = [string]$set.Count} else {$count = "ERROR"}
if ($count -ne "ERROR") {
  if ($count -ne "100") { $count = fn_count( $count ) } else {
    $verbose = [string]$verbose ; $count = $verbose.Split(" ")[-1] ; $count = fn_count($count) } }
$AppAxx_Display = $count

This has the feel of "My copy-pasted code is getting long, I'm going to shorten it by REALLYCRAMMINGEVERYTHINGUPUNREADABLY; fn_count already casts $p1 to string so skip doing that here, fn_count passes non-numeric strings straight through so you can skip checking if it's "ERROR" and just feed it through whatever it is. Forget casting verbose to string (how could it not be one already?), forget lots of nested if/else with hard-to-read alignment, forget storing $count at all actually - throw one or other thing into fn_count and assign the result to AppAxx in one go.

  • The $table / format-table shennanigans: idk, but with the 5 lines of setup code for a ForEach loop, I think it can mostly be squashed.

  • This $Verbose = $( $set = $( Get-DFSRBackLog ... | Select FullPathname ) ) 4>&1 is uncommented voodoo. I don't even want to touch it.

Anyway, I dropped fn_is_numeric, setup a hashtable of the five servers and their replication members and turned the two big chunks of copy-paste into one loop, pulled out the double-Get-DFSRBacklog call into a function and merged fn_count into it, moved the "0,0" test to its source, trimmed the $table stuff by reusing the same hashtable I setup earlier, removed a heap of (), weird number formatting, double lines which could be one, and generally knocked 100+ lines off it, and fixed the 170->200+ bug:

###################################################################################################
#  File:    DFSR-check-3-show-backlog.ps1
#  Desc:    Simple monitor of DFSR backlog.
#
#  Vers   Date      Who Description
#  ----   ----      --- -----------
#  v0.01  11-Aug-2016   sdo Initial draft, based on commands from BM.
#  v0.02  12-Aug-2016   sdo Use "Out-String" to trim the blank lines of the table.
#  v0.03  12-Aug-2016   sdo Extract count from verbose output if "100" items are returned.
#  v0.04  12-Aug-2016   sdo Write to a log file.
#  v0.05  12-Aug-2016   sdo Only display when different or every 100 entries.
#  v0.06  12-Aug-2016   sdo Same layout and counter as other two scripts.
#  v0.07  12-Aug-2016   sdo If the return backlog value is "", make it "0".
#  v0.08  12-Aug-2016   sdo If display is "0,0", make it "-", which is easier to see activity.
#  v0.09  12-Aug-2016   sdo Round anything > 100 to units of 100.
#  v0.10  12-Aug-2016   sdo Use a function so that display updates less often.
###################################################################################################


# Functions...  ###################################################################################


Function Test-DFSRAtoB {
    Param($ComputerA, $ComputerB, $GroupName)

    # Get the backlog count, either directly or from the verbose log
    $verbose = $( $set = $(Get-DFSRBackLog -Verbose -SourceComputerName $ComputerA -DestinationComputerName $ComputerB -GroupName $GroupName -ErrorAction SilentlyContinue | Select FullPathname ) ) 4>&1
    if (!$?) { return "ERROR" }
    $Count = if ("100" -ne $set.Count ) { $set.Count } else { "$verbose".Split(" ")[-1] }

    # Round the backlog count to (0, <100, nn00+), or return it unchanged if that fails
    try {

        if     (100 -le $Count) { $Count -replace '..$', '00+' }
        elseif (  1 -le $Count) { "<100" } 
        elseif (  0 -eq $Count) { "0" }

    } catch { $Count }

}

Function Show-Header {
    ""
    "           Disp    Cnt  AppAxx          AppBxxxWorking  AppCxxxx        AppKxx          AppTxxxFiles"
    "          =====  =====  ======          ==============  ========        ======          ============"
}


###################################################################################################
# Main code...

$ScriptFileName = [System.IO.FileInfo]$PSCommandPath
$ScriptLog = Join-Path $ScriptFileName.Directory "$($ScriptFileName.BaseName).log"

$Host.UI.RawUI.WindowTitle = $ScriptFileName.Name

$line    = ""
$z_count = 0
$z_lines = 0


Show-Header
Show-Header| Out-File $ScriptLog -Append

$Replications = @(
    @{Name='AppAxx';         Member1='ZZZPAAACSFT001'; Member2='ZZZPAAACSFT002'},
    @{Name='AppBxxxWorking'; Member1='ZZZPAAACSFT001'; Member2='ZZZPAAACSFT002'},
    @{Name='AppCxxxx';       Member1='ZZZPAAACSFT001'; Member2='ZZZPAAACSFT002'},
    @{Name='AppKxx';         Member1='ZZZPAAACSFT001'; Member2='ZZZPAAACSFT002'},
    @{Name='AppTxxxFiles';   Member1='ZZZPAAACSTA003'; Member2='ZZZPAAACSTA004'}
)

while ($true) {

###################################################################################################
# Establish whether DFSR is up/enabled/available, or unknown...
# Get DFSR back-log counts, from both sides... or report the "unknown" from the diagnostics...

  $DisplayData = @{}

  $Replications | ForEach {

    $set = $( DFSRDiag backlog /rgname:$_.Name  /rfname:$_.Name  /sendingmember:$_.Member1   /receivingmember:$_.Member2 )
    if ($LastExitCode -eq 0) 
    {
        $AtoBResult = Test-DFSRAtoB $_.Member1 $_.Member2 $_.Name
        $BtoAResult = Test-DFSRAtoB $_.Member2 $_.Member1 $_.Name

        $Display = "$AtoBResult, $BtoAResult"
        $DisplayData[$_.Name] = if ($Display -eq "0,0") { "-" } else { $Display }
    } 
    else 
    {
        $DisplayData[$_.Name] = "UNKNOWN"
    }

  }

###################################################################################################
# Build the table for display...

  $prev = if (++$z_count % 100) { $line } else { "" }

  $line = ( [PSCustomObject]$DisplayData | Format-Table -HideTableHeaders `
     @{ expression = { $_.AppAxx         } ; width = 15 } `
    ,@{ expression = { $_.AppBxxxWorking } ; width = 15 } `
    ,@{ expression = { $_.AppCxxxx       } ; width = 15 } `
    ,@{ expression = { $_.AppKxx         } ; width = 15 } `
    ,@{ expression = { $_.AppTxxxFiles   } ; width = 15 } `
  | Out-String ).Trim()


  if ($line -ne $prev) {

    if ( (++$z_lines % 10) -eq 0 ) {
      Show-Header
      Show-Header | Out-File $ScriptLog -Append
    }

    ($display = "$(Get-Date -Format T)  $("$z_lines".PadLeft(5))  $("$z_count".PadLeft(5))  $line")
    $display | Out-File $ScriptLog -Append
  }

  Start-Sleep -Seconds 10
}

exit

What I didn't do is test it, so I doubt it will work as-is. Hopefully it's conceptually sound, though.