Powershell change the wmi timeout value

From Svendsen Tech Powershell Wiki

Jump to: navigation, search

Contents






Changing the WMI Timeout Value for a Series of WMI Queries

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 source 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.

Also consider checking out the PowerShell Get-WmiObject Wrapper script here. It's pretty darn useful.

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. 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 -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:

Image:Wmi-timeout-halting-error.png

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.

For an adaptable example using PowerShell jobs, which can be combined with a lowered, or increased, WMI timeout value, see this article. You can also check out another non-generic example using jobs, in this article.

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:

Image:Wmi-timeout-example-script.png


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".

Personal tools