Sleepy PowerShell

Putting PowerShell to sleep. Seems cruel. But it is necessary sometimes in order to either wait for another process or for an operator to read output. In this week’s PowerShell tip, we will review the cmdlet(s) that make this possible as well as walk through some real-world examples of how this could be used.

PowerShell Cmdlets
[sourcecode language=”powershell”]
get-command *sleep*
[/sourcecode]
This provides us with two results, one is an alias and the other is a cmdlet:

CommandType Name
———– —-
Alias sleep -> Start-Sleep
Cmdlet Start-Sleep


While we can use the alias ‘Sleep’, it isn’t necessarily a best practice to do so. For the examples provided in this week’s tip, we’ll use Start-Sleep. Now that we have a cmdlet, we need to see what switches or parameters are available.

If we run ‘Get-Help Start-Sleep -Full’, there are two parameters that become available – Milliseconds and Seconds. This basically allows us to determine how log the cmdlet runs for. It is really that simple. Start-Sleep pauses PowerShell for a fixed period. This does not mean PowerShell stops other processes, only cmdlets to be run in PowerShell itself. So if PowerShell kicks off an external process like a MSI or an EXE file, that cannot be controller with Start-Sleep. However, if a PowerShell script has more lines to execute, then those lines will wait until Start-Sleep releases the shell.

Uses of Start-Sleep
This week’s tip is not meant to be an exhaustive list of the possible uses of the Start-Sleep cmdlet but more of a quick review of some potential uses of the cmdlet. So let’s review a couple of quick examples.

Pausing to wait for a process to complete:

For a real-world example, we’ll use a script that is written to installed a series of patches. Most, if not all of these patches are quick to install, so we can pause the script for something like 60 seconds while we wait for the patch to be installed. The syntax would look something like this:

[sourcecode language=”powershell”]
[string]$Expression = ".\NDP471-KB4033342-x86-x64-AllOS-ENU.exe /quiet /norestart" Invoke-Expression $Expression
Start-Sleep -Seconds 60
[/sourcecode]
When this is run, PowerShell will kick off the installation process and then pause the PowerShell window for 60 seconds. What this is useful for is again waiting for the patch to install, but then we can run code post install to verify that the patch was successful like so:
[sourcecode language=”powershell”]
$NETval = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" -Name "Release"
If ($NETval.Release -lt "461310") {Write-Host "ERROR – .NET 4.7.1 was not installed."}
If ($NETval.Release -eq "461310") {Write-Host ".NET 4.7.1 not installed successfully!"}
[/sourcecode]
The only caveat to this is that if the installation does not complete in that 60 seconds, the post verification is useless.
Alternate Method for this example
Another way to handle external processes and waiting for completion is to use the Start-Process cmdlet combined with -Wait parameter. This parameter will wait for the process that was kicked off to complete before moving on to the next cmdlet in PowerShell.
[sourcecode language=”powershell”]
$Arglist='Windows8.1-KB2959977-x64.msu','/quiet','/norestart'
Start-Process -FilePath 'c:\windows\system32\wusa.exe' -ArgumentList $Arglist -NoNewWindow -Wait
[/sourcecode]
Now we have a way to install the patch and properly wait for it to install before running the post check code.

Example #2

Another relevant use would be simply to pause PowerShell for a loop that runs a series of checks that just take time to run. For example, let’s use another real-world scenario where a series of mailboxes are being moved to Office 365. A script could be created that would check the status of the movies every 5, 10 or 15 minutes.
[sourcecode language=”powershell”]
Do {
$NotComplete = (Get-MigrationBatch | Where {$_.Status -ne 'Complete'}).Count
Get-MigrationUserStatistics -Identity <User moving> | ft Identity,TotalItemsInSourceMailboxCount,SyncedItemCount,EstimatedTotalTransferSize,BytesTransferred,PercentageComplete,BadItemLimit
Start-Sleep -Seconds 900
} While ($NotComplete -gt 1)
[/sourcecode]
The above code will run the statistics check every 15 min while there are active migrations running. The script could be modified to send an email report as well.

Example #3

Another practical use would be to pause a PowerShell script to allow for an operator to read output briefly before moving on. For example:
[sourcecode language=”powershell”]
Write-host 'Installation of hotfix KB123214 is complete. Moving on to the next hotfix install."
Start-Sleep -Seconds 5
[/sourcecode]
The code above would allow an operator to read about the successful install and then the script would automatically move on to the next hotfix without user input.

Summary

The Start-Sleep cmdlet can be put to good use if you know it’s limits. There are other options depending on what you need, like ‘Start-Process’ -wait. As a side note, the default parameter is seconds for the Start-Sleep cmdlet. So if you want to save some characters in your script, a pause of 10 seconds would look like this:
[sourcecode language=”powershell”]
Start-Sleep 10
[/sourcecode]

Related Post