Public/Network/Show-Progress.ps1

function Show-Progress {
  # .SYNOPSIS
  # Show progress as items pass through a section of the pipline
  # .DESCRIPTION
  # This function allows you to show progress from the pipeline.
  # It's intentionally written for efficiency/speed so some compromises on readability were made
  # .PARAMETER InputObject
  # The items on the pipeline being processed. Can also accept this input from the pipeline, which is how this parameter
  # is normally used.
  # .PARAMETER Activity
  # A decription of the activity being measured.
  # .PARAMETER UpdatePercentage
  # The percentage of time to update the progress bar. Write-Progress is a slow cmdlet so this is used for performance
  # reasons with larger data sets. Defaults to 1.
  # .PARAMETER PassThru
  # Controls whether data is passed along the pipeline. Normally this switch should be used.
  # .PARAMETER Id
  # Allows you to have multiple progress bars running simultaneously. Defaults to 1.
  # .PARAMETER SecondsRemaining
  # Switch that will control the display of estimated number of seconds remaining based upon current progress.
  # .EXAMPLE
  # # This runs through the numbers 1 through 1000 and displays a progress bar based on how many have gone through the pipeline
  # 1..1000 | Show-Progress
  # .EXAMPLE
  # # Showing progress with a specific activity message and only updating at 10%, 20% etc
  # 1..10000 | Show-Progress -Activity "doin stuff" -UpdatePercentage 10
  # .EXAMPLE
  # # Showing progress displaying estimated seconds remaining.
  # 1..50 | show-progress -PassThru -SecondsRemaining | foreach { start-sleep -Milliseconds (get-random -min 100 -max 1000) }
  # .EXAMPLE
  # 1..1000 | show-progress -SecondsRemaining -PassThru -Activity 'Performing activity' | foreach { start-sleep -mil (get-random -min 100 -max 1000)}
  # Will display a progress bar at the top that will look similar to:
  # Performing activity
  # Working - 2%
  # [ooo ]
  # 00:09:20 remaining.
  # .NOTES
  # # Inspired by: https://www.powershellgallery.com/packages/Show-Progress/
  [CmdletBinding(ConfirmImpact = 'None')]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseProcessBlockForPipelineCommand', '')]
  param (
    [Parameter(Mandatory, Position = 0, ValueFromPipeline)]
    [PSObject[]]$InputObject,

    [string] $Activity = 'Processing items',

    [ValidateRange(1, 100)]
    [int] $UpdatePercentage = 1,

    [Alias('PassThrough')]
    [switch] $PassThru,

    [int] $Id = 1,

    [switch] $SecondsRemaining
  )

  begin {
    $OgEAP = $ErrorActionPreference; $OgPP = $ProgressPreference; $ErrorActionPreference = $ProgressPreference = 'SilentlyContinue'
  }

  process {
    $count = 0
    [int] $totalItems = $Input.count
    Out-Verbose "TotalItems in the pipeline: $TotalItems"
    $progressWritten = @()
    $StartTime = Get-Date

    # use a dot sourced scriptblock to loop despite its lower redability as its extremely fast
    $Input | . {
      process {
        # pass thru the input
        if ($PassThru) {
          $_
        }
        $count++
        [int] $percentComplete = ($Count / $totalItems * 100)
        $writeProgressSplat = @{
          Activity        = $Activity
          PercentComplete = $percentComplete
          Status          = "Working - $percentComplete%"
          Id              = $Id
        }
        if ($SecondsRemaining) {
          $SecondsElapsed = (Get-Date) - $StartTime
          $SecondsTogo = ($SecondsElapsed.TotalSeconds / $count) * ($totalItems - $count)
          $writeProgressSplat.SecondsRemaining = $SecondsTogo
        }
        if ($percentComplete -notin $progressWritten -and ($UpdatePercentage -eq 0 -or $percentComplete % $UpdatePercentage -eq 0)) {
          $progressWritten += $percentComplete
          Write-Progress @writeProgressSplat
        }
      }
    }

    # cleanup - if for some reason the function doesn't arrive at 100% percent completed. This will make the progress bar disappear.
    $writeProgressSplat = @{
      Activity        = $Activity
      PercentComplete = 100
      Status          = 'Completed'
      Id              = $Id
      Completed       = $true
    }
    Write-Progress @writeProgressSplat
    # Write-ProgressBar -PercentComplete $i -Total 150 -ActivityName "Running a long Task" -CurrentOperation "Working on Position $i"
    # Write-ProgressBar -Completed $i -OutOf 8192 -ActivityName "Running TaskName:" -CurrentOperation "Working on object $i"

    # ===============================================================
    # Create PowerShell Runspace
    [powershell]$ps = [PowerShell]::Create()
    # Add script and parameters to runspace
    $null = $ps.AddScript($task).AddParameter("sbParam", $sbParam)#.AddParameter("sbParam2", $sbparam2)
    # Start the runspace and grab is report status
    $status = $ps.BeginInvoke()

    # Set loop count
    $count = 0

    # Loop to check if status is completed
    while (!$status.IsCompleted) {
      Write-Progress -Id $idNum -Activity $message -Status "Please Wait..." -PercentComplete ($count % 100)
      $count++
      Start-Sleep -Milliseconds 300
    }

    $ps.EndInvoke($status)
  }

  end {
    $ps.Stop()
    $ps.Runspace.Close()
    $ps.Dispose()
    $ErrorActionPreference = $OgEAP; $ProgressPreference = $OgPP
  }
}