Adding a progress bar in PowerShell : a step-by-step guide
Table of Contents
I. Introduction
In this tutorial, we'll learn how to add a Progress Bar to a PowerShell script! The progress bar will allow you to follow the execution progress of a PowerShell script, which comes in handy when the script has to process a large number of elements. We could almost say that this will enhance the user experience by displaying progress information.
II. The Write-Progress cmdlet in PowerShell
PowerShell includes the "Write-Progress" cmdlet for creating custom progress bars in scripts. To use this cmdlet, we'll configure several of its parameters:
- -PercentComplete : specify the percentage of completion of the task, which involves performing a calculation to return precise information.
- -Activity : shows the current task, with more detailed information.
- -Status : indicates the current task.
We therefore have two parameters to manage the information displayed on the screen, as well as a parameter to calculate the percentage of progress. This last parameter is important, as it directly affects the progress bar visible on the screen.
There are other parameters for the "Write-Progress" cmdlet. In addition to this tutorial, you can consult the help page on the Microsoft website:
III. Creating your first progress bar
To familiarize ourselves with the progress bar principle, we'll start with a very basic example to put the "Write-Progress" cmdlet into practice.
In this example, we have a "for" loop that executes 10 times, with a 1 second pause between each iteration (this simulates a task). This allows us to temporize and easily see the progress of processing. The calculation "($i/10*100)" is used to calculate a consistent percentage.
for ($i = 1 ; $i -le 10 ; $i++) {
Write-Progress -Activity "Treatment in progress" -Status "$($i/10*100)% done" -PercentComplete ($i/10*100)
Start-Sleep -Seconds 1
}
This piece of code gives the following result (click on the image to see the animation):

We can see that there is a real consistency between the percentage indicated and the progress of the task. This is the main difficulty in creating a progress bar.
IV. Tracking a task with Write-Progress
We can use a progress bar to track the progress of a task, without indicating a percentage of progress (although this will be calculated for consistency). Instead, we'll indicate the total number of items to be processed and the number of items already processed.
The example below is used to browse a list of identifiers defined in an array (variable "ListeDesIdentifiants") using a "Foreach" loop. We could use this list to create user accounts, etc. The important thing here is to see the mechanics of the progress bar.
Here is the complete code:
# List of logins
$ListeDesIdentifiants = @("florian.burnel","guy.mauve","gerard.mensoif","jeanmichel.diledefrance")
# Total number of logins to process
$ListeDesIdentifiantsCount = $ListeDesIdentifiants.Count
# Processed logins counter
$NbIdentifiantsTraites = 1
# Process each login with a loop
foreach($Identifiant in $ListeDesIdentifiants) {
Write-Progress -PercentComplete ($NbIdentifiantsTraites/$ListeDesIdentifiantsCount*100) -Status "Creating current accounts" -Activity "Processed logins : $NbIdentifiantsTraites of $ListeDesIdentifiantsCount"
# Actions for each account
# New-LocalUser...
# New-ADUser...
Start-Sleep -Seconds 1
# Increment the number of accounts processed
$NbIdentifiantsTraites++
}
We manage two important values to calculate the progression:
- The total number of items to be processed, stored in the "ListeDesIdentifiantsCount" variable and retrieved via the "Count" property.
- The number of items already processed, to monitor progress. This value is stored in the "NbIdentifiantsTraites" variable. It is incremented by 1 each time the loop is iterated.
Here's the result (click on the image to see the animation):

Finally, here's another example where we track a multi-file download by using the progress bar to display the name of the file being downloaded. Here, a "For" loop is used to browse the file list, but we could use a "Foreach" loop.
# List of files to download
$ListeDesFichiers = @("Archive1.zip", "Archive2.zip", "Document1.zip")
for ($i = 0 ; $i -lt $ListeDesFichiers.Length ; $i++) {
$Fichier = $ListeDesFichiers[$i]
Write-Progress -Activity "Files download" -Status "Download $Fichier " -PercentComplete (($i + 1) / $ListeDesFichiers.Length * 100)
# Download action (simulated here)
# Invoke-WebRequest...
Start-Sleep -Seconds 1
}
Write-Host "File download complete!"
Here's the result in pictures (click on the image to see the animation):

V. Managing progress with fixed milestones
Percentage calculation is not mandatory if you simply want to track the progress of your scripts based on statically defined milestones. If you have a script that performs actions in 4 main stages and you simply want to know what stage it's at, the "Write-Progress" cmdlet can be used.
Write-Progress -Activity "Task n°1 : <description>" -Status "Task 1 on 4" -PercentComplete 25
# Operation to perform... (pause for 1 second)
Start-Sleep -Seconds 1
Write-Progress -Activity "Task n°2 : <description>" -Status "Task 2 on 4" -PercentComplete 50
# Operation to perform... (pause for 1 second)
Start-Sleep -Seconds 1
Write-Progress -Activity "Task n°3 : <description>" -Status "Task 3 on 4" -PercentComplete 75
# Operation to perform... (pause for 1 second)
Start-Sleep -Seconds 1
Write-Progress -Activity "Task n°4 : <description>" -Status "Task 4 on 4" -PercentComplete 100
# Operation to perform... (pause for 1 second)
Start-Sleep -Seconds 1
We obtain the following result (click on the image to see the animation):

VI. Managing an interruption in progress
We can customize the messages displayed by modifying the value of the "-Activity" and "-Status" parameters in the "Write-Progress" cmdlet. This can be useful for managing an interruption in processing and notifying the user.
Here's an example where we simulate an interruption of the task at 50%, before resuming 2 seconds later. So, in practice, the progress bar will display an interruption message before resuming the task and informing the user. This technique can be useful for informing the user of pauses or waits during processing.
for ($i = 1; $i -le 100; $i++) {
if ($i -eq 50) {
Write-Progress -Activity "Interruption" -Status "50% of job interrupted" -PercentComplete $i
Start-Sleep -Seconds 2
Write-Progress -Activity "Resume" -Status "Resuming the task" -PercentComplete $i
} else {
Write-Progress -Activity "Treatment in progress" -Status "$i% done" -PercentComplete $i
}
Start-Sleep -Milliseconds 50
}
VII. Conclusion
We've just seen how to use the "Write-Progress" cmdlet to create progress bars with PowerShell. There are various possible scenarios, depending on your needs and usage scenarios. But a progress bar is always a nice touch when executing a PowerShell script, as well as providing precise (and valuable) tracking.
It is possible to go further by declaring several progress bars to manage overall progress and the progress of a sub-task. This means using "Write-Progress" twice and playing with the "-PercentComplete" parameter to calculate the percentage of the subtask. We could also use the "-SecondsRemaining" parameter to specify the number of seconds remaining before the end of processing, but here again this involves calculations (notably relying on the average execution time of previous elements to give an estimate).