PowerShell and WMI

WMI — identifying writable properties

One common mistake I see is people trying to set the value of a read only property on a WMI class.  There isn’t a quick way to see if a property is writable. Get-CimClass can be used but you have to dig into the Qualifiers for each property.

 

You can use this function to determine the read\write settings on all of the properties of a WMI class

function get-cimreadwriteproperties {
[CmdletBinding()]
param (
[string]$classname
)

$props = @()

$class = Get-CimClass -ClassName $classname
$class.CimClassProperties |
foreach {
  $prop = [ordered]@{
    Name = $psitem.Name
    Read = $false
    Write = $false
  }
 
  $psitem |
  select -ExpandProperty Qualifiers |
  foreach {
    if ($_.Name.ToLower() -eq 'read') {
      $prop.Read = $true
    }
    if ($_.Name.ToLower() -eq 'write') {
      $prop.Write = $true
    }
  }

  $props += New-Object -TypeName PSObject -Property $prop
}

$props

}

 

Take the class name as a parameter and use Get-CimClass. Iterate through the properties and foreach create an output object. Test each qualifier to determine if read or write and set out to true. Add to array and output.

 

The output looks like this

 

£> get-cimreadwriteproperties -classname Win32_bios | ft -AutoSize

Name                  Read Write
----                  ---- -----
Caption               True False
Description           True False
InstallDate           True False
Name                  True False
Status                True False
BuildNumber           True False

etc

 

 

£> get-cimreadwriteproperties -classname Win32_LogicalDisk | ft -AutoSize

Name                          Read Write
----                          ---- -----
Caption                       True False
Description                   True False
InstallDate                   True False
<truncated>

ErrorMethodology              True False
NumberOfBlocks               False False
Purpose                       True False
<truncated>
VolumeDirty                   True False
VolumeName                    True  True
VolumeSerialNumber            True False

WMI integer properties – alternative decoding options

 

WMI has many properties where the the value is an integer:

£> Get-CimInstance -ClassName Win32_LogicalDisk | Format-Table DeviceId, DriveType, Size, FreeSpace -a

DeviceId DriveType         Size    FreeSpace
-------- ---------         ----    ---------
C:               3 135810510848 120492625920
D:               5

 

In the example drive type 3 is a standard hard disk and drive type 5 is defined as a compact disk

http://msdn.microsoft.com/en-us/library/aa394173%28v=vs.85%29.aspx

 

 

Remembering these can be a pain – there are a couple of ways to decode these values.

 

You could use a hash table – I showed many examples of this in PowerShell and WMI – www.manning.com/siddaway2

 

$dtype = DATA {ConvertFRom-StringData -StringData @'
3 = Hard Drive
5 = Compact Disk
'@}
Get-CimInstance -ClassName Win32_LogicalDisk |
Format-Table DeviceId, DriveTYpe, @{N='TYpe'; E={$dtype["$($_.DriveType)"]}}, Size, FreeSpace –a

 

 

DeviceId DriveTYpe TYpe                 Size    FreeSpace
-------- --------- ----                 ----    ---------
C:               3 Hard Drive   135810510848 120495980544
D:               5 Compact Disk
  

 

 

Define the hash table via ConvertFrom-StringData . You can then just use the hash table as a look up to convert the numeric value of drive type into a descriptive name.

With WMF 5.0 and PowerShell classes there is another option

 

enum dtype {
HardDrive = 3
CompactDisk = 5
}

Get-CimInstance -ClassName Win32_LogicalDisk |
Format-Table DeviceId, DriveTYpe, @{N='TYpe'; E={[dtype]$($_.DriveType)}}, Size, FreeSpace –a

 

DeviceId DriveTYpe        TYpe         Size    FreeSpace
-------- ---------        ----         ----    ---------
C:               3   HardDrive 135810510848 120496607232
D:               5 CompactDisk

 

Create a enumeration using the enum keyword. The descriptive text CANNOT have spaces (delimited strings don’t work either). You can then substitute the enum value into your calculated field.

WMI Associations

 

I saw a question regarding finding the Win32_NetworkAdapter instance using the matching Win32_NetworkAdapterConfiguration starting point.  This answers the “which adapter has an IP address of X” type question.

 

The Index property on a Win32_NetworkAdapterConfiguration instance has the same value as the DeviceId property on the corresponding Win32_NetworkAdapter.

 

An alternative is to use the ASSOCIATORS WQL keyword.

 

That approach get s a bit messy but looks like this:

 

$query = "ASSOCIATORS OF {Win32_NetworkAdapterConfiguration.Index='18'} WHERE RESULTCLASS = Win32_NetworkAdapter"
Get-WmiObject -Query $query

 

The CIM cmdlets get a bit better

 

$config = Get-CimInstance win32_networkadapterconfiguration -Filter "Index = 18"
Get-CimAssociatedInstance -InputObject $config -ResultClassName Win32_NetworkAdapter

 

Much simpler and you avoid the WQL

WMI and CIM dates

A question on the forum asked about extracting the year from the ReleaseDate property returned by Win32_BIOS

 

They were trying to do this

Get-CimInstance Win32_BIOS | Select-Object @{n="ReleaseDate";e={$_.ConvertToDateTime($_.ReleaseDate).year()}}

 

There are 2 problems with this approach – firstly the objects that Get-CimInstance produces don’t have the ConvertToDateTime method (its added by PowerShell to the objects produces by Get-WmiObject) and secondly on a DateTime object Year is a property not a method.

 

If you use the WMI cmdlet you see this

£> Get-WmiObject -Class Win32_Bios | select Releasedate

Releasedate
-----------
20140512000000.000000+000

 

The date is in WMI format and needs to be converted.

£> Get-WmiObject Win32_BIOS | Select-Object @{n="ReleaseDate";e={$_.ConvertToDateTime($_.ReleaseDate)}}

ReleaseDate
-----------
12/05/2014 01:00:00

 

If you want just the year

£> Get-WmiObject Win32_BIOS | Select-Object @{n="ReleaseDate";e={($_.ConvertToDateTime($_.ReleaseDate)).Year}}

ReleaseDate
-----------
       2014

 

This conversion is already done for you with the CIM cmdlets

£> Get-CimInstance -CimSession $c -ClassName Win32_Bios | select ReleaseDate

ReleaseDate
-----------
12/05/2014 01:00:00

 

Again if you just want the year

£> ((Get-CimInstance -CimSession $c -ClassName Win32_Bios).ReleaseDate).Year
2014

CimInstanceProperties

If you use Get-CimInstance and examine the object returnd – you’ll find some meta-data at the end of the object:

CimClass                                  : root/cimv2:Win32_OperatingSystem
CimInstanceProperties                     : {Caption, Description, InstallDate, Name...}
CimSystemProperties                       : Microsoft.Management.Infrastructure.CimSystemProperties

 

Expand CimInstanceProperties

 

£> Get-CimInstance Win32_OperatingSystem | select -ExpandProperty CimInstanceProperties | fl *

 

And you’ll see an entry like this for each property


Name            : Caption
Value           : Microsoft Windows 8.1 Pro
CimType         : String
Flags           : Property, ReadOnly, NotModified
IsValueModified : False

 

The CimType and Flags attributes are especially useful

 

For instance you can use them to discover which properties can be modified:

 

Get-CimInstance Win32_OperatingSystem |
select -ExpandProperty CimInstanceProperties |
foreach {
$flags =   ($psitem | select -ExpandProperty Flags)  -split ", "  

if ($flags -notcontains 'ReadOnly'){$psitem}
}

 

Remember that other properties may be modifiable via a method on the class.

 

You can find similar information via Get-CimClass

 

$class = Get-CimClass -ClassName Win32_OperatingSystem

$class.CimClassProperties |
foreach {
$flags =   ($psitem | select -ExpandProperty Flags)  -split ", "  

if ($flags -notcontains 'ReadOnly'){$psitem}
}

 

Get-CimClass gives you the qualifiers as well as the flags. Get-CimInstance gives you the value of the property if set.

Two options with slightly different results. All the better to investigate CIM classes

Finding a file version

Interesting question on the forum – how to find the file version of IE on remote machines?

 

Get-CimInstance -ClassName CIM_DataFile -Filter "Name = 'C:\\Program Files\\Internet Explorer\\iexplore.exe'"  | select -ExpandProperty Version

 

Use the CIM_dataFile class.  Its one of the few CIM_ classes that doesn’t have a Win32_ equivalent.

 

In this case you know the path to the file – note the \\ as you have to escape a single \ in WMI filters

WMI troubleshooting

A WMI troubleshooting series has been started on the Ask the Performance Team Blog.

The overview article is:

http://blogs.technet.com/b/askperf/archive/2014/08/07/troubleshooting-wmi-series-coming.aspx

 

The first article is about common symptoms and errors:

http://blogs.technet.com/b/askperf/archive/2014/08/08/wmi-common-symptoms-and-errors.aspx

 

followed by an article on dealing with WMI repository corruption:

http://blogs.technet.com/b/askperf/archive/2014/08/08/wmi-repository-corruption-or-not.aspx

 

Very useful information in both articles. If the rest of the series is at this level you’ll want to book mark these posts

ServerManagerTasks module – – Get-SMServerFeature

Finding the Windows features installed on a remote machine can be an interesting task. You could use Get-WindowsFeature but that gives you a “graphical” display:

£> Get-WindowsFeature | where DisplayName -like '*DNS*'

Display Name         Name             Install State
------------         ----             -------------
[X] DNS Server       DNS              Installed
[X] DNS Server Tools RSAT-DNS-Server  Installed

Or you could use Get-SMServerFeature from the ServerManagerTasks module:

£> Get-SMServerFeature -BatchSize 1000  | where DisplayName -like '*DNS*' | sort Displayname | ft -a Displayname, State, Type, ConfigurationStatus

Displayname      State Type ConfigurationStatus
-----------      ----- ---- -------------------
DNS Server           1    0                   3
DNS Server Tools     1    2                   3

Which is CIM based and uses a new class: root/microsoft/windows/servermanager/MSFT_ServerFeature

Following the tradition firmly established since the introduction of WMI the data from this class is represented by integer values AND just for giggles its not documented. To be fair most of the WMI classes aren’t documented.

<rant>

We need documentation for these classes

</rant>

In an effort to work out which are the important values I compared the outputs from

Get-SMServerFeature -BatchSize 1000  | where DisplayName -like '*PowerShell*' | sort Displayname | ft -a Displayname, State, Type, ConfigurationStatus

AND

Get-WindowsFeature | where DisplayName -like '*PowerShell*' | sort Displayname

The important property from  Get-SMServerFeature  seems to be State which takes a 0 or 1. O corresponds to Available in Get-WindowsFeature and 1 corresponds to Installed.

You can use the trick I show in PowerShell and WMI (www.manning.com/siddaway2)

$state = DATA {ConvertFrom-StringData -StringData @'
0 = Available
1 = Installed
'@}

Get-SMServerFeature -BatchSize 1000  |
where DisplayName -like '*PowerShell*' |
sort Displayname |
select DisplayName, UniqueName,
@{N='State'; E={$state["$($_.State)"]}

$state = DATA {ConvertFrom-StringData -StringData @'
0 = Available
1 = Installed
'@}

Create a hash table for the integer values and their meaning and use that hash table in a calculated field to get the meaning from the integer value.

The output will look like this:

DisplayName : Windows PowerShell Web Access
UniqueName  : WindowsPowerShellWebAccess
State       : Available

Its now easy to compare the data between different machines.  Get-SMServerFeature has a CimSession parameter for working with remote machines

Formatting disks–the old way

Saw a question on the forums about formatting all disks but the C: drive.  Assuming you ever need to do such a destructive activity you might want think about ths sort of approach

Get-CimInstance -ClassName Win32_Volume -Filter "DeviceId != 'C:'" |
foreach {
Invoke-CimMethod -whatif -InputObject $psitem -MethodName Format -Arguments @{
   ClusterSize = 4096
   EnableCompression = $false
   FileSystem = 'NTFS'
   QuickFormat = $true
}
}

 

Notice I’ve left the –whatif in for safety

More details on the Win32_Volume class at http://msdn.microsoft.com/en-us/library/aa394515%28v=vs.85%29.aspx

Formatting disks

Saw a question on the forums about formatting all disks but the C: drive.  Assuming you ever need to do such a destructive activity you might want think about ths sort of approach Get-CimInstance -ClassName Win32_Volume -Filter “DeviceId != ‘C:’” … Continue reading