<

VBScript WMI Tutorial: Retrieve Free Space On C Drive From Multiple Computers

Published on
27,794 Points
15,294 Views
10 Endorsements
Last Modified:
Awarded
Community Pick

Introduction

During my participation as a VBScript contributor at Experts Exchange, one of the most common questions I come across is this:
"I have a script that runs against only one computer. How can I make it run against a list of computers in a text file?"
This article will provide you with an understanding of what you need to do to get this done, and also throw in some helpful features to make things a bit "neater".

Where Do We Start?

We should start with a script that presents the problem; one that only runs against one computer.  Here is a simple script that will retrieve the free space available on C Drive of the local computer.  The free space information is gathered using WMI, Windows Management Instrumentation.  WMI provides access to many classes that store information about the system and its settings and state.

strComputer = "."
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID = 'C:'")
For Each objItem In colItems
   dblFreeSpace = Round(objItem.FreeSpace / 1024 / 1024, 3)
Next
WScript.Echo dblFreeSpace & " MB Free"

Open in new window


There's not much to this script, but here’s how it works.
strComputer = "." refers to the local computer, so the WMI connection will be made to the computer that the script is running on.
objWMIService is the user defined variable name that represents the WMI Connection we are creating.  We can then use that connection to execute the WMI queries we need to use.
 
The GetObject call is the method that actually creates the WMI connection, and winmgmts represents the WMI Service.
{impersonationLevel=impersonate} is the default security moniker, which doesn't need to be specified, but I like to keep it in, just for completeness.

Finally, \root\cimv2 is the WMI namespace that we are connecting to, which is the most common Namespace to use.

Now to the working part of the script. ExecQuery is the method that allows us to execute a WMI query that retrieves a collection of objects for us to enumerate.  In this case, as we are only interested in the FreeSpace property, we just select FreeSpace from the Win32_LogicalDisk class.  If you want to know all of the properties that are available, you can do either of the following:
Download the ScriptoMaticV2.hta tool from Microsoft, and select the appropriate namespace, then select the appropriate class.  All of the properties will be displayed to you in the code window.
View the class documentation on the MSDN website.  Simply search for the class name, and you will find the full documentation for that class.
The query is written in Windows Query Language (WQL), so while the syntax is similar to SQL, it does not support all of the features of SQL.  For the purposes of this script, we are able to further restrict the results of the query to only a logical disk with a DeviceID of "C:".

Now, how do we enumerate the objects returned by the WMI query?  Using a For Each … Next loop.  In our loop, objItem refers to each individual element of colItems as we step through.  To refer to the property of each item we are interested in, we use objItem.FreeSpace, and store that value in dblFreeSpace.  Notice we are performing a calculation on this value.  This is because the FreeSpace property returns a value in bytes, and we want it in megabytes, so we divide it by 1024, then by 1024 again.  We have also used the Round function, to round the calculated figure to three decimal places.

Finally, once we have that value, and the loop exits, we echo the result to the screen, using WScript.Echo, and append the " MB Free" string, just to make the output nicer.

Right, So Now What About Multiple Computers?

OK, with the introduction out of the way, we now need to make this work against multiple computers.  For the sake of this exercise, we will take our input from a flat text file, which consists of one computer name per line, like so:
Computer1
Computer2
Computer3

Open in new window


Having done that, assuming the file name is computers.txt, here's the code that will read this file, and obtain the free space on C Drive of each computer, and output it to a CSV file.

strInputFile = "computers.txt"
strOutputFile = "CDriveSpace.csv"

Set objFSO = CreateObject("Scripting.FileSystemObject")
Const intForReading = 1
Const WMITimeOutInSeconds = 10
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20

Set objInputFile = objFSO.OpenTextFile(strInputFile, intForReading, False)
Set objOutputFile = objFSO.CreateTextFile(strOutputFile, True)
objOutputFile.WriteLine """Computer"",""C Drive Free Space (MB)"""

While Not objInputFile.AtEndOfStream
   strComputer = objInputFile.ReadLine
   If Ping(strComputer) = True Then
      strReturn = TestWMIConnection(strComputer, WMITimeOutInSeconds)

      If strReturn = "success" Then

         Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
         ' The wbemFlagReturnImmediately flag is the default ExecQuery behavior and is semi-synchronous. The important optimization is the addition of the wbemFlagForwardOnly flag.
         ' Combining wbemFlagReturnImmediately with wbemFlagForwardOnly results in a forward-only enumerator. A forward-only enumerator performs much faster than the default enumerator,
         ' because WMI doesn't maintain references to objects in the SWbemObjectSet.
         ' Source: http://msdn.microsoft.com/en-us/library/ms974547.aspx
         Set colItems = objWMIService.ExecQuery("SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID = 'C:'", "WQL", wbemFlagReturnImmediately + wbemFlagForwardOnly)
         For Each objItem In colItems
            dblFreeSpace = Round(objItem.FreeSpace / 1024 / 1024, 3)
         Next
         objOutputFile.WriteLine """" & strComputer & """,""" & dblFreeSpace & """"

      ElseIf strReturn = "failed" Then
         objOutputFile.WriteLine """" & strComputer & """,""WMI ERROR"""

      Else
         objOutputFile.WriteLine """" & strComputer & """,""WMI TIME OUT"""

      End If

   Else
      objOutputFile.WriteLine """" & strComputer & """,""OFFLINE"""

   End If
Wend

objInputFile.Close
objOutputFile.Close
WScript.Echo "Script complete. Please see " & strOutputFile

Function Ping(strComputer)
   Dim objShell, boolCode
   Set objShell = CreateObject("WScript.Shell")
   boolCode = objShell.Run("Ping -n 1 -w 300 " & strComputer, 0, True)
   If boolCode = 0 Then
      Ping = True
   Else
      Ping = False
   End If
End Function

Function TestWMIConnection(strComputer, intTimeOutInSeconds)
   ' Function written by Rob Sampson - 12 Jan 2011
   ' Experts-Exchange volunteer: http://www.experts-exchange.com/M_3820065.html
   ' Return strings from this function are in lower case, and consist of:
   ' "success": WMI Connection successful
   ' "failed": WMI Connection failed
   ' "time out": WMI Connection attempt timed out

   Set objFSO = CreateObject("Scripting.FileSystemObject")
   strTempScript = Replace(WScript.ScriptFullName, WScript.ScriptName, "") & "TempWMITestToBeDeleted.vbs"

   Set objTempFile = objFSO.CreateTextFile(strTempScript, True)
   objTempFile.WriteLine "On Error Resume Next"
   objTempFile.WriteLine "Set objWMIService = GetObject(""winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2"")"
   objTempFile.WriteLine "If Err.Number = 0 Then"
   objTempFile.WriteLine vbTab & "WScript.StdOut.Write ""success"""
   objTempFile.WriteLine "Else"
   objTempFile.WriteLine vbTab & "WScript.StdOut.Write ""failed"""
   objTempFile.WriteLine "End If"
   objTempFile.Close

   Set objShell = CreateObject("WScript.Shell")
   Set objExec = objShell.Exec("wscript " & objFSO.GetFile(strTempScript).ShortPath)
   intSeconds = 0
   While objExec.Status = 0 And intSeconds <= intTimeOutInSeconds
      WScript.Sleep 1000
      intSeconds = intSeconds + 1
   Wend
   If objExec.Status = 1 Then
      strReturn = objExec.StdOut.ReadAll
   Else
      On Error Resume Next
      objExec.Terminate
      Err.Clear
      On Error GoTo 0
      strReturn = "time out"
   End If
   objFSO.DeleteFile strTempScript, True

   TestWMIConnection = LCase(strReturn)
End Function

Open in new window


How Does THAT Work?

I know, it looks a bit daunting, but there’s a few extra features and error checking here too that I will explain.

The only variable values we have in this script, for the user to configure, are these two:
strInputFile = "computers.txt"
strOutputFile = "CDriveSpace.csv"

Open in new window


The first is the name of the input file to read from, and the second is the name of the output file for the script to create.  If a full path is not specified, the files will be created in the same directory that the script is running from (i.e., the current directory).

The first thing we need to do, when we know we’re going to work with the file system, is create an instance of the Scripting.FileSystemObject object.  This is the ActiveX object that allows us to use basic file system functions. That is provided by this line:
Set objFSO = CreateObject("Scripting.FileSystemObject")

Open in new window

Then we have some Constants we need to create for use in the script.
Const intForReading = 1
Const WMITimeOutInSeconds = 10
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20

Open in new window


The first is for use when opening text files using the OpenTextFile method.  WMITimeOutInSeconds is a constant for use with the custom function I have written: TestWMIConnection.  The last two are for use as extra parameters (see the comments in the code for their explanation) for the ExecQuery method we touched on earlier.

The OpenTextFile method returns a text stream object we can use to read through the input file we have instructed it to open.  Its first parameter is for the file path of the file to open, the second parameter is the operation mode, and the third is the create file flag, specifying whether or not to create the file if it doesn’t already exist.  For the operation mode, we have specified intForReading (which evaluates to 1), meaning the file is opened in read mode only.  Other options are write mode (integer value of 2), or append mode (integer value of 8).  We could have defined those constants, in case we needed them, but I have opted not to (because we don’t need them).  There is a fourth parameter to the OpenTextFile method, for the file format, being ASCII or Unicode, but I have not used that. The default format uses the system default.

The CreateTextFile method returns a text stream object we can use to write information to, writing to the file that we have instructed it to create.  The second parameter specifies whether to overwrite an existing file or not.  We have specified True so that it overwrites the report each time the script is run.  There is a third parameter to the CreateTextFile method, for the file format, being ASCII or Unicode, but I have not used that. The default format uses the system default.

Now that we have created our file objects, we can begin to read and write to them.  First off, we’ll write a header line to our CSV file, by using this:
objOutputFile.WriteLine """Computer"",""C Drive Free Space (MB)"""

Open in new window

That looks odd, doesn’t it?  Well, as we know, with the CSV format, the comma is the default delimiter, and when you have a comma as part of the actual data in a field, this field is treated as two fields, with the comma not being displayed as part of the data.  To get around this, you can enclose each field in quotes to be able to keep the comma as part of the data, and not split the field.  This is what I have done here.  By providing the extra quotes, we avoid the possibility that the data will be split up if the system is configured to present numbers in #,### format.  In VBScript, if you want to place a literal quote inside a string, you need to double it, hence the doubling up of the quotes around the field names.

To begin reading through the Input file, you need to use a loop.  I tend to use a While loop, while others like to use a Do loop.  They both do the same thing, it’s just personal preference.  This is the block responsible for reading through the input file:

While Not objInputFile.AtEndOfStream
   strComputer = objInputFile.ReadLine
   ...
Wend

Open in new window


The ReadLine method reads one line at a time, and returns it to the variable strComputer.  The While loop will continue to check for the AtEndOfStream condition, reading one line at a time, until the end of the file is reached.

So on each iteration through the loop, we can work with one computer name from the text file at a time.  The next few lines:

   If Ping(strComputer) = True Then
      strReturn = TestWMIConnection(strComputer, WMITimeOutInSeconds)
      If strReturn = "success" Then

Open in new window


Perform some connectivity checks, which I’ll explain later. For now, assume each computer passes the connectivity checks.  Once that occurs, we end up where we were before, creating the WMI connection, and reading the required values from the current computer we are querying.  This time, however, instead of outputting the result to the screen, we write it to the CSV file, using:
         objOutputFile.WriteLine """" & strComputer & """,""" & dblFreeSpace & """"

Open in new window


This writes the computer name (enclosed in quotes) in the first field, and the free space value (enclosed in quotes) in the second field.  Note this time, though, we have not added the " MB Free" string, because this is already implied by the header we have written.

Finally, aside from the other Else conditions for my connectivity tests, we exit the loop, close the two files, and display a message to the user that the script has finished.

objInputFile.Close
objOutputFile.Close
WScript.Echo "Script complete. Please see " & strOutputFile

Open in new window


Great! So What About The Extra Stuff?

In this script, I have provided two custom functions:
Function Ping(strComputer)
   ...
End Function

Open in new window

And
Function TestWMIConnection(strComputer, intTimeOutInSeconds)
   ...
End Function

Open in new window


We call these using the following lines:
   If Ping(strComputer) = True Then
      strReturn = TestWMIConnection(strComputer, WMITimeOutInSeconds)

Open in new window


The call to the Ping function returns True or False based on the response from the command:
Ping -n 1 -w 300 computername

Open in new window


That’s a shell command, so we need to execute it using the Run method of the WScript.Shell object.  We instantiate the WScript.Shell object using:
   Set objShell = CreateObject("WScript.Shell")

Open in new window


And execute the command using:
   boolCode = objShell.Run("Ping -n 1 -w 300 " & strComputer, 0, True)

Open in new window


The ping command returns 0 if successful, or 1 if it fails.  Therefore, we can get the Ping function to return True or False to the main code, so we know whether a computer is online or not.

I wrote the TestWMIConnection function in response to many people asking whether WMI supported a time out, because sometimes the connection would hang for a long period of time before finally returning a connection error, or sometimes hang indefinitely.  There is a method you can employ that allows a two minute time out, which consists of the following code:

Const wbemConnectFlagUseMaxWait = 128
Set objSWBemLocator = CreateObject("WbemScripting.SWbemLocator")
Set objWMIService = objSWBemLocator.ConnectServer(strComputer, "root\cimv2", Null, Null, , ,wbemConnectFlagUseMaxWait)
Set colItems = objWMIService.ExecQuery("SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID = 'C:'")

Open in new window


However, I found that sometimes that didn’t work, and I also didn’t want to wait 2 minutes per machine, because when you’re querying a few hundred, or even thousand, machines this can take far too long.

The hurdle we need to get over with the time out of the GetObject call is that it is a synchronous method, meaning that the script waits for the current command to finish executing before continuing with the next command.  To get over this, I make the function write another, temporary script, and execute it, being able to either wait for that second script to complete (returning a connection result), or force it to terminate if the timeout value has been reached.

The temporary script is created by using a series of WriteLine statements, but this time, we write actual lines of code.  Once the temporary script is written, this is what the contents of that file looks like:

On Error Resume Next
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\<computername>\root\cimv2")
If Err.Number = 0 Then
   WScript.StdOut.Write "success"
Else
   WScript.StdOut.Write "failed"
End If

Open in new window


Except that <computername> is actually written into the script as the actual computer name that we are checking from the main code.  So, what this second script does, that we can monitor asynchronously, is perform the same GetObject call that we’ve already used, and returns "success" or "failed" depending on whether there was an error or not.  The advantage to writing out a second script, and executing it, is that we can enforce a time out, which is the purpose of this function.

To be able to terminate the second script, we use the Exec method of the WScript.Shell object.  Whereas the Run method is synchronous, the Exec method is asynchronous, so you can monitor its status to see whether it has completed or not, using a loop.  During that loop, we use WScript.Sleep 1000 to pause the script for 1000 milliseconds (1 second) on each iteration, and increase a counter by one.  We do this until either the script is complete, or the time out value has been reached, based on comparing the counter with the time out value that was passed to the function.  In this script, I have passed a time out value of 10 seconds.  Once we know that the script has ended, we can read back the result using:
strReturn = objExec.StdOut.ReadAll

Open in new window


The temporary script returns either "success" or "failed" to the TestWMIConnection function, and the function then passes that, or "time out", back to the main code.  The temporary script is deleted just before the function exits.

The output for the connectivity tests:
      objOutputFile.WriteLine """" & strComputer & """,""WMI ERROR"""

Open in new window

And
      objOutputFile.WriteLine """" & strComputer & """,""WMI TIME OUT"""

Open in new window

And
      objOutputFile.WriteLine """" & strComputer & """,""OFFLINE"""

Open in new window


Each write "WMI ERROR", "WMI TIME OUT", or "OFFLINE" respectively, in the second field of the CSV file, depending on which condition was triggered.

Conclusion


So there you have it.  A script that reads computer names from a text file, tests the ping and WMI connectivity to each machine, queries WMI to retrieve the free space on C Drive, and writes the output to a CSV file.

My apologies for this article being much longer than I intended, but I wanted to include the extra bits and pieces, to provide a complete script, and also give you an understanding of some particular checks that need to be implemented when using a script like this in a large environment.  Without such tests, you either receive errors, your script terminates unexpectedly, or it takes days to complete.

On a final note, there is another version of the Ping function that uses WMI itself (only locally though), which is supported in Windows XP or greater.  I still use the shell command though, because I’ve been using that since Windows 2000, and just haven’t changed.

Function Ping(strComputer)
   Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
   Set colPings = objWMIService.ExecQuery ("Select StatusCode From Win32_PingStatus Where Address = '" & strComputer & "'")
   For Each objStatus in colPings
      If IsNull(objStatus.StatusCode) or objStatus.StatusCode <> 0 Then 
         Ping = False
      Else
         Ping = True
      End If
   Next
End Function

Open in new window

10
Comment
Author:RobSampson
14 Comments
LVL 50

Expert Comment

by:Dave
Great job Rob

Many thx

Dave
0
LVL 65

Author Comment

by:RobSampson
Thanks Dave.  It's my first article....trying to think of something else that might be useful for me to post as an article.  If you have any requests, let me know.

Rob.
0

Expert Comment

by:p1techservices
At the very end of the script you should include the entire script with all the bells and whistles. Otherwise, a very good article.
0
Cloud Class® Course: Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

LVL 65

Author Comment

by:RobSampson
Hi, glad you like it.  The entire code is in the snippet directly above the "How Does THAT Work?" sub heading.

Regards,

Rob.
0
LVL 7

Expert Comment

by:krish5music
Hi,

Is this can be run by local administrator or domain admin?

Because i have the same type script, but it should be run by domain admin.

could you please assist?

Thanks!

Krish.
0
LVL 65

Author Comment

by:RobSampson
A domain admin account would be required to run this against remote computers, since it does not have any provision to use alternate credentials.

Rob.
0
LVL 7

Expert Comment

by:krish5music
Yes. This is the only problem i am facing with vbscript. If i am  Is there any way to enable local administrator to retrieve information from remote computers?

Please let me know.

Thanks!

Krish.
0
LVL 65

Author Comment

by:RobSampson
You cannot use the local administrator account of the account you run the script on, because the credentials may be different.  The only way it will work is if you specify the local administrators credentials of the remote computer (one script per remote computer if they are different credentials).  To do this, change line 21 in the script from this:
		Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Open in new window


to this:
		Set objWbemLocator = CreateObject("WbemScripting.SWbemLocator")
		Set objWMIService = objwbemLocator.ConnectServer(strComputer, "\root\cimv2", "localusername", "password")
		objWMIService.Security_.authenticationLevel = 6

Open in new window


and change the username and password there for the remote computer.

You would also need to change line 74 from this:
   objTempFile.WriteLine "Set objWMIService = GetObject(""winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2"")"

Open in new window


to this, so that the WMI Timeout function uses the same credentials.
   objTempFile.WriteLine "Set objWbemLocator = CreateObject(""WbemScripting.SWbemLocator"")"
   objTempFile.WriteLine "Set objWMIService = objwbemLocator.ConnectServer(" & strComputer & ", ""\root\cimv2"", ""localusername"", ""password"")"
   objTempFile.WriteLine "objWMIService.Security_.authenticationLevel = 6"

Open in new window


Note the ""localusername"", and ""password"" in that section must have two sets of quotes, because they are strings inside the string that is written to the temporary file.

Regards,

Rob.
0
LVL 7

Expert Comment

by:krish5music
Hi Rob,

I edited the script as you mentioned. But it is showing the output as wmi error.

I logged in as a local administrator and i also included the local administrator credentials of remote computer in the code. I am able to ping the remote computer.
 
I have attached the code that i edited.

Please advise me regarding this.

Thanks!

Krish.









strInputFile = "C:\Concepts\MachineList.txt"
strOutputFile = "C:\Concepts\CDriveSpace.csv"

Set objFSO = CreateObject("Scripting.FileSystemObject")
Const intForReading = 1
Const WMITimeOutInSeconds = 10
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20

Set objInputFile = objFSO.OpenTextFile(strInputFile, intForReading, False)
Set objOutputFile = objFSO.CreateTextFile(strOutputFile, True)
objOutputFile.WriteLine """Computer"",""C Drive Free Space (MB)"""

While Not objInputFile.AtEndOfStream
   strComputer = objInputFile.ReadLine
   If Ping(strComputer) = True Then
      strReturn = TestWMIConnection(strComputer, WMITimeOutInSeconds)

      If strReturn = "success" Then
      
		Set objWbemLocator = CreateObject("WbemScripting.SWbemLocator")      
		Set objWMIService = objwbemLocator.ConnectServer(strComputer, "\root\cimv2", "localadmin", "password")
		objWMIService.Security_.authenticationLevel = 6


         'Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
         ' The wbemFlagReturnImmediately flag is the default ExecQuery behavior and is semi-synchronous. The important optimization is the addition of the wbemFlagForwardOnly flag.
         ' Combining wbemFlagReturnImmediately with wbemFlagForwardOnly results in a forward-only enumerator. A forward-only enumerator performs much faster than the default enumerator,
         ' because WMI doesn't maintain references to objects in the SWbemObjectSet.
         ' Source: http://msdn.microsoft.com/en-us/library/ms974547.aspx
         Set colItems = objWMIService.ExecQuery("SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID = 'C:'", "WQL", wbemFlagReturnImmediately + wbemFlagForwardOnly)
         For Each objItem In colItems
            dblFreeSpace = Round(objItem.FreeSpace / 1024 / 1024, 3)
         Next
         objOutputFile.WriteLine """" & strComputer & """,""" & dblFreeSpace & """"

      ElseIf strReturn = "failed" Then
         objOutputFile.WriteLine """" & strComputer & """,""WMI ERROR"""

      Else
         objOutputFile.WriteLine """" & strComputer & """,""WMI TIME OUT"""

      End If

   Else
      objOutputFile.WriteLine """" & strComputer & """,""OFFLINE"""

   End If
Wend

objInputFile.Close
objOutputFile.Close
WScript.Echo "Script complete. Please see " & strOutputFile

Function Ping(strComputer)
   Dim objShell, boolCode
   Set objShell = CreateObject("WScript.Shell")
   boolCode = objShell.Run("Ping -n 1 -w 300 " & strComputer, 0, True)
   If boolCode = 0 Then
      Ping = True
   Else
      Ping = False
   End If
End Function

Function TestWMIConnection(strComputer, intTimeOutInSeconds)
   ' Function written by Rob Sampson - 12 Jan 2011
   ' Experts-Exchange volunteer: http://www.experts-exchange.com/M_3820065.html
   ' Return strings from this function are in lower case, and consist of:
   ' "success": WMI Connection successful
   ' "failed": WMI Connection failed
   ' "time out": WMI Connection attempt timed out

   Set objFSO = CreateObject("Scripting.FileSystemObject")
   strTempScript = Replace(WScript.ScriptFullName, WScript.ScriptName, "") & "TempWMITestToBeDeleted.vbs"

   Set objTempFile = objFSO.CreateTextFile(strTempScript, True)
   objTempFile.WriteLine "On Error Resume Next"
   objTempFile.WriteLine "Set objWbemLocator = CreateObject(""WbemScripting.SWbemLocator"")"
   objTempFile.WriteLine "Set objWMIService = objwbemLocator.ConnectServer(" & strComputer & ", ""\root\cimv2"", ""localadmin"", ""password"")"
   objTempFile.WriteLine "objWMIService.Security_.authenticationLevel = 6"
   'objTempFile.WriteLine "Set objWMIService = GetObject(""winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2"")"
   objTempFile.WriteLine "If Err.Number = 0 Then"
   objTempFile.WriteLine vbTab & "WScript.StdOut.Write ""success"""
   objTempFile.WriteLine "Else"
   objTempFile.WriteLine vbTab & "WScript.StdOut.Write ""failed"""
   objTempFile.WriteLine "End If"
   objTempFile.Close

   Set objShell = CreateObject("WScript.Shell")
   Set objExec = objShell.Exec("wscript " & objFSO.GetFile(strTempScript).ShortPath)
   intSeconds = 0
   While objExec.Status = 0 And intSeconds <= intTimeOutInSeconds
      WScript.Sleep 1000
      intSeconds = intSeconds + 1
   Wend
   If objExec.Status = 1 Then
      strReturn = objExec.StdOut.ReadAll
   Else
      On Error Resume Next
      objExec.Terminate
      Err.Clear
      On Error GoTo 0
      strReturn = "time out"
   End If
   objFSO.DeleteFile strTempScript, True

   TestWMIConnection = LCase(strReturn)
End Function

Open in new window

Wmi-error.jpg
0
LVL 65

Author Comment

by:RobSampson
Hi, this should work now.

strInputFile = "C:\Concepts\MachineList.txt"
strOutputFile = "C:\Concepts\CDriveSpace.csv"
strLocalAdmin = "localadmin"
strLocalPassword = "password"

Set objFSO = CreateObject("Scripting.FileSystemObject")
Const intForReading = 1
Const WMITimeOutInSeconds = 10
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20

Set objInputFile = objFSO.OpenTextFile(strInputFile, intForReading, False)
Set objOutputFile = objFSO.CreateTextFile(strOutputFile, True)
objOutputFile.WriteLine """Computer"",""C Drive Free Space (MB)"""

While Not objInputFile.AtEndOfStream
   strComputer = objInputFile.ReadLine
   If Ping(strComputer) = True Then
      strReturn = TestWMIConnection(strComputer, WMITimeOutInSeconds)

      If strReturn = "success" Then
      
		Set objWbemLocator = CreateObject("WbemScripting.SWbemLocator")      
		Set objWMIService = objwbemLocator.ConnectServer(strComputer, "\root\cimv2", strComputer & "\" & strLocalAdmin, strLocalPassword)
		objWMIService.Security_.authenticationLevel = 6


         'Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
         ' The wbemFlagReturnImmediately flag is the default ExecQuery behavior and is semi-synchronous. The important optimization is the addition of the wbemFlagForwardOnly flag.
         ' Combining wbemFlagReturnImmediately with wbemFlagForwardOnly results in a forward-only enumerator. A forward-only enumerator performs much faster than the default enumerator,
         ' because WMI doesn't maintain references to objects in the SWbemObjectSet.
         ' Source: http://msdn.microsoft.com/en-us/library/ms974547.aspx
         Set colItems = objWMIService.ExecQuery("SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID = 'C:'", "WQL", wbemFlagReturnImmediately + wbemFlagForwardOnly)
         For Each objItem In colItems
            dblFreeSpace = Round(objItem.FreeSpace / 1024 / 1024, 3)
         Next
         objOutputFile.WriteLine """" & strComputer & """,""" & dblFreeSpace & """"

      ElseIf strReturn = "failed" Then
         objOutputFile.WriteLine """" & strComputer & """,""WMI ERROR"""

      Else
         objOutputFile.WriteLine """" & strComputer & """,""WMI TIME OUT"""

      End If

   Else
      objOutputFile.WriteLine """" & strComputer & """,""OFFLINE"""

   End If
Wend

objInputFile.Close
objOutputFile.Close
WScript.Echo "Script complete. Please see " & strOutputFile

Function Ping(strComputer)
   Dim objShell, boolCode
   Set objShell = CreateObject("WScript.Shell")
   boolCode = objShell.Run("Ping -n 1 -w 300 " & strComputer, 0, True)
   If boolCode = 0 Then
      Ping = True
   Else
      Ping = False
   End If
End Function

Function TestWMIConnection(strComputer, intTimeOutInSeconds)
   ' Function written by Rob Sampson - 12 Jan 2011
   ' Experts-Exchange volunteer: http://www.experts-exchange.com/M_3820065.html
   ' Return strings from this function are in lower case, and consist of:
   ' "success": WMI Connection successful
   ' "failed": WMI Connection failed
   ' "time out": WMI Connection attempt timed out

   Set objFSO = CreateObject("Scripting.FileSystemObject")
   strTempScript = Replace(WScript.ScriptFullName, WScript.ScriptName, "") & "TempWMITestToBeDeleted.vbs"

   Set objTempFile = objFSO.CreateTextFile(strTempScript, True)
   objTempFile.WriteLine "On Error Resume Next"
   objTempFile.WriteLine "Set objWbemLocator = CreateObject(""WbemScripting.SWbemLocator"")"
   objTempFile.WriteLine "Set objWMIService = objwbemLocator.ConnectServer(""" & strComputer & """, ""\root\cimv2"", """ & strComputer & "\" & strLocalAdmin & """, """ & strLocalPassword & """)"
   objTempFile.WriteLine "objWMIService.Security_.authenticationLevel = 6"
   'objTempFile.WriteLine "Set objWMIService = GetObject(""winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2"")"
   objTempFile.WriteLine "If Err.Number = 0 Then"
   objTempFile.WriteLine vbTab & "WScript.StdOut.Write ""success"""
   objTempFile.WriteLine "Else"
   objTempFile.WriteLine vbTab & "WScript.StdOut.Write ""failed"""
   objTempFile.WriteLine "End If"
   objTempFile.Close

   Set objShell = CreateObject("WScript.Shell")
   Set objExec = objShell.Exec("wscript " & objFSO.GetFile(strTempScript).ShortPath)
   intSeconds = 0
   While objExec.Status = 0 And intSeconds <= intTimeOutInSeconds
      WScript.Sleep 1000
      intSeconds = intSeconds + 1
   Wend
   If objExec.Status = 1 Then
      strReturn = objExec.StdOut.ReadAll
   Else
      On Error Resume Next
      objExec.Terminate
      Err.Clear
      On Error GoTo 0
      strReturn = "time out"
   End If
   objFSO.DeleteFile strTempScript, True

   TestWMIConnection = LCase(strReturn)
End Function

Open in new window


I have edited my post ID: 73062 so that it uses
		Set objWMIService = objwbemLocator.ConnectServer(strComputer, "\root\cimv2", strComputer & "\" & strLocalAdmin, strLocalPassword)

Open in new window


instead of
objwbemLocator.ConnectServer(strComputer, "\root\cimv2", "localusername", "password")

Open in new window


and also
   objTempFile.WriteLine "Set objWMIService = objwbemLocator.ConnectServer(""" & strComputer & """, ""\root\cimv2"", """ & strComputer & "\" & strLocalAdmin & """, """ & strLocalPassword & """)"

Open in new window


instead of
   objTempFile.WriteLine "Set objWMIService = objwbemLocator.ConnectServer(" & strComputer & ", ""\root\cimv2"", ""localusername"", ""password"")"

Open in new window


For this to work, I have also added
strLocalAdmin = "localadmin"
strLocalPassword = "password"

at the top of the code.

Regards,

Rob.
0
LVL 65

Author Comment

by:RobSampson
Sorry, I can't edit that post, but I have made the changes in the code as mentioned.
0
LVL 7

Expert Comment

by:krish5music
Hi Rob,

Thanks a lot for the code. Its working fine.
But its shows the output along with the OFFLINE message.
What could be the reason?  Please tell me if i have to comment any line in this code.
I have attached the output.

Thanks!
Krish.
Wmi-output.jpg
0
LVL 65

Author Comment

by:RobSampson
That looks like it might just be that you have a blank line at the end of your input file.  Remove any blank line at the end and try again.

Rob.
0
LVL 7

Expert Comment

by:krish5music
Great Job Rob. The code is working fine. Sorry for the delayed reply.

Thanks for your help.

Thanks!
Krish.
0

Featured Post

Cloud Class® Course: Microsoft Exchange Server

The MCTS: Microsoft Exchange Server 2010 certification validates your skills in supporting the maintenance and administration of the Exchange servers in an enterprise environment. Learn everything you need to know with this course.

Join & Write a Comment

Key to your CPU's ability to stay cool is to use the right amount of thermal paste and apply it correctly. In other words you want as much thermal conductivity between CPU and the cooling block. Use a quality thermal paste and apply it in a manner…
Learn how to collaborate with office 365 Office Online

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month