Powershell change the wmi timeout value - Svendsen Tech
Jump to page sections

Changing the WMI Timeout Value for a Series of WMI Queries

NB! Old article, quality is not superb. Comment made 2020-03-17.

To change/tweak the WMI timeout value, you can instantiate a WMI or WMI searcher object and set its timeout property, which is a time span object. In this informational top example, it's done through the PSBase member set. Notice the empty string after the [wmi] type accelerator in the example directly below. The WMI timeout is only changed for that specific object instance, so this is basically useless other than for the sake of enlightenment.

The example further down uses a [WmiSearcher] object and sets its properties. This top example is for informational purposes only. Look at the code below to see the real deal immediately.

Now the bad news: During testing I discovered that the clients that "get stuck" still get stuck... And the timeout appears only to be in effect if WMI is working properly on the target computer. It appears to be limiting the time an actual query takes, not when to abort the attempt. Now that makes it a lot less useful; I will have to dig deeper to see if this broken behavior is at all remediable. Note made 2022-01-25: Runspaces allow you to set a timeout, as do PowerShell jobs. See Get-Help Start-Job.

Also consider checking out the PowerShell Get-WmiObject Wrapper script here. It's pretty darn useful. It uses runspaces to handle the timeouts.
PS E:\> $wmi = [wmi]''
PS E:\> $wmi.PSBase.Options.Timeout = '0:0:5'

Here "0:0:5" is cast to a time span value, which in this case is equivalent to 5 seconds (hours:minutes:seconds). You can look at the timeout property you just set like this:

PS E:\> $wmi.PSBase.Options.Timeout

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 5
Milliseconds      : 0
Ticks             : 50000000
TotalDays         : 5,78703703703704E-05
TotalHours        : 0,00138888888888889
TotalMinutes      : 0,0833333333333333
TotalSeconds      : 5
TotalMilliseconds : 5000

To inspect the various properties, use:

$wmi.PSBase | Get-Member

... and

$wmi.PSBase.Options | Get-Member

Source Code For An Example Script With A WMI Timeout

See below the source code for examples on how to use this script.

# Utility function making lists "less quoty" function ql { $args } # A list of computers to process. $Computers = ql win7esxi ubuntu64esxi silverstone winxpesxi 2008r2esxi localhost # The purpose here is to have clean output to the console (or redirected to a file). $Computers | ForEach-Object { # This is needed for the error handling in the catch block where $_ gets # set to the last error rather than the computer name. $Computer = $_ # Since certain WMI errors are terminating, we need a try/catch statement. try { # Here we specify the WMI query $Searcher = [WmiSearcher]'SELECT * FROM Win32_ComputerSystem' # This is where we set the timeout to 5 seconds. $Searcher.Options.TimeOut = "0:0:5" # Create necessary objects, specify a scope and namespace for the remote computer (or localhost or '.'), # then set the scope on the WMISearcher object, which is usually "\\computername\root\cimv2". $ConnectionOptions = New-Object Management.ConnectionOptions $ManagementScope = New-Object Management.ManagementScope("\\${Computer}\root\cimv2", $ConnectionOptions) $Searcher.Scope = $ManagementScope # Perform the query and retrieve the desired property, and expand it to a # string with Select -ExpandProperty. $MemSize = $Searcher.Get() | Select-Object -ExpandProperty TotalPhysicalMemory # Use this check for if there's a non-terminating error. if ($?) { $MemSizeGB = ($MemSize/1GB).ToString('N') Write-Output "${computer}: $MemSizeGB" } # If a non-terminating error occurs, we get here, but I think all errors are terminating with $searcher.Get() ... else { Write-Output "${Computer}: Error: $($Error[0])" } } # If WMI has a terminating error, we get here. catch { Write-Output "${Computer}: Error (terminating): $($Error[0])" } }

Example Use

A problem with the default behaviour of gwmi / Get-WmiObject, or rather cmdlets in general, is that even though you specify "-ErrorAction SilentlyContinue", it will break on for instance "Access denied" errors. "-ErrorAction" is only for non-terminating errors. "Access denied" was deemed by the cmdlet author to be a terminating error, so you get the behaviour in this screenshot:

The one it halts on there is "silverstone", which is a workgroup computer, where pass-through authentication doesn't work. "ubuntu64esxi" is a Linux computer (not part of the domain in any way), and is silently ignored, as you (at least I) would expect when setting -ErrorAction to SilentlyContinue. The remainder of the computers after silverstone are not processed. To work around this, you can use a try/catch statement and also change the WMI timeout value on a ''WmiSearcher'' object, and use "foreach" (non-pipeline keyword) or "Foreach-Object" (pipeline) to process the computers individually/sequentially. For clean output, you will need some "glue logic", and to handle errors.

You can use PowerShell jobs, which can be combined with a lowered, or increased, WMI timeout value. You can also check out another non-generic example using jobs, in this article about getting computer models in a domain.

Note made on 2022-01-25: This article is ... "weird". I'm not 100 % sure my test environment was representative when I was researching this. The article gets some minor traffic, which tells me it helps some people with something. For a way to set a thread timeout using efficient runspaces, see this article. These runspaces are compatible with PowerShell version 2 and up. With PowerShell 7 / PowerShell Core came Foreach-Object -Parallel, which you may want to research.

Example Output

Here is the output from the example script below, where you get the list of remote computers' RAM in GB, formatted with two decimals (and two errors):
PS C:\prog\Powershell> .\Wmi-Timeout-Example-New.ps1
win7esxi: 2.00
ubuntu64esxi: Error (terminating): Exception calling "Get" with "0" argument(s): "The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)"
silverstone: Error (terminating): Exception calling "Get" with "0" argument(s): "Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))"
winxpesxi: 0.50
2008r2esxi: 2.00
localhost: 2.00

Screenshot of Example Output

Here is a shiny screenshot of the same:

A Couple of Examples Where We Filter Away Errors and Redirect to a File

Since we're outputting text, we can filter the errors crudely by matching "error":
PS C:\prog\Powershell> .\Wmi-Timeout-Example-New.ps1 | Select-String -NotMatch 'error' > memsize.txt
PS C:\prog\Powershell> type .\memsize.txt

win7esxi: 2.00
winxpesxi: 0.50
2008r2esxi: 2.00
localhost: 2.00

For better validation, and not excluding unwanted lines that for some reason contain the string "error", we can use a regular expression (read more about regular expressions here) for more control. This should be sufficient in most cases:

PS C:\prog\Powershell> .\Wmi-Timeout-Example-New.ps1 | 
    Select-String -NotMatch '^[^:]+:\s*Error' > memsize.txt
PS C:\prog\Powershell> type .\memsize.txt

win7esxi: 2.00
winxpesxi: 0.50
2008r2esxi: 2.00
localhost: 2.00

The regular expression used above is "^[^:]+:\s*Error", which means: Start at the beginning, match one or more non-colon characters, as many as you can, match a literal colon (":"), possibly whitespace ("*" is a quantifier and indicates zero or more, always matches) and it's finally followed by the literal string "Error".

    Powershell     Windows     Wmi     All Categories

Google custom search of this website only

Minimum cookies is the standard setting. This website uses Google Analytics and Google Ads, and these products may set cookies. By continuing to use this website, you accept this.

If you want to reward my efforts