Get-WmiCustom (aka: Get-WMIObject with timeout!)

Was this helpful? Share It!

Get-WmiCustom (aka: Get-WMIObject with timeout!)

  • Comments 12

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

Leave a Comment
  • Please add 8 and 2 and type the answer here:
  • Post
  • So I stopped the time:

    First timeout message after 20 seconds.

    Second timeout message after another 20 seconds.

    Exception calling "Connect" with "0" argument(s): "The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)"

    At line:9 char:5

    +     $Scope.Connect()

    +     ~~~~~~~~~~~~~~~~

       + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

       + FullyQualifiedErrorId : COMException

    Exception calling "Get" with "0" argument(s): "The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)"

    At line:17 char:17

    +     trap { $_ } $result = $searcher.get()

    +                 ~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

       + FullyQualifiedErrorId : COMException

  • Hi, very useful little function. I know I'm a few years behind this being posted, so hopefully you're still around. My question here is, how would I use your function when I need to run a query? this is the get-wmi I'm using...

    $LogFiles = Get-Wmiobject -computername $Server -Query "Select * from CIM_DataFile Where Drive = '$LogDrive' And Path = '$LogQueryPath' And Extension = 'log'" | Sort LastModified

    problem is, run time can vary greatly depending on the number of logs which could be anywhere from less than 10 to somewhere in the hundreds of thousands and I can't have an excessive run time...

  • @laderio - not sure... either you are passing a wrong server name, or there is a firewall in between.

    @Laurie - Yes I am still around. The BEST solution is to make sure that WMI actually works on all your machines. That way you don't have to worry about timeouts as data will eventually come. Unfortunately in the past I have seen situations where WMI was broken on some customers machines and I had to run my collection tool on them but was not allowed to fix or modify anything: for me at the time was preferable to collect partial information (and know that some machines were broken) rather than blocking my whole data collection exercise forever...

  • Ok sorted it at the top of the function I placed:

    $ErrorActionPreference = "Stop"

    Then I wrapped your functions code in a try{} and then used the catch{} to handle the errors as required.

  • @Liam - this function was originally written to work on Powershell v1 - there was no Try/Catch support in Powershell at the time - hence the use of trap for error handling. On v2/3 there will of course be more elegant ways.

  • Thanks! Love the script. I run automated checks against large groups of computers all the time and this is one thing that was really annoying. I do have one problem though, is it possible to include a filter in the WMI query? Currently it's not working but I might attempt to hack it in.

  • Jason - you would indeed need to modify it to support for queries and filters; current implementation only supports class instances enumeration. I wrote this a long time ago on Powershell v1... I would say that by now there are much better ways to achieve the same result. If I were to rewrite this in a more 'modern' Powershell version, I would probably do it very differently, i.e. use the task/job infrastructure so that you can wait and abort/cancel the jobs if they take too long, and even parallelize execution... but the hack posted here served its purpose quite well for quite some time :-)

  • Thanks Daniele, I was able to get it working easy enough, I guess I should have given it the old college try before posting. Anyway, I'd like to get this working in my multi-threaded job/task script but I haven't had the time to get it returning all the objects correctly. Once I get more time I'd like to do that to speed up execution, multiple WMI queries that return a lot of data takes a long time on 6k workstations.

  • Daniele - Is there a way to add Credentials to this function?  I have a need to query some DMZ systems to perform an audit with different credentials.  Is there a way to add this easily to this function?

  • Yes Graeme I think that would be possible - the .NET classes support all those options such as authentication. I haven't tinkered with this code for years, but there is a forum post here with a similar custom function that seems to implement authentication: social.technet.microsoft.com/.../how-to-connect-to-wmi-using-systemmanagementconnectionoptions-for-alternate-credentials-in

  • Trying to use this, but no matter what computername I pass it only executes against the server its running on.

  • Nevermind, found the problem, I was leaving out the namespace

Page 1 of 1 (12 items)