---
title: "Write-Progress: Display Progress Bars in Console"
description: "Explore PowerShell Write-Progress, a cmdlet for displaying graphical progress bars and status messages in the console."
canonical: "https://adamtheautomator.com/write-progress/"
---

# Write-Progress: Display Progress Bars in Console

> Explore PowerShell Write-Progress, a cmdlet for displaying graphical progress bars and status messages in the console.

Source: https://adamtheautomator.com/write-progress/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![Write-Progress: Display Progress Bars in Console](https://adamtheautomator.com/wp-content/uploads/2019/06/5d238ae5c824b514689ea545.jpg)

# Write-Progress: Display Progress Bars in Console

[![](https://secure.gravatar.com/avatar/d0b9d42e21e5622713f8b693aa5c0f9244d5f7dd200ed29b8398f52dee5de337?s=192&d=mm&r=g)Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)18 June 20193 min. read

Categories: [IT Ops](/category/it-ops/)

Tags:[PowerShell](/tag/powershell/)

Table of Contents

*   [How to Use the Write-Progress Cmdlet](#how-to-use-the-write-progress-cmdlet)

This article will discuss how to use Powershell Write-Progress. Most of the time I prefer to display the working status of my script using `Write-Verbose`. `Write-Verbose` is an excellent way to send periodic messages out to the console to let the user know what’s going on. It’s also nice because you can quickly switch it on and off with the built-in `Verbose` switch.

![Using the PowerShellVerbose parameter](https://adamtheautomator.com/content/images/2019/07/building-progress-bar-powershell-scripts---write-progress.png)

Using the PowerShell`Verbose` parameter

However, `Write-Verbose` is pretty simplistic. It’s just a periodic text display to the console. There’s not much else to it. There are times when you need something more pronounced and a better way to indicate to the script user just how far along in the process your script is. For this, I use the Write-Progress [`Write-Progress`](https://technet.microsoft.com/en-us/library/hh849902.aspx?f=255&MSPPError=-2147217396) cmdlet.

This Powershell cmdlet is ideal for displaying a graphical progress bar right in the console. It’s an intuitive way to not only display status messages to the user but also to have a progress bar to indicate to the user how far the script is along in its execution.

![Write-Progress output](https://adamtheautomator.com/content/images/2019/07/building-progress-bar-powershell-scripts---write-progress2.png)

`Write-Progress` output

## How to Use the Write-Progress Cmdlet

Using this cmdlet is pretty straightforward. At its most basic, you simply need to specify the title of the progress bar using the `Activity` parameter, the status message to display using the `Status` parameter and how much to fill in the progress bar using the `PercentComplete` parameter.

```powershell
Write-Progress -Activity 'Title' -Status 'Doing something' -PercentComplete 0
```

Newcomers to [PowerShell](https://adamtheautomator.com/tag/powershell/) may see this and immediately try to do this something like this:

```powershell
Write-Progress -Activity 'Title' -Status 'Doing something 2' -PercentComplete 25

## Some process here

Write-Progress -Activity 'Title' -Status 'Doing something 3' -PercentComplete 50

## Some process here

Write-Progress -Activity 'Title' -Status 'Doing something 4' -PercentComplete 75

## Some process here
```

At first glance, you might not notice a problem here but think about how this is maintained. Notice the `PercentComplete` parameter. Notice that I had to statically assign the values 0,25,50 and 75? We never want to statically code anything unless necessary. Also, I’m not following the DRY principle here by repeating the same string `Write-Progress -Activity 'Title' -Status 'Doing something'` four different times. We need to refactor this code to come up with a more dynamic way to specify the `PercentComplete` parameter and remove as much code duplication as possible.

To do this, I’ll first need to figure out a way to prevent having to hard code the `PercentComplete` value in each `Write-Progress` call. To do this, I first need to find out how many `Write-Progress` calls I have. Once I figure this out, I can then do some arithmetic to get the actual percentage values.

I can either do this manually or not. I choose not. However, it gets a little hairy at this point. I’ll need to use the PowerShell parser to find each reference to `Write-Progress` just inside of the function itself.

```powershell
$steps = ([System.Management.Automation.PsParser]::Tokenize((gc "$PSScriptRoot\$($MyInvocation.MyCommand.Name)"), [ref]$null) | where { $_.Type -eq 'Command' -and $_.Content -eq 'Write-Progress' }).Count
```

It now doesn’t matter if I add or remove `Write-Progress` function calls. `$steps` will always have the total number of `Write-Progress` calls in the function.

We can now dynamically pass a value to `PercentComplete` by incrementing a `$stepcounter` variable and creating a percentage value from it.

```powershell
$steps = ([System.Management.Automation.PsParser]::Tokenize($MyInvocation.MyCommand.Definition, [ref]$null) | where { $_.Type -eq 'Command' -and $_.Content -eq 'Write-Progress' }).Count

$stepCounter = 0

Write-Progress -Activity 'Title' -Status 'Doing something' -PercentComplete ((($stepCounter++) / $steps) * 100)

## Some process here

Write-Progress -Activity 'Title' -Status 'Doing something' -PercentComplete ((($stepCounter++) / $steps) * 100)

## Some process here

Write-Progress -Activity 'Title' -Status 'Doing something' -PercentComplete ((($stepCounter++) / $steps) * 100)

## Some process here
```

Great! But this still looks ugly. Look at all that code duplication! It’s time to build a helper function. I’ll build one now.

```powershell
function Write-ProgressHelper {
    param(
        [int]$StepNumber,
        [string]$Message
    )
    
    Write-Progress -Activity 'Title' -Status $Message -PercentComplete (($StepNumber / $steps) * 100)
}
```

Now, we can simply call this helper function from within the function itself.

```powershell
$stepCounter = 0

Write-ProgressHelper -Message 'Doing something' -StepNumber ($stepCounter++)

## Some process here

Write-ProgressHelper -Message 'Doing something2' -StepNumber ($stepCounter++)

## Some process here

Write-ProgressHelper -Message 'Doing something3' -StepNumber ($stepCounter++)

## Some process here
```

Even though we’ve still got some code duplication, we were able to remove some of it. Since we replaced those `Write-Progress` references to `Write-ProgressHelper`, we’ll have to change the code to find the steps since our steps code was looking for function reference names.

```powershell
$steps = ([System.Management.Automation.PsParser]::Tokenize((gc "$PSScriptRoot\$($MyInvocation.MyCommand.Name)"), [ref]$null) | where { $_.Type -eq 'Command' -and $_.Content -eq 'Write-ProgressHelper' }).Count
```

This finally leaves us with this script.

```powershell
function Write-ProgressHelper {
	param (
	    [int]$StepNumber,
	    [string]$Message
	)

	Write-Progress -Activity 'Title' -Status $Message -PercentComplete (($StepNumber / $steps) * 100)
}

$script:steps = ([System.Management.Automation.PsParser]::Tokenize((gc "$PSScriptRoot\$($MyInvocation.MyCommand.Name)"), [ref]$null) | where { $_.Type -eq 'Command' -and $_.Content -eq 'Write-ProgressHelper' }).Count

$stepCounter = 0

Write-ProgressHelper -Message 'Doing something' -StepNumber ($stepCounter++)
Start-Sleep -Seconds 5

## Some process here

Write-ProgressHelper -Message 'Doing something2' -StepNumber ($stepCounter++)

Start-Sleep -Seconds 5

## Some process here

Write-ProgressHelper -Message 'Doing something3' -StepNumber ($stepCounter++)

Start-Sleep -Seconds 5

## Some process here
```

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fwrite-progress%2F&text=Write-Progress%3A%20Display%20Progress%20Bars%20in%20Console)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fwrite-progress%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fwrite-progress%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2026/06/26995-troubleshoot-dns-issues-powershell-codex.webp)

### [Troubleshoot DNS Issues with PowerShell](/troubleshoot-dns-issues-powershell/)

Troubleshoot DNS issues with PowerShell by testing name resolution, DNS client settings, cache entries, and network connectivity in a repeatable workflow.

![](https://adamtheautomator.com/wp-content/uploads/2025/10/image_2025-10-24_095322075.png)

### [Migrating from PowerShell 6 to 7.5: Breaking Changes/New Features](/migrating-powershell-6-to-7-5/)

Migrate from PowerShell Core 6 to 7.5: breaking changes, features, and testing tips.

![](https://adamtheautomator.com/wp-content/uploads/2025/06/featured-image-6.png)

### [How to Add Timeouts to Pester Tests with PowerShell Runspaces](/pester-test-timeout-runspaces/)

Prevent Pester tests from hanging indefinitely using PowerShell runspaces. Learn to handle variable scoping, module loading, TestDrive access, and stream capture challenges with timeout protection.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
