Get-WmiCustom (aka: Get-WMIObject with timeout!)
I make heavy use of WMI.
But when using it to gather information from customer’s machines for assessments, I sometimes find the occasional broken WMI repository. There are a number of ways in which WMI can become corrupted and return weird results. Most of the times you would just get errors, such as “Class not registered” or “provider load failure”. I can handle those errors from within scripts.
But there are some, more subtle – and annoying – ways in which the WMI repository can get corrupted. the situations I am talking about are the ones when WMI will accept your query… will say it is executing it… but it will never actually return any error, and just stay stuck performing your query forever. Until your client application decides to time out. Which in some cases does not happen.
Now that was my issue – when my assessment script (which was using the handy Powershell Get-WmiObject cmdlet) would hit one of those machines… the whole script would hang forever and never finish its job. Ok, sure, the solution to this would be actually FIXING the WMI repository and then try again. But remember I am talking of an assessment: if the information I am getting is just one piece of a bigger puzzle, and I don’t necessarily care about it and can continue without that information – I want to be able to do it, to skip that info, maybe the whole section, report an error saying I am not able to get that information, and continue to get the remaining info. I can still fix the issue on the machine afterward AND then run the assessment script again, but in the first place I just want to get a picture of how the system looks like. With the good and with the bad things. Especially, I do want to take that whole picture – not just a piece of it.
Unfortunately, the Get-WmiObject cmdlet does not let you specify a timeout. Therefore I cooked my own function which has a compatible behaviour to that of Get-WmiObject, but with an added “-timeout” parameter which can be set. I dubbed it “Get-WmiCustom”
Function Get-WmiCustom([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15)
{
$ConnectionOptions = new-object System.Management.ConnectionOptions
$EnumerationOptions = new-object System.Management.EnumerationOptions
$timeoutseconds = new-timespan -seconds $timeout
$EnumerationOptions.set_timeout($timeoutseconds)
$assembledpath = "\\" + $computername + "\" + $namespace
#write-host $assembledpath -foregroundcolor yellow
$Scope = new-object System.Management.ManagementScope $assembledpath, $ConnectionOptions
$Scope.Connect()
$querystring = "SELECT * FROM " + $class
#write-host $querystring
$query = new-object System.Management.ObjectQuery $querystring
$searcher = new-object System.Management.ManagementObjectSearcher
$searcher.set_options($EnumerationOptions)
$searcher.Query = $querystring
$searcher.Scope = $Scope
trap { $_ } $result = $searcher.get()
return $result
}
You can call it as follows, which is similar to how you would call get-WmiObject
get-wmicustom -class Win32_Service -namespace "root\cimv2" -computername server1.domain.dom
or, of course, specifying the timeout (in seconds):
get-wmicustom -class Win32_Service -namespace "root\cimv2" -computername server1.domain.dom –timeout 1
and obviously, since the function returns objects just like the original cmdlet, it is also possible to pipe them to other commands:
get-wmicustom -class Win32_Service -namespace "root\cimv2" -computername server1.domain.dom –timeout 1 | Format-Table
Related posts:
- The mystery of the lost registry values
- Looking at OpsMgr2007 Alert trend with Command Shell
- Articles
- Out-Blog!
- Early Adoptions, Health Checks and New Year Rants.
Related posts brought to you by Yet Another Related Posts Plugin.
September 23rd, 2009 at 3:17 pm
Hello!
I'm writing script for collecting network adapters data using WMI.
I tried to use your great code (ooh, that 10 000 000 days timeout was a someone's crazy idea… ), but have faced some problems.
The code works well if queried computer is either available or totally offline. If it is online but do not returns WMI object properly, I have PowerShell totally crashed with the message "An error has occurred that was not properly handled. Addidtional information is shown below. The Windows PowerShell process will exit". After that PowerShell console closing… Do you have any idea how to fix such behaviour?
I am using such function call: get-wmicustom -class win32_networkadapterconfiguration -namespace "root\cimv2" -computername sunset -timeout 10
Crash occurs when executing $result = $searcher.get() reaching timeout.
Thank you very much.
September 25th, 2009 at 10:57 am
I have not seen this… but that is what the trap is for.
try changing
trap { $_ } $result = $searcher.get()
to
trap { $_; continue } $result = $searcher.get()
December 8th, 2009 at 6:55 pm
Hi there,
This is cool and seems to work well for single queries. As a Powershell novice I'm trying to figure out how I can make it work in a script that reads computer names from a CSV file and queries the space on the C: partition via your function (which I modified quickly to add a filter, which does work). I tried the following code but it returns errors like 'Exception calling "Connect" with "0" argument(s): "The RPC server is unavailable."' I guess this means the computer names aren't being read in properly. Any idea how to make this work? Thanks so much!
Function Get-WmiCustom([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15)
{
$ConnectionOptions = new-object System.Management.ConnectionOptions
$EnumerationOptions = new-object System.Management.EnumerationOptions
$timeoutseconds = new-timespan -seconds $timeout
$EnumerationOptions.set_timeout($timeoutseconds)
$assembledpath = "\\" + $computername + "\" + $namespace
#write-host $assembledpath -foregroundcolor yellow
$Scope = new-object System.Management.ManagementScope $assembledpath, $ConnectionOptions
$Scope.Connect()
$querystring = "SELECT * FROM " + $class + " where DriveType=3 AND DeviceID='C:'"
#write-host $querystring
$query = new-object System.Management.ObjectQuery $querystring
$searcher = new-object System.Management.ManagementObjectSearcher
$searcher.set_options($EnumerationOptions)
$searcher.Query = $querystring
$searcher.Scope = $Scope
trap { $_ } $result = $searcher.get()
return $result
}
$csv = "machines.csv"
$import = Import-CSV $csv
$import | foreach {
get-wmicustom -class win32_logicaldisk -namespace "root\cimv2" -computername {$_.hostname} | Select SystemName,DeviceID,VolumeName,@{Name="size(GB)";Expression={"{0:N1}" -f($_.size/1gb)}},@{Name="freespace(GB)";Expression={"{0:N1}" -f($_.freespace/1gb)}}
}
December 17th, 2009 at 7:15 pm
I should see how your CSV file looks like… why not just a simple TXT file, with one machine per row?
foreach ($comp in (get-content c:\computers.txt))
{
get-wmicustom -class win32_logicaldisk -namespace "root/cimv2" – computername $comp
}
August 31st, 2010 at 4:55 pm
Thanks for this post, it is very frustrating that most powershell cmdlets do not have a timeout value. I made a modification to your function to add the ability to add the -filter attribute, just two changes were needed. I added – [string]$filter – to the variable definition line and made this modification to the $querystring variable
$querystring = "SELECT * FROM " + $class + $(if ($filter) {" WHERE $filter"})
August 31st, 2010 at 8:40 pm
Sweet addition, thanks.