multithreadingpowershellprogress-bar

Add a 5 minutes progress bar in powershell


I would like to add a 5 minutes progress bar in my Powershell script. I don't find the solution to run my progress bar and the other part of my script at the same time... Can someone help please ? Do I need to implement any thread ? Thank you.

Here is the progress bar :

$seconds = 60
$minutes = $seconds * 5
1..$minutes |ForEach-Object {
    $percent = $_ * 100 / $minutes 

    Write-Progress -Activity "Créations des machines virtuelles" -Status "$([math]::ceiling((($minutes - $_) / 60))) minutes remaining..." -PercentComplete $percent 

    Start-Sleep -Seconds 1
}

Solution

  • You can use a System.Timers.Timer instance and subscribe to its Elapsed events using Register-ObjectEvent with a script block ({ ... }) passed to its -Action parameter, from where you can call Write-Progress periodically.

    This allows you to perform foreground operations as usual.

    Here's a self-contained example:

    # Initialize the progress display
    $activity = 'Creating VMs...'
    Write-Progress -Activity $activity -PercentComplete 0
    
    $totalMinutes = 1           # How long to show the progress bar for.
    $timerIntervalSecs = 3      # The interval for firing timer events
    
    # Create an initially disabled timer that fires every $intervalSecs when enabled.
    $timer = [System.Timers.Timer]::new($timerIntervalSecs * 1000) 
    
    # Register for the timer's  "Elapsed" event. 
    # The relevant values are passed via a hashtable passed to the -MessageData parameter, 
    # which can be accesssed with $Event.MessageData inside the -Action script block.
    # -Action script block - which runs in a dynamic module - full access to the caller's variables.
    $eventJob = Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action { 
      $endTime, $totalMinutes, $activity = $Event.MessageData.EndTime, $Event.MessageData.TotalMinutes, $Event.MessageData.Activity
      $timeLeft = $endTime - (Get-Date)
      $percent = (1 - $timeLeft.TotalSeconds / ($totalMinutes * 60)) * 100
      if ($timeLeft -lt 0) { $percent = 100; $timeLeft = [timespan] 0 } # fully elapsed already.
      Write-Progress -Activity $activity -Status "$([math]::Floor($percent))% complete, $([math]::Ceiling($timeLeft.TotalMinutes)) minute(s) remaining..." -PercentComplete $percent
    } -MessageData @{ EndTime = (Get-Date).AddMinutes($totalMinutes); TotalMinutes = $totalMinutes; Activity = $activity }
    
    $timer.Start() # Start the timer.
    
    Write-Verbose -vb 'Simulating foreground processing. Press Ctrl-C to exit.'
    try {
        # Simulate foreground processing. 
        while ($true) {
          Write-Host -NoNewline .
          # Sleep a little. Note: Event-processing is blocked during sleep.
          Start-Sleep -Seconds 1
        }
    } finally {
       # Cleanup.
       $timer.Stop()
       Remove-Job $eventJob -Force
       Write-Progress -Activity $activity -Completed
    }