How to use Write-Progress in my function

I have never used Write-Progress before and I am having a heck of a time trying to figure out how I can toss it in my existing script below since it takes a while to run on systems that have many updates installed. I would like to throw in a ProgressBar switch parameter so the user can have the option of including it or not.

Function Get-WindowsUpdates
		{
			Param (
				
				$ComputerName = $env:COMPUTERNAME
				
			)
			
			$ComputerName = $ComputerName.ToUpper()
			
			Foreach ($Computer in $ComputerName)
			{
				$Session = [activator]::CreateInstance([type]::GetTypeFromProgID(“Microsoft.Update.Session”, $Computer))
				$Searcher = $Session.CreateUpdateSearcher()
				$History = $Searcher.GetTotalHistoryCount()
				$Query = $Searcher.QueryHistory(0, $History)
				
				ForEach ($Item in $Query)
				{
					$Properties = New-Object -Type PSObject -Property @{
						'ComputerName' = $Computer
						'UpdateDate' = $Item.Date
						'KB' = [regex]::match($Item.Title, 'KB(\d+)')
						'UpdateTitle' = $Item.Title
						'UpdateDescription' = $Item.Description
						'SupportUrl' = $Item.SupportUrl
						'UpdateId' = $Item.UpdateIdentity.UpdateId
						'RevisionNumber' = $Item.UpdateIdentity.RevisionNumber
					}
					
					Write-Output $Properties
				}
			}
		}

Thank you

Basically, you can’t show the progress of a query. Display actual progress requires you to process each item individually. If you look at most Write-Progress examples, you will see they are processing files and you can do the math because you are processing each file in a collection. In your function, you are running a query against the computer and collecting information, but you are NOT getting records one at a time, you are returning a result based on that query. To get the progress you are looking for, you would need to say “Give me the first WIndows Update out of all of the Updates, when I process this record, increment the %”. All of the processing time of your function is here: $Query = $Searcher.QueryHistory(0, $History). When the query returns the results, you are simply creating a PSObject, which just takes a couple of seconds.

Even you wrap the query with a do\while loop and increment it, it’s just fake progress because you don’t know how long the query is going to take or where it’s at in the process. You can put manual progress in steps, but there isn’t anyway to provide actual progress on the query itself:

Function Get-WindowsUpdates	{
	Param (
		[string[]]$ComputerName = $env:COMPUTERNAME,
        [switch]$ShowProgress
	)
	begin{}		
	process{
        Foreach ($Computer in $ComputerName) {
            if ($ShowProgress) {write-progress -activity "Connecting to $Computer" -status "25% Complete:" -percentcomplete 25;}		    
            $Session = [activator]::CreateInstance([type]::GetTypeFromProgID(“Microsoft.Update.Session”, $Computer))
            if ($ShowProgress) {write-progress -activity "Connected to $Computer, creating update searcher..." -status "50% Complete:" -percentcomplete 50;}		    
		    $Searcher = $Session.CreateUpdateSearcher()
		    $History = $Searcher.GetTotalHistoryCount()
            if ($ShowProgress) {write-progress -activity "Found $History updates on $Computer, beginning update query..." -status "75% Complete:" -percentcomplete 75;}		    
		    $Query = $Searcher.QueryHistory(0, $History)
				
		    $results = ForEach ($Item in $Query) {
			   $props =  @{
				    'ComputerName' = $Computer.ToUpper()
				    'UpdateDate' = $Item.Date
				    'KB' = [regex]::match($Item.Title, 'KB(\d+)')
				    'UpdateTitle' = $Item.Title
				    'UpdateDescription' = $Item.Description
				    'SupportUrl' = $Item.SupportUrl
				    'UpdateId' = $Item.UpdateIdentity.UpdateId
				    'RevisionNumber' = $Item.UpdateIdentity.RevisionNumber
			    }
					
			    New-Object -TypeName PSObject -Property $props
		    }
	    }
        if ($ShowProgress) {write-progress -activity "Completed Windows update query on $Computer" -Completed}		    

    } #process
    end{$results}
}

Get-WindowsUpdates -ShowProgress

As a side note, your $ComputerName parameter would be a string array since you are doing a foreach computer. The .ToUpper() is a string method for an individual string, so:

PS C:\Windows\System32\WindowsPowerShell\v1.0> [string[]]$Computers = "Computer1","Computer2"

PS C:\Windows\System32\WindowsPowerShell\v1.0> $Computers.ToUpper()
COMPUTER1
COMPUTER2

The only reason that works is because Powershell V3 introduced implicit foreach, so it assumes you want to run .ToUpper() on each line. If you run that code on a V2 machine it would generate an error. I’d highly recommend doing that inside your foreach loop on individual objects as a best practice.

That’s what I was thinking as well, just wanted confirmation. Yeah, I know about the implicit foreach in PS3 and I am trying to purposely make my scripts not run on PS2 if at all possible. I’m trying to force those I work with to upgrade to at least PS3 right now. I usually throw in a #Requires -Version 3.0. I didn’t know it was a best practice, but I guess that makes sense. I also changed the “$Properties = New-Object -Type PSObject -Property” portion to [pscustomobject] since I am trying to force the use of PowerShell 3 on everyone.

Thanks for your help.