PowerShell Scripting with WMI

PowerShell Tutorial 11 – Part 1: Scripting with Windows Management Instrumentation (WMI) – Properties


The purpose of this PowerShell training session is to introduce WMI. Like all the tutorials before it, I am going to introduce concepts used when scripting with WMI. Because of the vast amount of data available on WMI, I’m going to present just the basics needed to start working with it in your environment. This is part 1 of a 3 part tutorial which will cover the following:

  • Part 1 - What is WMI, how to navigate the name space, how to collect information using properties of WMI classes (Objects).
  • Part 2 - Using Methods to make changes in the environment.
  • Part 3 – Sending output to a file, creating reports from information gathered.

Note: I have added Part 3 because of an issue I and others have run into with the scripts posted in the Microsoft Script Center. We are not able to use the “Out-File” nor the “>” redirect to send script output to a file (Like we did with VBScript). If you use a Microsoft script and attempt either of the following, the file gets created but there is no data.

.\SomeScript.ps1 | Out-File -FilePath “C:\MyScripts\Output.txt”

-or-

.\SomeScript.ps1 > “C:\MyScripts\Output.txt

Again, we will go over this in part 3.

What is WMI?

WMI is basically a database of information maintained on each Windows system. We connect to the WMI Service to query information maintained in the database. To visually see WMI on your system do the following:

  • Open “Computer Management” from Administrative Tools.
  • Expand “Services and Applications.”
  • Right-click “WMI Control” and choose Properties.
  • Click on the Advanced Tab. Notice: Default namespace for scripting by default set to root/cimv2.

WMI consists of classes that are made up of properties and methods that we can manipulate with PowerShell scripting. So how do we find out what classes are available?

Using a GUI Tool to Discover WMI Classes, Properties, and Methods.

Before PowerShell existed, VBScript writers used a tool called WMI CIM Studio which provided a GUI into the WMI hierarchy. In previous PowerShell training session we have examined how the “Get-Member” cmdlet allows us to view properties and methods of objects. I’m going to show you how both methods (WMI CIM Studio and PowerShell) are used to find Classes, Properties, and Methods.

The WMI Administrative Tools are available from the Microsoft website. I HIGHLY RECOMMEND DOWNLOADING IT!

After you have installed the WMI Tools, launch WMI CIM Studio from Start – Programs – WMI Tools.

  • Allow blocked content – if your browser is locked down.
  • You will be prompted to connect to namespace: make sure “root/cimv2″ is selected – click OK.
  • Click OK to use your current user log on. Local Admin Rights are required.
  • Click on the binocular icon to search for a class.
  • In the search box type processor and click GO! for results.
  • For now we are interested in the Win32 classes.
  • Choose Win32_Processor and click OK.

WMI CIM Studio

image 11.1

Image 11.1 is a picture of WMI CIM Studio Tool. The left pane exposes the classes available and the right pane shows which properties(tab) and methods(tab) are available for the selected class. In our case the selected class is Win32_Processor.

You can also use the WMI CIM Studio to connect to remote machines to discover which WMI classes are available. This is helpful as there are different versions of WMI and different classes dependent on which OS is running.

  • Click “Browse for Namespace” button which is located to the right of the “Classes in” drop-down menu.
  • In “Machine Name:” type the UNC for the remote computer (\\ServerName).
  • Leave the “Starting Namespace” set to root\CIMV2. Click the “Connect” button.
  • You are prompted to login as current user. If the current user is not an administrator on the remote computer, un-check the option and supply proper credentials.
  • Once connected click OK.

To verify that you are connected to the remote server look at the “Classes in:” drop-down. It should read similar to the following: \\ServerName\root\CIMV2. Use the same methods of searching for Win32 classes as done in the first example.

Starting to see the power of the tool? Take a few minutes to browse, play, and discover. Let’s say you want to write a script that gets disk information but your not sure which WMI class you need to enumerate. Use the find (binocular) button and type in “disk.” Now you have a set of classes that you can examine. Try other searches like Server, Printer, Memory, and Network. What I want you to understand is that the “Tool” assists in finding the right class for any script concept you may have. Half the battle is finding the class, once that’s completed the script code used to enumerate and make changes is much the same. Meaning, you will use the same script code to work with any WMI object as I’ll show you later in this tutorial.

The WMI Administrative Tools were developed to help VBScripters and Programmers when working with WMI. The tool is still viable and you should have it in your arsenal.

Using PowerShell to Find Classes, Properties, and Methods.

Now that we know how to find WMI Classes with the GUI, let’s examine how to use PowerShell to accomplish the same.

A cool feature of PowerShell is the ability to search WMI Classes. With a couple of short commands we can find which WMI classes are available on local and remote machines.

Example 1. List the available Classes on the local machine

Get-WmiObject -List -Namespace “root\CIMV2″
PowerShell Training WMI Classes

Classes

image 11.2

Example 2. List the available Classes on a remote machine

Get-WmiObject -List -Namespace “root\CIMV2″ -ComputerName TypeComputerNameHere

Note: the “-Namespace” parameter is optional. Remember from looking at the WMI Control properties, the default namespace is root\cimv2. Sometimes the default namespace gets changed on a machine which is beyond our control. By adding the “-Namespace” parameter we insure connection to the desired namespace. Otherwise our scripts may fail to connect to the WMI Class required.

Example 3. List the Properties and Methods of a WMI Class.

From examples 1 and 2 we get a list of all the WMI classes within the namespace: root/cimv2. For now, we are interested in the “Win32″ classes. If you really want to get under the hood with what WMI is and does, follow this link for more information.

Sticking with the Theme of this training session, in the classes list you find the Win32_Processor class. Let’s get the properties and methods using the “Get-Member” cmdlet.

Get-WmiObject -Class “Win32_Processor” -Namespace “root\CIMV2″ | Get-Member
PowerShell Training WMI Properties

Properties and Methods

image 11.3

The importance of examples 1, 2, and 3, are to help you find classes to complete tasks. Something else I want you to take note of. Throughout these PowerShell Training Sessions we have talked about “data types.” Notice that both the GUI tool and PowerShell also include which data types we can expect when working with Properties.

In this short section we have discovered how to find WMI Classes that we require. In the following section we will learn how to connect to WMI and gather information. We are going to write a script that is going to create a Hardware inventory report.

Scripting with WMI

Scenario: The big boss has come to us requesting a hardware inventory report of all the servers on our network. What he wants to know:

  • Machine manufacturer, model number, and serial number.
  • BIOS information to determine if updates are required,
  • OS type
  • CPU information: Manufacturer, type, speed, and version.
  • Amount of memory in each server.
  • Disk information: Size, interface type, and media type.
  • Network Information: IP settings and MAC address.

Our response: As always, no problem!

As you have seen from this tutorial, the amount of system information provided by WMI seems endless. But we have the tools to help us navigate the sea of system information. With the “Get-WmiObject” cmdlet we have access to all of this information. We only need to learn one syntax, which drastically cuts the learning curve.

Get-WmiObject syntax:

Get-WmiObject -Class [classname] -NameSpace [namespace] -ComputerName [ComputerName]

Using the syntax let’s see what result we get enumerating the BIOS settings. Since we are new to WMI and not sure which class to connect to, let’s use WMI CIM Tool and do a search on BIOS. Here are the results:

  • CIM_BIOSElement
  • CIM_BIOSFeature
  • CIM_BIOSFeaturedBIOSElements
  • CIM_BIOSLoadedlnNV
  • CIM_VideoBIOSElemnt
  • CIM_VideoBIOSFeatureVideoBIOSElements
  • Win32_BIOS
  • Win32_SMBIOSMemory
  • Win32_SystemBIOS

Ok, so we have a lot to choose from. From expreince, I know the information I’m looking for is in the Win32_BIOS class. You will start to recognize which classes contain which information, just comes with time spent working with WMI. So using the syntax let’s look at our local systems BIOS information:

Get-WmiObject -Class Win32_BIOS -NameSpace “root\CIMV2″
PowerShell Training - WMI BIOS settings

BIOS Information

image 11.4

Noticing the syntax I used for this example, I omitted the -computername parameter which by default enumerates the local system. The other parameters are optional as well, but take note of the different results that occur:

Get-WmiObject Win32_BIOS
PowerShell Training - WMI BIOS without Parameters

BIOS

image 11.5

In this example we omitted the -Class, -Namespace, and -ComputerName parameters. Note: we did not omit the actual class “Win32_BIOS” – we still need that. Ignoring the -Namespace parameter works because the local computer has the WMI name space default set to root\CIMV2, as mentioned earlier in this PowerShell training session. The output is also different, we are only presented with a small subset of properties that exist for the class. You will see this syntax example used on the Microsoft Scripts Repository examples as well as from other sources. The problem, if we are searching for available properties of a class this example doesn’t work for us. So, we can either use the full syntax or pipe this example to the Format-* cmdlet:

Get-WmiObject Win32_BIOS | Format-List *

By using the wild card (*) in a regular expression, you should see a list of all the properties and their results.

Building our Inventory Script

I’m going to be using the Script and Function Templates that were introduced in the PowerShell Introduction to Scripting. You can copy the templates here if you haven’t already.

Step 1. Machine manufacturer, model number, and serial number

  1. Using the WMI CIM Tool search for system. From the list of available classes choose Win32_ComputerSystem. Let’s now use PowerShell to return all the property results:
    Get-WmiObject Win32_ComputerSystem | Format-List *
  2. Identify which properties will provide the information for the requirement.
    • Manufacturer = Manufacturer property.
    • Model Number = Model property.
    • Serial Number = No property exists in this class. We will have to find it somewhere else.
    • Total Amount of Memory = TotalPhysicalMemory property exists in this class and does fulfill one of our requirements. So let’s use this property from this class.
  3. Build Code.
#sets the computer to local
$strComputer =”.”

#Creates a variable called $colItems which contains the WMI Object
$colItems = Get-WmiObject Win32_ComputerSystem -Namespace “root\CIMV2″ `
-ComputerName $strComputer

#Use a foreach loop to iterate the $colItems (collection).
#Store the properties information in the $objItem Variable.
foreach($objItem in $colItems) {
#Use the Write-Host cmdlet to output required property information
Write-Host “Computer Manufacturer: ” $objItem.Manufacturer
Write-Host “Computer Model: ” $objItem.Model
Write-Host “Total Memory: ” $objItem.TotalPhysicalMemory “bytes”
}

Step 2. Get BIOS Information

Let’s build the next block of code for getting BIOS information.

  1. Search “BIOS” in WMI CIM Studio – Choose Win32_BIOS Class. Return property results:
    Get-WmiObject Win32_BIOS | Format-List *
  2. Identify Properties:
    • BIOS Type = Description property.
    • Version = SMBIOSBIOSVersion, SMBIOSMajorVersion, SMBIOSMinorVersion properties.
    • Serial Number requirement located = SerialNumber property.
  3. Build Code
$strComputer = “.”

$colItems = Get-WmiObject Win32_BIOS -Namespace “root\CIMV2″ -computername $strComputer
foreach($objItem in $colItems) {
Write-Host “BIOS:”$objItem.Description
Write-Host “Version:”$objItem.SMBIOSBIOSVersion”.”`
$objItem.SMBIOSMajorVersion”.”$objItem.SMBIOSMinorVersion
Write-Host “Serial Number:” $objItem.SerialNumber
}

Note: I’ve used the escape character (`) in each code block. In the first example after “root\CIMV2″` and in code above $objItem.SMBIOSBIOSVersion”.”`. The escape character lets the script engine know that this is a single line of code, even though we have a line break in the code. This is for aesthetics, keeping long lines of code readable.  Warning: The escape character can also make troubleshooting difficult. I usually write my code in full to verify functionality, then add the escape character to clean things up.

Step 3. Write Code for the remaining requirements

To save space and keep you from being bored to tears, I’m going to write the rest of the script blocks for each of our Inventory requirements. By now I think you get the picture on how to find classes and can see which classes and properties I’ll be using in the code.

OS TYPE:

$strComputer = “.”

$colItems = Get-WmiObject Win32_OperatingSystem -Namespace “root\CIMV2″`
-Computername $strComputer

foreach($objItem in $colItems) {
Write-Host “Operating System:” $objItem.Name
}

CPU Info:

$strComputer = “.”

$colItems = Get-WmiObject Win32_Processor -Namespace “root\CIMV2″`
-Computername $strComputer

foreach($objItem in $colItems) {
Write-Host “Processor:” $objItem.DeviceID $objItem.Name
}

DISK Info:

$strComputer = “.”

$colItems = Get-WmiObject Win32_DiskDrive -Namespace “root\CIMV2″`
-ComputerName $strComputer

foreach($objItem in $colItems) {
Write-Host “Disk:” $objItem.DeviceID
Write-Host “Size:” $objItem.Size “bytes”
Write-Host “Drive Type:” $objItem.InterfaceType
Write-Host “Media Type: ” $objItem.MediaType
}

NETWORK Info:

$strComputer = “.”

$colItems = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace “root\CIMV2″`
-ComputerName $strComputer | where{$_.IPEnabled -eq “True”}

foreach($objItem in $colItems) {
Write-Host “DHCP Enabled:” $objItem.DHCPEnabled
Write-Host “IP Address:” $objItem.IPAddress
Write-Host “Subnet Mask:” $objItem.IPSubnet
Write-Host “Gateway:” $objItem.DefaultIPGateway
Write-Host “MAC Address:” $ojbItem.MACAddress
}

Building the User-Defined Functions

Now that we have the raw code, I’m going to convert it a user-defined function. Store the Function in your code library so that you can use it in other scripts.

SysInfo Function:

#* FileName:  SysInfo.txt    #*=============================================================================
#* FUNCTION LISTING #*=============================================================================
#* Function:    SysInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_ComputerSystem properties
#*
#*
#*=============================================================================

Function SysInfo {

$colItems = Get-WmiObject Win32_ComputerSystem -Namespace “root\CIMV2″ `
-ComputerName $strComputer

foreach($objItem in $colItems) {
Write-Host “Computer Manufacturer: ” $objItem.Manufacturer
Write-Host “Computer Model: ” $objItem.Model
Write-Host “Total Memory: ” $objItem.TotalPhysicalMemory “bytes”
}

}

To test the function, copy and paste the code into powershell. Hit the Enter/Return key until you return to the command prompt. Call the function by typing the function name SysInfo in the command line.

BIOSInfo Function:

#* FileName:  BIOSInfo.txt    #*=============================================================================
#* FUNCTION LISTING #*=============================================================================
#* Function:    BIOSInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_BIOS properties
#*
#*
#*=============================================================================

Function BIOSInfo {

$colItems = Get-WmiObject Win32_BIOS -Namespace “root\CIMV2″ -computername $strComputer
foreach($objItem in $colItems) {
Write-Host “BIOS:”$objItem.Description
Write-Host “Version:”$objItem.SMBIOSBIOSVersion”.”`
$objItem.SMBIOSMajorVersion”.”$objItem.SMBIOSMinorVersion
Write-Host “Serial Number:” $objItem.SerialNumber
}

}

OSInfo Function:

#* FileName:  OSInfo.txt    #*=============================================================================
#* FUNCTION LISTING #*=============================================================================
#* Function:    OSInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_OperatingSystem properties
#*
#*
#*=============================================================================

Function OSInfo {

$colItems = Get-WmiObject Win32_OperatingSystem -Namespace “root\CIMV2″`
-Computername $strComputer

foreach($objItem in $colItems) {
Write-Host “Operating System:” $objItem.Name
}

}

CPUInfo Function:

#* FileName:  CPUInfo.txt    #*=============================================================================
#* FUNCTION LISTING #*=============================================================================
#* Function:    CPUInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_Processor properties
#*
#*
#*=============================================================================

Function CPUInfo {

$colItems = Get-WmiObject Win32_Processor -Namespace “root\CIMV2″`
-Computername $strComputer

foreach($objItem in $colItems) {
Write-Host “Processor:” $objItem.DeviceID $objItem.Name
}

}

DiskInfo Function:

#* FileName:  DiskInfo.txt    #*=============================================================================
#* FUNCTION LISTING #*=============================================================================
#* Function:    DiskInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_DiskDrive properties
#*
#*
#*=============================================================================

Function DiskInfo {

$colItems = Get-WmiObject Win32_DiskDrive -Namespace “root\CIMV2″`
-ComputerName $strComputer

foreach($objItem in $colItems) {
Write-Host “Disk:” $objItem.DeviceID
Write-Host “Size:” $objItem.Size “bytes”
Write-Host “Drive Type:” $objItem.InterfaceType
Write-Host “Media Type: ” $objItem.MediaType
}

}

NetworkInfo Function:

#* FileName:  NetworkInfo.txt    #*=============================================================================
#* FUNCTION LISTING #*=============================================================================
#* Function:    NetworkInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_NetworkAdapterConfiguration properties
#*
#*
#*=============================================================================

Function NetworkInfo {

$colItems = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace “root\CIMV2″`
-ComputerName $strComputer | where{$_.IPEnabled -eq “True”}

foreach($objItem in $colItems) {
Write-Host “DHCP Enabled:” $objItem.DHCPEnabled
Write-Host “IP Address:” $objItem.IPAddress
Write-Host “Subnet Mask:” $objItem.IPSubnet
Write-Host “Gateway:” $objItem.DefaultIPGateway
Write-Host “MAC Address:” $ojbItem.MACAddress
}

}

Putting it all Together

So here is are long awaited script. Copy the script to a file called ServerInventory.ps1. Next, run the script.

#* FileName:  ServerInventory.ps1
#*=============================================================================
#* Script Name: [ServerInventory]
#* Created:     [12/14/07]
#* Author:      Jesse Hamrick
#* Company:     PowerShell Pro!
#* Email:
#* Web:         http://www.powershellpro.com
#* Reqrmnts:
#* Keywords:
#*=============================================================================
#* Purpose:    Server Hardware Inventory
#*
#*
#*=============================================================================

#*=============================================================================
#* REVISION HISTORY
#*=============================================================================
#* Date:        [DATE_MDY]
#* Time:        [TIME]
#* Issue:
#* Solution:
#*
#*=============================================================================

#*=============================================================================
#* FUNCTION LISTING
#*=============================================================================
#* Function:    SysInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_ComputerSystem properties
#*
#*
#*=============================================================================

Function SysInfo {

$colItems = Get-WmiObject Win32_ComputerSystem -Namespace “root\CIMV2″ `
-ComputerName $strComputer

foreach($objItem in $colItems) {
Write-Host “Computer Manufacturer: ” $objItem.Manufacturer
Write-Host “Computer Model: ” $objItem.Model
Write-Host “Total Memory: ” $objItem.TotalPhysicalMemory “bytes”
}

}

#*=============================================================================
#* FUNCTION LISTING
#*=============================================================================
#* Function:    BIOSInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_BIOS properties
#*
#*
#*=============================================================================

Function BIOSInfo {

$colItems = Get-WmiObject Win32_BIOS -Namespace “root\CIMV2″ -computername $strComputer
foreach($objItem in $colItems) {
Write-Host “BIOS:”$objItem.Description
Write-Host “Version:”$objItem.SMBIOSBIOSVersion”.”`
$objItem.SMBIOSMajorVersion”.”$objItem.SMBIOSMinorVersion
Write-Host “Serial Number:” $objItem.SerialNumber
}

}

#*=============================================================================
#* FUNCTION LISTING
#*=============================================================================
#* Function:    OSInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_OperatingSystem properties
#*
#*
#*=============================================================================

Function OSInfo {

$colItems = Get-WmiObject Win32_OperatingSystem -Namespace “root\CIMV2″`
-Computername $strComputer

foreach($objItem in $colItems) {
Write-Host “Operating System:” $objItem.Name
}

}

#*=============================================================================
#* FUNCTION LISTING
#*=============================================================================
#* Function:    CPUInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_Processor properties
#*
#*
#*=============================================================================

Function CPUInfo {

$colItems = Get-WmiObject Win32_Processor -Namespace “root\CIMV2″`
-Computername $strComputer

foreach($objItem in $colItems) {
Write-Host “Processor:” $objItem.DeviceID $objItem.Name
}

}

#*=============================================================================
#* FUNCTION LISTING
#*=============================================================================
#* Function:    DiskInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_DiskDrive properties
#*
#*
#*=============================================================================

Function DiskInfo {

$colItems = Get-WmiObject Win32_DiskDrive -Namespace “root\CIMV2″`
-ComputerName $strComputer

foreach($objItem in $colItems) {
Write-Host “Disk:” $objItem.DeviceID
Write-Host “Size:” $objItem.Size “bytes”
Write-Host “Drive Type:” $objItem.InterfaceType
Write-Host “Media Type: ” $objItem.MediaType
}

}

#*=============================================================================
#* FUNCTION LISTING
#*=============================================================================
#* Function:    NetworkInfo
#* Created:     [12/14/07]
#* Author:       Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: WMI Function that enumerate win32_NetworkAdapterConfiguration
#*         properties
#*
#*=============================================================================

Function NetworkInfo {

$colItems = Get-WmiObject Win32_NetworkAdapterConfiguration -Namespace “root\CIMV2″`
-ComputerName $strComputer | where{$_.IPEnabled -eq “True”}

foreach($objItem in $colItems) {
Write-Host “DHCP Enabled:” $objItem.DHCPEnabled
Write-Host “IP Address:” $objItem.IPAddress
Write-Host “Subnet Mask:” $objItem.IPSubnet
Write-Host “Gateway:” $objItem.DefaultIPGateway
Write-Host “MAC Address:” $ojbItem.MACAddress
}

}

#*=============================================================================
#* SCRIPT BODY
#*=============================================================================
#* Connect to computer
$strComputer = “.”

#* Call SysInfo Function
Write-Host “Sytem Information”
SysInfo
Write-Host

#* Call BIOSinfo Function
Write-Host “System BIOS Information”
BIOSInfo
Write-Host

#* Call OSInfo Function
Write-Host “Operating System Information”
OSInfo
Write-Host

#* Call CPUInfo Function
Write-Host “Processor Information”
CPUInfo
Write-Host

#* Call DiskInfo Function
Write-Host “Disk Information”
DiskInfo
Write-Host

#* Call NetworkInfo Function
Write-Host “Network Information”
NetworkInfo
Write-Host

#*=============================================================================
#* END OF SCRIPT: [ServerInventory]
#*=============================================================================

PowerShell Training Server Inventory

Script Results

Running the script I receive the following data:

image 11.6

Looks like the script is working and were getting back the information required. But this script only works on the local machine. We can now start tweaking the “SCRIPT BODY” portion of the script to attach to remote machines.

Tweak 1. Connecting to a remote computer

This tweak is easy, we only need to change one line of code:
In the script body make the following change.
#* Connect to computer
$strComputer = “.”

-to-

#* Connect to computer
$strComputer = “ComputerName

PowerShell Training Remote Server Inventory

Remote Computer Results

image 11.7

Image 11.7 is a list of properties from my remote file and print server. I want to change the script to prompt me to enter a computer name so that I don’t have to manually change within the code. I also want to display the computer name so I know where the information is coming from.

Tweak 2. Script to prompt user for computer name

Again, change the $strComputer=”.” code to:

$strComputer = Read-Host “Enter Computer Name”
Write-Host “Computer:” $strComputer

By adding the two lines above, the script will prompt for and display the computer name used by the script. Make your edit in the “SCRIPT BODY” and then execute it.

PowerShell Training Remote Server Inventory Prompt

Read-Host

image 11.8

When executing the script you are prompted to enter the name of the computer you wish to inventory. Type a computer name and press the Enter/Return key.

PowerShell Training Remote Server Inventory Computername Prompt

Results

image 11.9

Now you can start having some fun running your script against other remote computers on your network (local admin rights required). The boss is happy with what he sees and wants you to gather the same information from all the computers on your network. Your answer: “No Problem…”

Since it would not be practical to run the script one time for each computer on the network, we are going to change the code in the “SCRIPT BODY” to use an array. To demonstrate this I will be using the “Get-Content” cmdlet to read a text file that contains all the computer names I want to enumerate. The “Get-Content” cmdlet reads the file and creates the array for me.

Tweak 3. Use an array to gather information from multiple computers

Step 1. Create a text file C:\MyScripts\Computers.txt and import or enter each computer name on a separate line. For Example:
Computer01
Computer02
Computer03

Etc…

PowerShell Training Remote Server Inventory Comuters.txt

Computer Names

For this demonstration this is what my text file looks like:

image 11.10

When you do this in the real world, it is not uncommon to have hundreds or thousands of entries in you text file.

Step 2. Change to “SCRIPT BODY” code to use the “Get-Content” cmdlet.
Change $strComputer = “.”

-to-

$arrComputers = get-Content -Path “C:\MyScripts\Computers.txt”

foreach ($strComputer in $arrComputers){Function Calls go here}

At this point I am also going to use a couple more tweaks to organize the output on the screen. I will add Write-Host $strComputer at the beginning of the “SCRIPT BODY” and use Write-Host “End of report for $strComputer” at the end.

Here is what the new “SCRIPT BODY” should look like:

#*=============================================================================
#* SCRIPT BODY
#*=============================================================================
#* Create and array from C:\MyScripts\Computers.txt

$arrComputers = get-Content -Path “C:\MyScripts\Computers.txt”

foreach ($strComputer in $arrComputers){ #Function Calls go here

Write-Host “Computer Name:” $strComputer
Write-Host “======================================”

#* Call SysInfo Function
Write-Host “Sytem Information”
SysInfo
Write-Host

#* Call BIOSinfo Function
Write-Host “System BIOS Information”
BIOSInfo
Write-Host

#* Call OSInfo Function
Write-Host “Operating System Information”
OSInfo
Write-Host

#* Call CPUInfo Function
Write-Host “Processor Information”
CPUInfo
Write-Host

#* Call DiskInfo Function
Write-Host “Disk Information”
DiskInfo
Write-Host

#* Call NetworkInfo Function
Write-Host “Network Information”
NetworkInfo
Write-Host “End of Report for $strComputer”
Write-Host “======================================”
Write-Host
Write-Host

} #End function calls.

Since I like the script that prompts for a computer name, I’m going to create a new script file called PCReport.ps1 by using Save as in the file menu. Now that we know the ServerInventory.ps1 script is safe we can edit the “SCRIPT BODY” in PCReport.ps1. The file names are just for demonstration, choose any file name you wish.

PowerShell Training Remote Server Inventory via an Array

Script Results

After you have completed editing the “SCRIPT BODY” run the script.

image 11.11

You output should be similar to that of image 11.11

This concludes Part 1. of the WMI Tutorial. In part 2. we will be making changes to WMI Objects using methods. For example: You have added a DHCP server to manage all of your IP subnets. I will show you how to use methods to change all of your workstations from static to dynamic DHCP clients, no need to physically visit each workstation.  And in Part 3. we will discuss how to create reports.

Until next time, happy scripting…

Email This Page To A Friend Email This Page To A Friend


Comments

Instead of using a list of computer names in an input file, would it be possible to use some sort of call to ADSI to get a list of servers and use that as input?

(Looking forward to parts 2 and 3. Can’t get enough of this stuff!)

Yes, click here for an article which shows how to query Active Directory for all computer names.

By Codey Codec on January 28th, 2008 at 6:31 pm

First of all, very good beginner tutorial for all the newbies including myself; keep up the good work. I’m eagerly awaiting the next tutorial.

My question is it possible to create functions that you can call on a as needed basis?

For example:
Function ListSoftware($strComputer){
$colItems = gwmi -class Win32_Product -co $strComputer
ForEach($objItem in $colItems){
write-host $objItem.caption
}
}

What I’d like to do with this function is save it to a file call ListSoftware.ps1 and run it once whenever I need the function. Then, as needed, I want to be able to type “ListSoftware ‘computername’” at the prompt to get the results I want without having to run the ListSoftware.ps1 script again. It seems that I cannot do this without saving this function to the profile and have it save to memory during startup. Any thoughts?

Codey,
Read this article about functions: http://www.powershellpro.com/function-calling/144/

Also try:
Tweak 2. Script to prompt user for computer name
Again, change the $strComputer=”.” code to:
$strComputer = Read-Host “Enter Computer Name”

If that doesn’t work, you could build your own Cmdlet, which is beyond the scope of this tutorial.

This is kick ass! Thanks for takinhg the time.

By codey codec on January 31st, 2008 at 8:13 am

Thanks for the reply Jesse. Are you planning to write a intro to .net scripting with Powershell? Can you recommend any resources for this? Thanks!!

Jesse, have you tried this Class at all?

PS C:\Users\administrator.PVMLAB\Desktop\scirptbu> get-wmiobject Win32_registryaction | get-member

TypeName: System.Management.ManagementObject#root\cimv2\Win32_RegistryAction

Name MemberType Definition
—- ———- ———-
Invoke Method System.Management.ManagementBaseObject Invoke()
ActionID Property System.String ActionID {get;set;}
Caption Property System.String Caption {get;set;}
Description Property System.String Description {get;set;}
Direction Property System.UInt16 Direction {get;set;}
EntryName Property System.String EntryName {get;set;}
EntryValue Property System.String EntryValue {get;set;}
key Property System.String key {get;set;}
Name Property System.String Name {get;set;}
Registry Property System.String Registry {get;set;}
Root Property System.Int16 Root {get;set;}
SoftwareElementID Property System.String SoftwareElementID {get;set;}
SoftwareElementState Property System.UInt16 SoftwareElementState {get;set;}
TargetOperatingSystem Property System.UInt16 TargetOperatingSystem {get;set;}
Version Property System.String Version {get;set;}
__CLASS Property System.String __CLASS {get;set;}
__DERIVATION Property System.String[] __DERIVATION {get;set;}
__DYNASTY Property System.String __DYNASTY {get;set;}
__GENUS Property System.Int32 __GENUS {get;set;}
__NAMESPACE Property System.String __NAMESPACE {get;set;}
__PATH Property System.String __PATH {get;set;}
__PROPERTY_COUNT Property System.Int32 __PROPERTY_COUNT {get;set;}
__RELPATH Property System.String __RELPATH {get;set;}
__SERVER Property System.String __SERVER {get;set;}
__SUPERCLASS Property System.String __SUPERCLASS {get;set;}
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime ScriptMethod System.Object ConvertToDateTime();

PS C:\Users\administrator.PVMLAB\Desktop\scirptbu>

for the life of me I caannot figure out the syntax it wants? You?

pdirt,
Thanks for submitting this comment in the PowerShellPro Forums. To continue troubleshooting issue look for my responses there

-Jesse

Thank you! That’s a great sample, exactly I’m looking for!

Something odd, but certainly not a show stopper:

Get-wmiobject syntax section:

I run: gwmi -class win32_bios -namespace “root\cimv2″

and get the short version of the output as if I ran: gwmi win32_bios

Then when I ran: gwmi win32_bios | format-list *

it’s when I get the full output version of the first screenshot.

No biggy, though. It just seems to be kind of backwards. :-)

This is the best site for learning PowersShell I have come across so far.
I have learned so much in the last 2 days from your site.
Big thank you.
I am trying to combine PowerShell and ActivePerl and hope to come up with some useful tools to manage Windows machines the ways shell scripts do for Unix boxes.

I hope to see somewhere in your tutorials to show us how to schedule PS scripts to run unattended.

Cheers

By NetGuyDave on August 18th, 2008 at 9:17 am

Jesse, thanks again for the great tutorial. Corporate feels that it is best to keep me on Novell eDir and my workstation left in Workgroup mode to manage our NT 4.0 domain. With that said, what do you suggest I do to logon to a list of servers to gather the WMI objects, Net Use logon? Or does PowerShell have a logon feature that resolves my situation? I’ve tried all this but without success.
http://powershelllive.com/blogs/lunch/archive/2007/04/02/day-5-using-wmi.aspx

Edit….all our servers are running Win 2003, we only have 4.0 for PDC and BDC. I want to enumerate information from our Win2k3 servers. Thanks.

By Serious Stan on August 27th, 2008 at 3:48 am

hrp2171 (and anyone else confused about the full / contracted list of properties displayed),

I was struck by this too. The thing that makes the difference is actually the “C” in the namespace. Try the following two commands:
gwmi -class win32_bios -namespace “root\cimv2“
gwmi -class win32_bios -namespace “root\Cimv2“

Note that the only difference is that the “C” is capitalised in the second example (the -class switch is irrelevant too – providing the class name is there it still works exactly the same).

I suspect this is a bug in the WMI system, but it’s difficult to imagine what could cause this particular effect. Perhaps there is some other (more intuitive) way to request a full or abridged list of certain properties – I’d be interested to know.

By Serious Stan on August 27th, 2008 at 3:57 am

… to follow on from (and correct) my previous comment, it isn’t actually the “C” that’s important. As far as I can tell, the full list is displayed when at least one character in the namespace is capitalised.

Serious Stan is indeed correct. Good catch Stan. Jesse…mega dittos. You should consider compiling these pages and publishing a book.

When I run the script, serverinventory.ps1

i receive the following:

Incomplete string token.
At D:\wmi\serverinventory.ps1:97 char:23
+ Write-Host “Operating S <<<< ystem:” $objItem.Name

i’m also seeing the following error:

Missing expression after unary oper
At D:\wmi\inv2.ps1:118 char:2
+ -C <<<< omputername $strComputer

i’m very new to powershell and wmi, so any help would be appreciated.

By Emiliano Christian on May 19th, 2009 at 7:42 am

The MAC address in the Network Information section, is not printing because instead of $objitem it says $ojbitem

I was getting same as Chad. My editor did not bring some of the close quotes across correctly so I had mismatched quotes. Check the end of the $colitems assignment statments for a proper close quote in your editor. (“root\CIMV2?`). Good stuff!

Is there a “WMI Administrative Tools” for Server 2008?

I am having an issue with Get-WmiObject -computername $machine

The Variable $machine is an IP. But it gives the following error message if the IP is not in the hosts file.

Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)

Is there anyway that it does not use this lookup and it just uses the ip provided?

I know I am thick as a brick and I am a complete newbie to PowerShell, but I have stripped out every extraneous thing I can think of and still get the same error on every script. The most basic thing I have tried is;

$arrComputers = get-Content -Path “C:\MyScripts\Servers2.txt”

foreach ($strComputer in $arrComputers) {

Write-Host `Computer Name:` $strComputer

$strComputer = Get-WmiObject Win32_BIOS -Namespace `root\CIMV2“-computername $strComputer

write-host “BIOS: ” $objItem.Description
write-host “BIOS: ” $objItem.SMBIOSBIOSVersion
write-host
}
but when I debug I ALWAYS get an error about
Invalid parameter
At :line:10 char:28
+ $strComputer = Get-WmiObject <<<< Win32_BIOS -Namespace `root\CIMV2“-computername $strComputer

with the “t” of WMIObject highlighted. WHat have I done wrong.

Try this…
$arrComputers = get-Content -Path “C:\MyScripts\Servers2.txt”

foreach ($strComputer in $arrComputers) {
$objCollection = Get-WmiObject Win32_BIOS -comp $strComputer
foreach ($objItem in $objCollection){
Write-Host “Computer Name:” $strComputer
write-host “BIOS: ” $objItem.Description
write-host “BIOS: ” $objItem.SMBIOSBIOSVersion
Write-Host
}
}

By Seth Flowers on August 20th, 2010 at 5:13 am

Thanks for the great tutorials.

thanks for this great tutorial helped me alot
can i export the entire results to csv file?
thanks

if I name this file name.ps1

#sets the computer to local
$strComputer =”.”

#Creates a variable called $colItems which contains the WMI Object
$colItems = Get-WmiObject Win32_ComputerSystem -Namespace “root\CIMV2? `
-ComputerName $strComputer

#Use a foreach loop to iterate the $colItems (collection).
#Store the properties information in the $objItem Variable.
foreach($objItem in $colItems) {
#Use the Write-Host cmdlet to output required property information
Write-Host “Computer Manufacturer: ” $objItem.Manufacturer
Write-Host “Computer Model: ” $objItem.Model
Write-Host “Total Memory: ” $objItem.TotalPhysicalMemory “bytes”
}

error
The string starting:
At c:\Myscripts\name.ps1:18 char:64 +Write-host “Total Memory:” $objItem.TotalPhysical Memory “bytes <<<< " is missing the terminator: ". at c:\MyScripts\name.ps1:20 char:1 + <<<<
+ categoryInfo :ParserError: [] ParseException +FullQualifierdDerrrorld :Terminatior ExpectedAt End of String

This is an awesome article, thanks for publishing it!

If i get necessary information on my Powershell prompt how can i store those information in my database? Please let me know urgently.

Our computers are all in a workgroup. The admin password is different on every machine. How do I get access to do a list printer port and/or create new ones?

We are migrating our printers to a new subnet and I want to pre-create the ports.
Thanks

Great stuff here. Now to put all the output into a SQL database and this will be a very nice piece for data center inventory.

By David Carriere on March 5th, 2012 at 6:48 am

Great script and article. If anyone is having issues with a copy and paste of the code, try replacing all of the double-quotes. There seems to be implicit left/right double quotes

“System BIOS Information”

while my Posh ISE only understands the following type.

“System BIOS Information”

A search and replace fixed it all for me(maybe it’s a Canadian thing).

By David Carriere on March 5th, 2012 at 6:53 am

Ahh, it just replaced the regular double-quotes I input in my last post. Must be a word press thing.

Error:
while connecting with remote PC
Get-WmiObject : Access is denied. (Exception from HRESULT: 0×80070005 (E_ACCESSDENIED))
At C:\remoteinventory.ps1:207 char:26
+ $colItems = Get-WmiObject <<<< Win32_NetworkAdapterConfiguration -Namespace "root\CIMV2"`
+ CategoryInfo : NotSpecified: (:) [Get-WmiObject], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

By Mike Robinson on July 30th, 2012 at 6:09 pm

Hi,

Thanks for sharing an easy to follow method of discovering and using WMI-Objects in Powershell. Great Article!

Hi,

@MANOJ, open the powerhsell console in “run as adminsitrator” mode.

Hi All,

In a script there is statement like:

1) $test = {doSomething}
2) &$test

What does &$test do? Please explain.

Thanks

This was a fun article to work on and play with…not to mention, it actually might be useful right out of the box for work purposes. Thanks so much…to the author and to all who added comments.

Here are a couple things I adjusted:
In the network function, I thought having the Adapter Name would be good to have. I added this to be included in the display:
Write-Host “Adapter: ” $objItem.Description

Having memory in Bytes was pretty useless to me, here’s what it looks like in MB:
Write-Host “Total Memory: ” ([math]::round(($objItem.TotalPhysicalMemory / 1024 / 1024), 2)) “MB”

Along with that, disk space was changed to:
Write-Host “Disk Size: ” ([math]::round(($objItem.Size / 1024 / 1024 / 1024), 2)) “GB”

By someotherjim on February 8th, 2013 at 4:18 pm

You might prefer ~
Write-Host “Total Memory: ” ([math]::round($objItem.TotalPhysicalMemory/1gb)) “GB”
May depend on your PS version – it should work for kb, mb, and gb.
Cheers!

For 64 bit machines will the wmi object be Win64_ComputerSystem ?

Thanks someotherjim! Great tip!

By Janssens Stan on May 10th, 2013 at 11:23 am

I love your cours.
Win7 64 bit service pack 1.
After running WMI CIM Studio I only can choose a class but on the right side there is nothing to see! It is greyed-out completely…
Please help, thanks

By Jonathan Manheim on August 1st, 2013 at 11:53 am

I’ve been able to successfully install wmitools.exe from the MSFT site, but there is no executable that gets installed so I’ve not been able to start any of the tools, like WMI CIM Studio. At the end of the install, I get a text file with HTML instructions opened. What am I doing wrong?

I noticed that no on pointed out this fails to display the MAC address even when it is valid. any thoughts?

In response to my own previous post, the last line of the NetworkInfo function should be:

Write-Host “MAC Address:” $objItem.MACAddress

instead of:

Write-Host “MAC Address:” $ojbItem.MACAddress

By Anonymousest on October 22nd, 2013 at 3:08 pm

John, my eyes fail to show me a difference between the two lines.

Hi,

I’m trying to update DHCPdomain name under SYSTEM\CurrentControlSet\Services\Tcpip\Parameters on around 100 servers. I have worked out the logic:

Set-ItemProperty -Path “HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters” DHCPDomain -Value “blah.com.au” –Force

But how do I update the script using ‘Get-WmiObject’ to remotely update the value on all the servers? Any pointers would be of great, great help. Thanks.

K

Sorry, here’s the script I’d put together but it’s failing with the error “The RPC server is unavailable”

$server = Get-Content -Path C:\Users\DJNKKQ\Downloads\scripts\servers.txt
$namespace = “root\CIMV2″
Get-WmiObject -class Win32_Registry -computername $server -namespace $namespace
foreach ($server1 in $server){
Write-Host “Removing AI domain for: ” $server1
Set-ItemProperty -Path “HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters” DhcpDomain -Value “blah.com.au” –Force
}

Do you now how writed systeminformation from the scipt (for eg. BIOSINFO) to customvalue virtual machine in VirtualMachineManager ?
Because:
Set-SCCustomPropertyValue : Cannot bind parameter ‘InputObject’. Cannot convert the ”
stpluat00008″ value of type “System.String” to type “Microsoft.SystemCenter.VirtualMa
chineManager.ClientObject”.

By Jesse Munos on March 3rd, 2014 at 12:03 pm

A few notes.
for WMI CIM tools on Win_7 and greater. install to a shared network drive, and open the studio.htm in Internet Explorer. (So far I have only gotten it to work in IE)
Same goes for browser.htm

For more info please see this link (its in polish thus the translate.google address):
http://translate.google.com/translate?hl=en&ie=UTF8&langpair=auto%7Cen&tbb=1&u=http://blogs.technet.com/b/pfe-polska/archive/2013/11/27/wmi-cim-studio-na-windows-8-1.aspx&sandbox=0&usg=ALkJrhhCED3pfSDDC-SdsiIZNuVHOf78-g

 

Leave a Comment