Inventorying and Evaluating Windows Systems - Windows PowerShell for Administration: The Personal Trainer (2015)

Windows PowerShell for Administration: The Personal Trainer (2015)

Chapter 3. Inventorying and Evaluating Windows Systems

Often when you are working with a user’s computer or a remote server, you’ll want to examine the working environment and computer configuration details. For example, you might want to know who is logged on, the current system time, or what accounts are available locally. You might also want to know what processor and how much RAM are installed. To do this and much more, you can take an inventory of your computers.

While you are inventorying your computers, you also might want to evaluate the hardware configuration and determine whether there are issues that need your attention. For example, if a computer’s primary disk is getting low in free space or a computer has little available memory, you’ll want to note this at the least and possibly take preventative measures.

Getting Basic System Information

Sometimes when you are working with a computer, you won’t know basic information, such as the name of the computer, the logon domain, or the current user. This can happen when you get called to support a computer in another department within your organization and when you are working remotely. Items that help you quickly gather basic user and system information include the following:

· $env:computername Displays the name of the computer

$env:computername

· $env:username Displays the name of the user currently logged on to the system, such as wrstanek

$env:username

· $env:userdomain Displays the logon domain of the current user, such as IMAGINEDLANDS

$env:userdomain

· Get-Date Displays the current system date or current system time

Get-Date [-Date] DateTime
Get-Date –DisplayHint [Date | Time]

· Set-Date Sets the current system date or current system time

Set-Date [-Date] DateTime
Set-Date [-Adjust] TimeChange

· Get-Credential Gets a credential needed for authentication

Get-Credential [-Credential] Credential

Determining the Current User, Domain, and Computer Name

Often, you can obtain the basic information you need about the working environment from environment variables. The most common details you might need to know include the name of the computer, the identity of the current user, and the logon domain.

You can obtain the user, domain, and computer name information by using $env:username, $env:userdomain, and $env:computername, respectively. In the following example and sample output, you write this information to the console:

write-host "Domain: $env:userdomain `nUser: $env:username `nComputer: $env:computername"

Domain: IMAGINEDLANDS
User: wrstanek
Computer: TECHPC76

Here, the user is wrstanek, the computer is TechPC76, and the user’s logon domain is IMAGINEDLANDS.

NOTE The grave-accent character (`) is an escape character in PowerShell. Following the escape character with n, instructs PowerShell to display the output on a new line. Other useful escape sequences are `a which plays an audible beep and `t which insert a horizontal tab.

TIP When used at the end of a line, the escape character is a line continuation marker, allowing command text to continue on the next line. The escape character can also be used to display otherwise unprintable characters. For example, if you wanted to write a single quote or double quote in the output and not have either be interpreted as a string delimiter, you’d preceed the quote character with the escape character.

A faster alternative that provides even more information about the working environment is to list all available environment variables and their values as shown in this example and sample output:

get-childitem env:

Name Value
---- -----
ALLUSERSPROFILE C:\ProgramData
APPDATA C:\Users\williams.IMAGINEDLANDS\AppData\Roaming
CLIENTNAME ROOM4-PC
CommonProgramFiles C:\Program Files\Common Files
CommonProgramFiles(x86) C:\Program Files (x86)\Common Files
CommonProgramW6432 C:\Program Files\Common Files
COMPUTERNAME CORPPC38
ComSpec C:\Windows\system32\cmd.exe
FP_NO_HOST_CHECK NO
HOMEDRIVE C:
HOMEPATH \Users\williams.IMAGINEDLANDS
LOCALAPPDATA C:\Users\williams.IMAGINEDLANDS\AppData\Local
LOGONSERVER \\CORPSERVER64
NUMBER_OF_PROCESSORS 4
OS Windows_NT
Path C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem...
PATHEXT .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;
.JSE;.WSF;.WSH;.MSC;.CPL
PROCESSOR_ARCHITECTURE AMD64
PROCESSOR_IDENTIFIER Intel64 Family 6 Model 58
PROCESSOR_LEVEL 6
PROCESSOR_REVISION 3a09
ProgramData C:\ProgramData
ProgramFiles C:\Program Files
ProgramFiles(x86) C:\Program Files (x86)
ProgramW6432 C:\Program Files
PSModulePath C:\Users\williams.IMAGINEDLANDS\Documents\...
PUBLIC C:\Users\Public
SESSIONNAME RDP-Tcp#9
SystemDrive C:
SystemRoot C:\Windows
TEMP C:\Users\WILLIA~1.IMA\AppData\Local\Temp
TMP C:\Users\WILLIA~1.IMA\AppData\Local\Temp
USERDNSDOMAIN IMAGINEDLANDS.LOCAL
USERDOMAIN IMAGINEDLANDS
USERDOMAIN_ROAMINGPROFILE IMAGINEDLANDS
USERNAME williams
USERPROFILE C:\Users\williams.IMAGINEDLANDS
windir C:\Windows

Determining and Setting the Date and Time

You can get the current date and time using Get-Date. To use Get-Date, simply type the cmdlet name at the PowerShell prompt and press Enter. The output of Get-Date is the current date and time as shown in the following example and sample output:

get-date

Friday, June 5, 2015 10:39:12 AM

If you want only the current date or current time, use the –DisplayHint parameter. While the output of Get-Date –DisplayHint Date is the current date—such as Friday, June 5, 2015—the output of Get-Date –DisplayHint Time is the current time, such as 10:39:12 AM. Here is an example and sample output:

get-date –displayhint time

10:39:12 AM

To set the date, time, or both, you must use an elevated administrator PowerShell prompt. Simply follow Set-Date with the desired date and time enclosed in quotation marks.

You enter the current date in MM-DD-YY format, where MM is for the two-digit month, DD is for the two-digit day, and YY is for the two-digit year, such as typing 07-20-15 for July 20, 2015, as shown in the following example:

set-date "07-20-15"

You enter the current time in HH:MM or HH:MM:SS format, where HH is for the two-digit hour, MM is for the two-digit minute, and SS is for the two-digit second. If you enter the time without designating AM for A.M. or PM for P.M., the time command assumes you are using a 24-hour clock, where hours from 00 to 11 indicate A.M. and hours from 12 to 23 indicate P.M. The following example sets the time to 3:30 P.M.:

set-date "3:30 PM"

You can set the date and time at the same time. All of the following examples set the date and time to July 20, 2015, 3:30 P.M.:

set-date "07-20-15 03:30 PM"
set-date "07-20-15 03:30:00 PM"
set-date "07-20-15 15:30:00"

You also can adjust the time forward or backward using the –Adjust parameter. Type Set-Date -Adjust followed by the time adjustment. Specify the time change in HH:MM:SS format. The following example sets the time ahead 30 minutes:

set-date –adjust 00:30:00

To adjust the time backward, use a minus sign (–) to indicate that you want to subtract time. The following example sets the time back one hour:

set-date –adjust -01:00:00

TIP The Get-Date cmdlet returns a DateTime object. Before you experiment with setting the date and time, store the current date and time in a variable by typing $date = get-date. When you are done testing, restore the date and time by typing set-date $date. Then adjust the time as necessary to get the current time exactly.

Specifying Authentication Credentials

When you are working with some cmdlets and objects in PowerShell that modify system information, you might need to specify a credential for authentication. Whether in a script or at the prompt, the easiest way to do this is to use Get-Credential to obtain a Credential object and save the result in a variable for later use. Consider the following example:

$cred = get-credential

When PowerShell reads this command, PowerShell prompts you for a user name and password and then stores the credentials provided in the $cred variable. It is important to point out that the credentials prompt is displayed simply because you typed Get-Credential.

You also can specify that you want the credentials for a specific user in a specific domain. In the following example, you request the credentials for the TestUser account in the DevD domain:

$cred = get-credential –credential devd\testuser

A Credential object has UserName and Password properties that you can work with. Although the user name is stored as a regular string, the password is stored as a secure, encrypted string. Knowing this, you can reference the user name and password stored in $cred as follows:

$user = $cred.username
$password = $cred.password

Examining the System Configuration and the Working Environment

Sometimes when you are working with a computer, you’ll want to obtain detailed information on the system configuration or the operating system. With mission-critical systems, you might want to save or print this information for easy reference. Items that help you gather detailed system information include the following:

· Get-HotFix Gets information about service packs and updates applied to the local computer or specified computers. Use –Id to look for a specific hotfix by its identifier. Use –Description to get hotfixes by type.

Get-HotFix [[–Id | -Description] HotFixes] {AddtlParams}

AddtlParams=
[-Credential Credential] [-ComputerName ComputerName1, ComputerName2, ...]

· Win32_ComputerSystem Lists detailed information about the local computer or a specified computer.

Get-Wmiobject -Class Win32_ComputerSystem [-ComputerName
ComputerName1, ComputerName2, ...] [–Credential Credential] | format-list *

· Win32_OperatingSystem Lists detailed information about the operating system installed on the local computer or a specified computer.

Get-Wmiobject -Class Win32_OperatingSystem [-ComputerName
ComputerName1, ComputerName2, ...] [–Credential Credential] | format-list *

· Win32_UserAccount Lists the user accounts created or available on a computer, which can include local user accounts and domain user accounts.

Get-Wmiobject -Class Win32_UserAccount [-ComputerName
ComputerName1, ComputerName2, ...] [–Credential Credential] | format-list Caption, Name, Domain, FullName, SID

· Win32_Group Lists the groups created or available on a computer, which can include local user accounts and domain user accounts.

Get-Wmiobject -Class Win32_Group [-ComputerName ComputerName1, ComputerName2, ...] [–Credential Credential] | format-list Caption, Name, Domain, SID

To use these commands on a local computer, simply type the commands on a single line using the syntax shown.

Determining Windows Updates and Service Packs

With Get-HotFix, you can use the –ComputerName parameter to specify computers to examine in a comma-separated list as shown in this example:

get-hotfix –ComputerName fileserver84, dcserver32, dbserver11

However, to access remote computers, you’ll often need to provide credentials, and you can do this using the –Credential parameter. Note that although you can provide credentials for remote connections, you typically won’t be able to provide credentials for working with the local computer (and this is why you need to start with an elevated, administrator prompt if required). The following example shows how you can prompt directly for a required credential:

get-hotfix –Credential (get-credential) –ComputerName fileserver84, dcserver32, dbserver11

You also can use a stored credential as shown in this example:

$cred = get-credential
get-hotfix –Credential $cred –ComputerName fileserver84, dcserver32, dbserver11

Because you’ll often work with the same remote computers, you might want to get the names of the remote computers from a file. To do this, enter each computer name on a separate line in a text file and save this text file to a location where you can always access it, such as a network share. Then get the content from the file as your input to the –ComputerName parameter as shown in this example:

get-hotfix –Credential (get-credential) –ComputerName (get-content c:\data\servers.txt)

Or get it as shown in this example and sample output:

$comp = get-content c:\data\servers.txt
$cred = get-credential
get-hotfix –Credential $cred –ComputerName $comp

Source Description HotFixID InstalledBy InstalledOn

------ ----------- -------- ----------- -----------

CORPPC38 Update KB3048778 NT AUTHORITY\SYSTEM
5/19/2015 12:00:00 AM
CORPPC38 Update KB3049508 NT AUTHORITY\SYSTEM
4/29/2015 12:00:00 AM
CORPPC38 Security Update KB3049563 NT AUTHORITY\SYSTEM
5/19/2015 12:00:00 AM
CORPPC38 Security Update KB3050514 NT AUTHORITY\SYSTEM
5/19/2015 12:00:00 AM
CORPPC38 Hotfix KB3046737 NT AUTHORITY\SYSTEM
5/19/2015 12:00:00 AM

Here, you are getting a list of hotfixes on a specified set of remote computers using credentials you entered when prompted. Each hotfix is listed by

· Source Shows the name of the source computer.

· Description Shows the type of hotfix. Types of hotfixes include software update, security update, and service pack. Hotfixes also can be listed simply as update or hotfix.

· HotFixID Shows the identifier for the hotfix, which can be a globally unique identifier (GUID), an update identification number, or a knowledge base identification number.

· InstalledBy Shows the name of the user who installed the update. If a specific user name is listed, this user installed the update or the update was installed on behalf of the user when the user was logged on. If the user is listed as NT AUTHORITY\SYSTEM, the update was automatically installed by Windows Update.

· InstalledOn Shows the date and time the update was installed.

Using the –Id and –Description parameters, you can look for hotfixes with a specific identifier or a specific descriptive type. In the following example and sample output, you look for security updates installed on the local computer:

get-hotfix -description "Security Update"

Source Description HotFixID InstalledBy InstalledOn

------ ----------- -------- ----------- -----------

CORPPC38 Security Update KB3008923 NT AUTHORITY\SYSTEM
4/28/2015 12:00:00 AM

CORPPC38 Security Update KB3010788 NT AUTHORITY\SYSTEM
11/13/2014 12:00:00 AM

CORPPC38 Security Update KB3011780 NT AUTHORITY\SYSTEM
11/21/2014 12:00:00 AM

CORPPC38 Security Update KB3013126 NT AUTHORITY\SYSTEM
4/28/2015 12:00:00 AM

When you are trying to determine the update status of computers throughout the enterprise, you can take this idea a step further by logging the output or by tracking computers that don’t have a particular update installed. For example, if KB3013126 is an important security update that you want to ensure is installed on specific computers, you can type the name of each computer to check on a separate line in a text file and store this list in a file named computers.txt. Next, you can use Get-HotFix to check each of these computers and log your findings. One approach is shown in the following example:

$comp = get-content c:\data\computers.txt
$comp | foreach { if (!(get-hotfix -id KB3013126 –computername `
$_)) { add-content $_ -path log.txt }}

Here, you retrieve a list of computer names and store each computer name as an item in an array called $comp, and then you use a ForEach loop to take an action on each item (computer name) in the array. That action is an If Not test that executes Get-HotFix for each computer. As a result, if a computer does not have the required hotfix, you write the computer’s name to a file called log.txt in the current working directory. When you use $_ in this way, it refers to the current item in a specified array, which in this case is the name of a computer.

Obtaining Detailed System Information

When inventorying computers in the enterprise, you’ll also want to use the Win32_OperatingSystem and Win32_ComputerSystem classes. You use the Win32_OperatingSystem object and its properties to obtain summary information regarding the operating system configuration, as shown in the following example and partial output:

Get-WmiObject -Class Win32_OperatingSystem | Format-List *

Status : OK
Name : Microsoft Windows 10 Pro
|C:\Windows|\Device\Harddisk0\Partition3
FreePhysicalMemory : 6665648
FreeSpaceInPagingFiles : 804536
FreeVirtualMemory : 7100676
BootDevice : \Device\HarddiskVolume2
BuildNumber : 9600
BuildType : Multiprocessor Free
Caption : Microsoft Windows 10 Pro
CodeSet : 1252
CountryCode : 1

When you are working with Win32_OperatingSystem, some of the most important information includes the following:

· The amount of free physical memory and free virtual memory, which are tracked in the TotalVisibleMemorySize and TotalVirtualMemorySize properties, respectively.

· The boot device, system directory, build number, build type, and operating system type, which are tracked in the BootDevice, SystemDirectory, BuildNumber, BuildType, and Caption properties, respectively.

· The encryption level and operating system architecture, which are tracked in the EncryptionLevel and OSArchitecture properties, respectively.

· The last boot-up time, which is tracked in the LastBootUp time property.

The TotalVisibleMemorySize and TotalVirtualMemorySize are shown in kilobytes. To quickly convert the values provided to megabytes, copy each value separately, paste it at the PowerShell prompt, and then type /1kb. For example, if the TotalVisibleMemorySize is 3403604, you type 3403604/1kb and the answer is 3323.832 MB.

NOTE Are you wondering why I didn’t use /1mb to get a value in megabytes? The value of the kb constant is 1024. The value of the mb constant is 1048576. If a value is in bytes, you can type /1mb to convert the value to megabytes. However, if the value is in kilobytes already, you must divide by 1024 to convert the value to megabytes.

Knowing this, you can obtain and store the memory values in megabytes using the following technique:

$os = get-wmiobject -class win32_operatingsystem
$AvailMemInMB = $os.totalvisiblememorysize/1kb
$VirtualMemInMB = $os.totalvirtualmemorysize/1kb

Whether you are at the PowerShell prompt or working in a script, the $AvailMemInMB and $VirtualMemInMB variables are then available for your use.

The boot device, system device, system directory, and build information provide essential information about the configuration of the operating system. You can use this information to determine the physical disk device on which Windows is installed, the actual directory in the file system, the type of build as either single or multiprocessor, and the exact operating system version installed.

Knowing this, you can obtain and store the related information using the following technique:

$os = get-wmiobject -class win32_operatingsystem
$BootDevice = $os.bootdevice
$SystemDevice = $os.systemdevice
$SystemDirectory = $os.systemdirectory
$BuildType = $os.buildtype
$OSType = $os.caption

You can then work with these values as necessary. For example, if you want to perform an action only when Windows 10 is installed, you can use the following technique:

$os = get-wmiobject -class win32_operatingsystem
$OSType = $os.caption

if ($OSType -match "Windows 10") {
#Windows 10 is installed; run the commands in this code block
} else {
#Windows 10 is not installed; run these commands instead
}

Using the LastBootUpTime property of the Win32_OperatingSystem object, you can determine how long a computer has been running since it was last started. To do this, you perform a comparison of the current date and time with the date and time stored in the LastBootUpTime property. However, because the value stored in this property is a string rather than a DateTime object, you must first convert the string value to a DateTime object using the ConvertToDateTime() method. An example and sample output follows:

$date = get-date
$os = get-wmiobject -class win32_operatingsystem
$uptime = $os.ConvertToDateTime($os.lastbootuptime)
write-host ($date - $uptime)

09:20:57.2639083

Here, you store the current date and time in the $date variable; then you use Get-WmiObject to get the Win32_OperatingSystem object. Next, you use the ConvertToDateTime() method to convert the string value in the LastBootUpTime property to a DateTime object. Finally, you perform a comparison of the current date and the boot date and display the difference. In this example, the computer has been running about 9 hours and 20 minutes.

You use the Win32_ComputerSystem object and its properties to obtain summary information regarding the computer configuration as shown in the following example and partial output:

Get-WmiObject -Class Win32_ComputerSystem | Format-List *

AdminPasswordStatus : 1
BootupState : Normal boot
ChassisBootupState : 3
KeyboardPasswordStatus : 2
PowerOnPasswordStatus : 1
PowerSupplyState : 3
PowerState : 0
FrontPanelResetStatus : 2
ThermalState : 3
Status : OK
Name : CORPSERVER84

With the Win32_ComputerSystem object, there is a great deal of useful information about the computer and its configuration. Some of the most important information includes the following:

· The boot-up state and status of the computer, which are tracked in the BootUpState and Status properties, respectively.

· The name, DNS host name, domain, and domain role, which are tracked in the Name, DNSHostName, Domain, and DomainRole properties, respectively.

· The system type and total physical memory, which are tracked in the properties SystemType and TotalPhysicalMemory, respectively. Note that the total memory available is shown in bytes, not kilobytes.

The boot-up state and status can help you decide whether you want to modify the configuration of a computer, which is helpful when you are working with a remote computer and you don’t know its current status. In the following example, you perform one block of commands if the computer is in a normal state and another block of commands if the computer is in a different state:

$cs = get-wmiobject -class win32_computersystem
$BootUpState = $cs.bootupstate

if ($BootUpState -match "Normal") {
"Computer is in a normal state, so run these commands."
} else {
"Computer not in a normal state, so run these commands."
}

A computer’s name and domain information also can help you decide whether you want to work with the computer. For example, although you might want to reconfigure desktops and laptops, you might not want to reconfigure servers and domain controllers. To help you avoid modifying computers of a specific type inadvertently, you can perform actions based on a computer’s role as shown in this example:

$cs = get-wmiobject -class win32_computersystem
$DomainRole = $cs.domainrole

switch –regex ($DomainRole) {
[0-1] { "This computer is a workstation." }
[2-3] { "This computer is a server but not a dc."}
[4-5] { "This computer is a domain controller."}
default { "Unknown value."}
}

In the shift to 64-bit computing, you might want to track which computers in the enterprise support 64-bit operating systems, which computers are already running 64-bit operating systems, or both. To determine whether a computer has a 64-bit operating system installed already, you can use the OSArchitecture property of the Win32_OperatingSystem object. To determine whether a computer supports a 64-bit operating system, you can use the Name and Description properties of the Win32_Processor object.

You can type the name of each computer to check on a separate line in a text file and store this list in a file called computers.txt.

Next, you can use Get-WmiObject to check each of these computers and log your findings. One approach is shown in the following example:

$comp = get-content computers.txt

#Get list of computers that don't have 64-bit OS installed
$comp | foreach {
$os = get-wmiobject -class win32_operatingsystem `
-computername $_
$OSArch = $os.osarchitecture
if (!($OSArch –match "32-bit")) { add-content $_ -path next.txt
}
}

#Determine which computers without 64-bit OS can have 64-bit OS
$comp2 = get-content next.txt
$comp2 | foreach {
$ps = get-wmiobject -class win32_processor -computername $_
$SystemType = $ps.description
if ($SystemType –like "*x64*") { add-content $_ -path final.txt
}
}

Here, you retrieve a list of computer names from a file called computers.txt in the current working directory and store each computer name as an item in an array called $comp. Then you use a ForEach loop to take an action on each item (computer name) in the array. As a result, if a computer has a 32-bit operating system installed, you write the computer’s name to a file called next.txt in the current working directory. When you use $_ in this way, it refers to the current item in a specified array, which in this case is the name of a computer.

In the final series of commands, you retrieve the list of computers that don’t have 64-bit operating systems installed and store each computer name as an item in an array called $comp2. Then you use a ForEach loop to take an action on each item (computer name) in the array. As a result, if a computer is capable of having a 64-bit operating system installed, you write the computer’s name to a file called final.txt in the current working directory. The result is a quick but clean approach to inventorying your computers. Any computer listed in final.txt is capable of having a 64-bit operating system but currently has a 32-bit operating system.

Determining Available Users and Groups

As part of your inventory of computers in the enterprise, you’ll often want to know what users and groups have been created and are available. One way to examine users and groups is to use the Win32_UserAccount and Win32_Group classes. As shown in the following example and sample output, Win32_UserAccount lists user accounts by name, domain, and more:

Get-Wmiobject -Class Win32_UserAccount | format-list Caption,Name,Domain

Caption : TECHPC76\Administrator
Name : Administrator
Domain : TECHPC76

Caption : TECHPC76\Barney
Name : Barney
Domain : TECHPC76

Here, you are working with the local computer. If the user or group was created on the local computer, the computer name is set as the domain. Otherwise, the Active Directory domain name is set as the domain.

You can use the –ComputerName parameter to specify the remote computer or computers that you want to work with and –Credential to specify credentials required for authentication. To see how these could be used, consider the following example:

$cred = get-credential
$comp = get-content c:\data\computers.txt

$comp | foreach { Get-Wmiobject -Class Win32_UserAccount `
-ComputerName $_ -Credential $cred }

Here, you prompt the user for a credential and store the credential in the $cred variable. Then you retrieve a list of computer names and store each computer name as an item in an array called $comp. Afterward, you use a ForEach loop to take an action on each item (computer name) in the array. That action is to list the user accounts available on the computer.

TIP You can enter these commands at the PowerShell prompt or use them in scripts. At the prompt, enter each command separately. In a script, place each command on a separate line. Either way works and will yield the same results. For continued lines, you might find it easier to enter the divided lines as a single line. If you do, don’t forget to remove the continuation character (`).

Because some scheduled jobs and backup processes require a computer to have a specific local user or group available, you might want to determine whether computers have this user or group. One way to do this is shown in the following example:

$comp = get-content computers.txt

#Get list of computers that don't have the BackUpUser account
$comp | foreach {

$Global:currentc = $_
$ua = get-wmiobject -class win32_useraccount -computername $_

$ua | foreach {
$user = $_.name
if ($user –eq "sqldb") {add-content $currentc -path valid.txt}
}
}

Here, you retrieve a list of computer names from a file called computers.txt in the current working directory and store each computer name as an item in an array called $comp. Then you use a ForEach loop to take an action on each item (computer name) in the $comp array. First, you store the current computer name in a global variable so that you can access it later. Then you retrieve objects representing all the user accounts on the computer and store these in an array called $ua. Next, you use a second ForEach loop to take action on each item (group object) in the $ua array. As a result, if a computer has a group called SqlDb, you write the computer’s name to a file called valid.txt in the current working directory.

Because we are using direct matching, you can use the –Filter parameter of the Get-WmiObject to get only the user account you are looking for in the first place. The –Filter parameter works like a Where clause in a WMI query. Here is an example of the revised code:

$comp = get-content computers.txt

#Get list of computers that don't have the BackUpUser account
$comp | foreach {
if (get-wmiobject -class win32_useraccount –computername `
$_ -filter "Name='sqldb'") { add-content $_ -path valid.txt }
}

Note the syntax for the Where clause in the string passed to the –Filter parameter. Because you use double quotes to enclose the string, you must use single quotes to match a specific property value.

You can work with Win32_Group in much the same way. Although this is a quick and easy way to inventory users and groups, you’ll want to use the Active Directory cmdlets to work with and manage users and groups. Other useful Win32 classes for inventorying computers include Win32_BIOS, Win32_NetworkAdapterConfiguration, Win32_PhysicalMemory, Win32_Processor, and Win32_LogicalDisk.

Evaluating System Hardware

When you are working with computers in the enterprise, you’ll often need to obtain detailed configuration information for hardware components. This configuration information will help you evaluate hardware to ensure it is functioning properly and help you diagnose and resolve difficult issues, such as hardware malfunctions and improper configurations.

Checking Firmware Versions and Status

A computer’s firmware can be the source of many hardware problems. The firmware must be configured properly and should be kept current with the latest revision.

You can use Win32_BIOS to examine the status, version, language, and manufacturer of a computer’s BIOS firmware. When you are trying to determine whether a computer’s BIOS is up to date, look at the SMBIOSBIOSVersion information. An example and sample output using WIN32_BIOS follow:

get-wmiobject win32_bios | format-list * |
Out-File -append -filepath save.txt

PSComputerName : CORPPC38
Status : OK
Name : BIOS Date: 07/31/12 18:51:30 Ver: A05.00
Caption : BIOS Date: 07/31/12 18:51:30 Ver: A05.00
SMBIOSPresent : True
__PATH :
\\CORPPC38\root\cimv2:Win32_BIOS.Name="BIOS Date: 07/31/12
18:51:30 Ver: A05.00 ",SoftwareElementID="BIOS Date: 07/31/12
BiosCharacteristics : {7, 9, 11, 12...}
BIOSVersion : {DELL - 1072009, BIOS Date: 07/31/12
18:51:30 Ver: A05.00 }
CurrentLanguage : en|US|iso8859-1
Description : BIOS Date: 07/31/12 18:51:30 Ver: A05.00
ListOfLanguages : {en|US|iso8859-1}
Manufacturer : Dell Inc.
OtherTargetOS :
PrimaryBIOS : True
ReleaseDate : 20120731000000.000000+000
SMBIOSBIOSVersion : A05
SMBIOSMajorVersion : 2
SMBIOSMinorVersion : 7
SoftwareElementID : BIOS Date: 07/31/12 18:51:30 Ver: A05.00
SoftwareElementState : 3
TargetOperatingSystem : 0
Version : DELL - 1072009

To help keep your computers current, you might want to inventory the firmware versions that are installed and determine whether computers need a firmware update. The SMBIOSBIOSVersion property provides the value you can use to do this. In the following example, you retrieve the BIOS version for a group of computers and store the computer name and BIOS version in a file called bioscheck.txt:

$comp = get-content computers.txt

#Store BIOS version for each computer
$comp | foreach {
$bios = get-wmiobject -class win32_bios -computername $_
$BiosVersion = $bios.SMBIOSBIOSVersion
add-content ("$_ $BiosVersion") -path bioscheck.txt
}

If your organization has standardized its computers, you might want to determine whether the BIOS version for a group of computers is up to date. One way to do this is to check to see if the current BIOS version is installed and log information about computers that have a different BIOS version, as shown in this example:

$comp = get-content computers.txt

$comp | foreach {
$bios = get-wmiobject -class win32_bios -computername $_
$BiosVersion = $bios.SMBIOSBIOSVersion
if (!($BiosVersion –match "A05")) {
add-content ("$_ $BiosVersion") -path checkfailed.txt }
}

Checking Physical Memory and Processors

Few things affect a computer’s performance more than the physical memory and processors that are installed. You’ll want to ensure computers have adequate memory and processors to support their daily tasks.

You can use Win32_PhysicalMemory to get detailed information for each individual DIMM of memory on a computer as well as status indicators that could indicate problems. A DIMM is a group of memory chips on a card handled as a single unit.

Many computers have an even number of memory card banks, such as two or four. A computer’s memory cards should all have the same data width and speed. An example and sample output using Win32_PhysicalMemory follow:

get-wmiobject Win32_PhysicalMemory | format-list * |
Out-File -append -filepath save.txt

__PATH : \\CORPPC38\root\cimv2:
Win32_PhysicalMemory.Tag="Physical Memory 1"
BankLabel : BANK 1
Capacity : 4294967296
Caption : Physical Memory
DataWidth : 64
Description : Physical Memory
DeviceLocator : ChannelA-DIMM1
FormFactor : 8
HotSwappable :
InstallDate :
InterleaveDataDepth : 2
InterleavePosition : 1
Manufacturer : Samsung
MemoryType : 0
Model :
Name : Physical Memory
Speed : 1600
Status :
Tag : Physical Memory 1
TotalWidth : 64
TypeDetail : 128
Version :

Depending on the type of computer and operating system, either the BankLabel entry or the DeviceLocation entry will show the channel and DIMM number, such as CHAN A DIMM 0 or ChannelA-DIMM1. Capacity is shown in bytes. To quickly convert the value provided to megabytes, copy the value and paste it at the PowerShell prompt and then type /1mb, such as 4294967296/1mb.

The Status entry tells you the current error status. The error status can help you identify a malfunctioning DIMM. The error status also can help you identify a DIMM that is not valid for the computer.

REAL WORLD Windows and Windows Server have built-in features to help you identify and diagnose problems with memory. If you suspect a computer has a memory problem that isn’t being automatically detected, you can run the Windows Memory Diagnostics utility by completing the following steps:

1. Run mdsched.exe. As an example, in the Start screen, type mdsched.exe in the Everywhere Search box, and then press Enter.

2. Choose whether to restart the computer and run the tool immediately or schedule the tool to run at the next restart.

3. Windows Memory Diagnostics runs automatically after the computer restarts and performs a standard memory test automatically. If you want to perform fewer or more tests, press F1, use the Up and Down arrow keys to set the Test Mix as Basic, Standard, or Extended, and then press F10 to apply the desired settings and resume testing.

4. When testing is completed, the computer restarts automatically. You’ll see the test results when you log on.

Note also that if a computer crashes because of failing memory, and Windows Memory Diagnostics detects this, you are prompted to schedule a memory test the next time the computer is restarted.

The total memory capacity is the sum of the capacity of all memory banks on the computer. The Win32_PhysicalMemoryArray class has a MaxCapacity property that tracks the total physical memory in kilobytes as well as a MemoryDevices property that tracks the number of memory banks.

You can use Win32_Processor to get detailed information about each processor on a computer. When you are working with processors, note the clock speed, data width, deviceID, and cache details as well as the number of cores and number of logical processor. A single processor might have multiple processor cores, and each of those processor cores might have a logical representation. Processor cache usually is shown in kilobytes. Most computers have both L2 and L3 cache. An example and sample output using Win32_Processor follow:

get-wmiobject Win32_Processor | format-list * |
Out-File -append -filepath save.txt

PSComputerName : CORPPC38
Availability : 3
CpuStatus : 1
CurrentVoltage :
DeviceID : CPU0
ErrorCleared :
ErrorDescription :
LastErrorCode :
LoadPercentage : 2
Status : OK
StatusInfo : 3
AddressWidth : 64
DataWidth : 64
ExtClock : 100
L2CacheSize : 1024
L2CacheSpeed :
MaxClockSpeed : 3201
PowerManagementSupported : False
ProcessorType : 3
Revision : 14857
SocketDesignation : CPU 1
Version :
VoltageCaps : 4
__PATH : \\CORPPC38\root\cimv2:Win32_Processor.DeviceID="CPU0"
Architecture : 9
Caption : Intel64 Family 6 Model 58 Stepping 9
ConfigManagerErrorCode :
ConfigManagerUserConfig :
CreationClassName : Win32_Processor
CurrentClockSpeed : 3200
Description : Intel64 Family 6 Model 58 Stepping 9
Family : 205
InstallDate :
L3CacheSize : 6144
L3CacheSpeed : 0
Level : 6
Manufacturer : GenuineIntel
Name : Intel(R) Core(TM) i5-3470 CPU @ 3.20GHz
NumberOfCores : 4
NumberOfLogicalProcessors : 4
OtherFamilyDescription :
PNPDeviceID :
PowerManagementCapabilities :
ProcessorId : BFEBFBFF000306A9
Role : CPU
SecondLevelAddressTranslationExtensions : True
Stepping :
SystemCreationClassName : Win32_ComputerSystem
SystemName : CORPPC38
UniqueId :
UpgradeMethod : 36
VirtualizationFirmwareEnabled : True
VMMonitorModeExtensions : True

In the output, note the ErrorCleared, ErrorDescription, and Status properties. These properties can help you identify a malfunctioning processor. Note error details or error conditions that are shown, and take corrective action as appropriate. For example, if a processor has an error status that restarting the computer doesn’t resolve, you might need to service the motherboard, the processor, or both. In some cases, updating the motherboard firmware can resolve intermittent errors.

Checking Hard Disks and Partitions

Hard disks are used to store data. Computers need enough disk space to accommodate the operating system files, the working environment, and user data. To ensure proper performance, hard disks need ample free space as well because this ensures housekeeping tasks and disk cleanup activities can be performed automatically as necessary.

WMI provides several Win32 classes for working with disk drives. Using Win32_DiskDrive, you can work with physical drives, including both fixed hard drives and USB reader devices. If you want to see only a computer’s fixed hard disks, you can filter on the media type as shown in the following example and sample output:

get-wmiobject -class win32_diskdrive -filter `
"MediaType='Fixed hard disk media'"

Partitions : 2
DeviceID : \\.\PHYSICALDRIVE0
Model : ST3500630AS
Size : 500105249280
Caption : ST3500630AS

Partitions : 1
DeviceID : \\.\PHYSICALDRIVE1
Model : ST3500630AS
Size : 500105249280
Caption : ST3500630AS
Partitions : 4
DeviceID : \\.\PHYSICALDRIVE2
Model : ST500DM002-1BD142
Size : 500105249280
Caption : ST500DM002-1BD142

The computer in this example has two fixed hard drives. PhysicalDrive0 has two disk partitions. PhysicalDrive1 has one disk partition.

If you filter the output by the device ID or caption, you can get information that is more detailed for individual fixed hard drives. In the following example and sample output, you examine a fixed hard drive by its caption:

get-wmiobject -class win32_diskdrive –filter `
"Caption='ST500DM002-1BD142'" | format-list *


ConfigManagerErrorCode : 0
LastErrorCode :
NeedsCleaning :
Status : OK
DeviceID : \\.\PHYSICALDRIVE0
StatusInfo :
Partitions : 2
BytesPerSector : 512
ConfigManagerUserConfig : False
DefaultBlockSize :
Index : 0
InstallDate :
InterfaceType : SCSI
SectorsPerTrack : 63
Size : 500105249280
TotalCylinders : 60801
TotalHeads : 255
TotalSectors : 976768065
TotalTracks : 15504255
TracksPerCylinder : 255
Caption : ST3500630AS
CompressionMethod :
ErrorCleared :
ErrorDescription :
ErrorMethodology :
FirmwareRevision : 3.AA
Manufacturer : (Standard disk drives)
MediaLoaded : True
MediaType : Fixed hard disk media
Model : ST3500630AS
Name : \\.\PHYSICALDRIVE0
SCSIBus : 0
SCSILogicalUnit : 0
SCSIPort : 0
SCSITargetId : 0

As you can see, the detailed information tells you the exact configuration of the physical device, including:

· The number of bytes per sector, sectors per track, and tracks per cylinder.

· The interface type, such as SCSI or IDE.

· The size in bytes. Divide the value by 1gb to get the size in gigabytes.

· The total number of cylinders, heads, sectors, and tracks.

· The bus, logical unit, port, and target ID.

In the output, note the ErrorCleared, ErrorDescription, ErrorMethodology, and Status properties. These properties can help you identify a malfunctioning disk. Note error details or error conditions that are shown, and take corrective action as appropriate. For example, if a processor has an error status that restarting the computer doesn’t resolve, you might need to service the hardware controller, the hard disk, or both. In some cases, updating the controller firmware can resolve intermittent errors.

You can use Win32_DiskPartition to obtain partitioning details for each fixed hard disk on the computer. The partitions correspond exactly to how you’ve partitioned fixed hard disks using Disk Management. As shown in the following example and sample output, each partition of each fixed hard disk is accessible:

get-wmiobject -class win32_diskpartition


NumberOfBlocks : 18603207
BootPartition : False
Name : Disk #0, Partition #0
PrimaryPartition : True
Size : 9524841984
Index : 0

NumberOfBlocks : 958164795
BootPartition : True
Name : Disk #0, Partition #1
PrimaryPartition : True
Size : 490580375040
Index : 1

NumberOfBlocks : 976768002
BootPartition : False
Name : Disk #1, Partition #0
PrimaryPartition : True
Size : 500105217024
Index : 0

The key information you need to know is listed as part of the standard output, so you might not need to view the extended properties. In this example, the computer has two fixed hard disks: Disk 0 and Disk 1. Disk 0 has two partitions: Partition 0 and Partition 1. Disk 1 has one partition: Partition 0. Because partition size is shown in bytes, you can divide the value listed by 1gb to get the size of the partition in gigabytes.

Windows represents formatted disk partitions as logical disks. The WMI object you can use to work with logical disks is Win32_LogicalDisk, which you can use to get detailed information for each logical disk on a computer. However, note that removable disks, CD/DVD drives, and paths assigned drive letters are also represented as logical disks. You can distinguish among these elements using the Description property. Values you’ll see include:

· CD-ROM Disc, for CD/DVD drives

· Removable Disk, for removable disks

· Local Fixed Disk, for fixed hard drives

When you are working with the logical representation of partitions on fixed hard disks, note the device ID, compression status, file system type, free space, size, and supported options. DeviceID shows the drive designator, such as C:. An example and sample output using Win32_LogicalDisk follow:

get-wmiobject -class win32_logicaldisk -filter "name='c:'" |
format-list * | Out-File -append -filepath save.txt


Status :
Availability :
DeviceID : C:
StatusInfo :
Access : 0
BlockSize :
Caption : C:
Compressed : False
ConfigManagerErrorCode :
ConfigManagerUserConfig :
CreationClassName : Win32_LogicalDisk
Description : Local Fixed Disk
DriveType : 3
FileSystem : NTFS
FreeSpace : 298870042624
InstallDate :
LastErrorCode :
MaximumComponentLength : 255
MediaType : 12
Name : C:
NumberOfBlocks :
QuotasDisabled : True
QuotasIncomplete : False
QuotasRebuilding : False
Size : 490580373504
SupportsDiskQuotas : True
SupportsFileBasedCompression : True
SystemCreationClassName : Win32_ComputerSystem
SystemName : ENGPC42
VolumeDirty : False
VolumeName :
VolumeSerialNumber : 008EA097

The FreeSpace and Size properties are shown in bytes. To quickly convert the value provided to gigabytes, copy the value, paste it at the PowerShell prompt, and then type /1gb, such as 302779912192/1gb. Here is an example and sample output:

$dr = get-wmiobject -class win32_logicaldisk -filter "name='c:'"
$free = [Math]::Round($dr.freespace/1gb)
$capacity = [Math]::Round($dr.size/1gb)

write-host $dr.name "on" $dr.systemname
write-host "Disk Capacity: $capacity"
write-host "Free Space: $free"


C: on ENGPC42
Disk Capacity: 457
Free Space: 278

Checking and Managing Device Drivers

Computers can have all sorts of hardware devices installed on and connected to them. Because all of these devices require device drivers to operate properly, you’ll often want to know detailed information about a particular device’s driver. For example, you might want to know whether the device driver is:

· Enabled or disabled.

· Running or stopped.

· Configured to start automatically.

The Win32 class for working with device drivers is Win32_SystemDriver. Using Win32_SystemDriver, you can obtain detailed configuration and status information on any device driver configured for use on a computer. You can examine device drivers by device display name using the DisplayName property, by state using the State property, or by start mode using the StartMode property. The display name for a device and its driver is the same as the one shown in Device Manager.

In the following example, you use the DisplayName property to check the RAID controller on a computer:

get-wmiobject -class win32_systemdriver | where-object `
{$_.displayname -like "*raid c*"} | format-list *


Status : OK
Name : iaStor
State : Running
ExitCode : 0
Started : True
ServiceSpecificExitCode : 0
AcceptPause : False
AcceptStop : True
Caption : Intel RAID Controller
CreationClassName : Win32_SystemDriver
Description : Intel RAID Controller
DesktopInteract : False
DisplayName : Intel RAID Controller
ErrorControl : Normal
InstallDate :
PathName : C:\Windows\system32\drivers\iastor.sys
ServiceType : Kernel Driver
StartMode : Boot
StartName :
SystemCreationClassName : Win32_ComputerSystem
SystemName : ENGPC42
TagId : 25
Site :
Container :

Generally, State is shown as either Running or Stopped. Knowing this, you can check for device drivers in either state as shown in the following example and sample output:

get-wmiobject -class win32_systemdriver -filter "state='Running'"

DisplayName : Microsoft ACPI Driver
Name : ACPI
State : Running
Status : OK
Started : True

DisplayName : Ancillary Function Driver for Winsock
Name : AFD
State : Running
Status : OK
Started : True

The start mode can be set as:

· Boot Used for boot device drivers

· Manual Used for device drivers that are started manually

· Auto Used for device drivers that are started automatically

· System Used for system device drivers

Using StartMode for your filter, you can list boot device drivers as shown in the following example:

get-wmiobject -class win32_systemdriver -filter "startmode='Boot'"

The Win32_SystemDriver class provides a number of methods for managing system drivers. These methods include:

· Change() Changes the device driver configuration. It accepts the following parameters in the following order: DisplayName, PathName, ServiceTypeByte, ErrorControlByte, StartMode, DesktopInteractBoolean, StartName, StartPassword, LoadOrderGroup, LoadOrderGroupDependenciesArray, and ServiceDependenciesArray.

CAUTION Modifying devices at the PowerShell prompt is not something you should do without careful forethought. PowerShell lets you make changes that will make your computer unbootable. Before you make any changes to devices, you should create a system restore point as discussed in Chapter 6, “Managing and Securing the Registry.” You also might want to consider performing a full backup of the computer.

· ChangeStartMode() Changes the start mode of the device driver. It accepts a single parameter, which is the start mode to use. Valid values are boot, manual, auto, or system.

CAUTION Before you change the start mode of a device driver, ensure the driver supports this start mode. You also should ensure that the start mode won’t affect the computer’s ability to start.

· Delete() Deletes the device driver (if the device is in a state that allows this). Deleting the device driver doesn’t prevent the device from being used. To prevent the device from being used, you should disable it instead. If you delete a device driver without disabling a device, Windows will, in most cases, detect and reinstall the device the next time the computer is started. As part of troubleshooting, you can sometimes delete a device’s driver to force Windows to reinstall the device.

CAUTION Exercise extreme caution if you plan to delete device drivers using PowerShell. PowerShell will not warn you if you are making harmful changes to your computer.

· InterrogateService() Connects to the device using the device driver. If the return value is zero, WMI was able to connect to and interrogate the device using the device driver. If the return value isn’t zero, WMI encountered a problem while trying to communicate with the device using the device driver. If the device is stopped or paused, this method always returns an error status.

· PauseService() Pauses the device, which might be necessary during troubleshooting or diagnostics. Devices that can be paused indicate this when the AcceptPause property is set to True for their device drivers. Further, you can pause only a device that is in a state where pausing is permitted.

· ResumeService() Resumes the device after it has been paused.

· StopService() Stops the device, which might be necessary during troubleshooting or diagnostics. Devices that can be stopped indicate this when the AcceptStop property is set to True for their device drivers. Further, you can stop only a device that is in a state where stopping is permitted.

· StartService() Starts a stopped device, including devices that are configured for manual start up.

If you want to change the start mode for a device driver, you can use the ChangeStartMode() method to specify the desired start mode. The basic syntax is

$driverObject.ChangeStartMode(StartMode)

where $driverObject is a reference to a Win32_SystemDriver object, and StartMode is the desired start mode entered as a string value, as shown in this example and sample output:

$d = get-wmiobject -class win32_systemdriver | where-object `
{$_.displayname -like "creative audio*"}

$d.changestartmode("auto")

__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ReturnValue : 0

Here, you set the start mode to Auto for a Creative Audio device. The return value in the output is what you want to focus on. A return value of 0 (zero) indicates success. Any other return value indicates an error. Typically, errors occur because you aren’t using an elevated administrator PowerShell prompt, you haven’t accessed the correct device driver, or the device isn’t in a state in which it can be configured. Keep in mind that if you alter the configuration of required device drivers, you might not be able to start the computer. Because of this, don’t make any changes to device drivers without careful planning and forethought.

Digging In Even More

Want to really dig in and explore what’s available on a computer? Enter the following command as a single line to list every available .NET type:

[System.AppDomain]::CurrentDomain.GetAssemblies() |
Foreach-Object { $_.GetTypes() }

You can expand on this idea by creating a function and then calling this function with various filters to find specific .NET types. The code for a ListType function follows:

function ListType() {
[System.AppDomain]::CurrentDomain.GetAssemblies() |
Foreach-Object { $_.GetTypes() }
}

To list all .NET types, you can call the ListType function without any filters, as shown in this example:

ListType

You can view specific .NET types if you check for names that are like a specified value. For example, to list all .NET types with “parser” as part of the name, you could enter

ListType | ? { $_.Name -like "*parser*" }

To learn more about a .NET type, you can look at the constructors for the type. The following example lists the constructors for all .NET types with “parser” as part of the name:

ListType | ? { $_.Name -like "*parser*" } |
% { $_.GetConstructors() }

Pretty cool. However, not every .NET type is loaded for use. Therefore, to use a .NET type you find, you might need to load it before you use it.

Another cool trick is to examine the available COM objects on a computer. COM objects are registered in the registry, and by exploring the appropriate registry branches, you can find COM objects that are registered for use on the computer. A function for checking the registry follows:

function ListProgID {
param()
$paths = @("REGISTRY::HKEY_CLASSES_ROOT\CLSID")
if ($env:Processor_Architecture -eq "amd64") {
$paths+="REGISTRY::HKEY_CLASSES_ROOT\Wow6432Node\CLSID" }

Get-ChildItem $paths -include VersionIndependentPROGID `
-recurse | Select-Object @{
Name='ProgID'
Expression={$_.GetValue("")}
}, @{
Name='Type'
Expression={
if ($env:Processor_Architecture -eq "amd64") { "Wow6432" }
else { "32-bit" }
}
}
}

Here, you check the processor architecture on the computer. If the computer is running a 32-bit operating system, you look under HKEY_CLASSES_ROOT\CLSID for 32-bit COM objects. If the computer is running a 64-bit operating system, you look under HKEY_CLASSES_ROOT\CLSID for 32-bit COM objects and under HKEY_CLASSES_ROOT\Wow6432Node\CLSID for additional COM objects. You then list the registered COM objects by their progID and type.

To use this function to list all COM objects by their ProgID and type, you could enter the following command:

ListProgID

You can view specific COM objects if you check for names that are like a specified value. For example, to list all COM objects with “Microsoft” as part of the name, you could enter

ListProgID | Where-Object { $_.ProgID -like "*Microsoft*" }

Have fun; there’s a lot here to explore. For more information on objects, .NET types, and COM objects, see Windows PowerShell: The Personal Trainer.