Monthly Archive

Categories

PowerShell and WMI

Dealing with CIM properties that are integer arrays

Saw a post about WmiMonitorID that intrigued me

 

If you use the WmiMonitorID:

 

PS> Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorID | select -f 1

Active                 : True
InstanceName           : DISPLAY\GSM598F\4&19086f00&0&UID200195_0
ManufacturerName       : {71, 83, 77, 0...}
ProductCodeID          : {53, 57, 56, 70...}
SerialNumberID         : {51, 48, 52, 78...}
UserFriendlyName       : {50, 50, 69, 65...}
UserFriendlyNameLength : 13
WeekOfManufacture      : 4
YearOfManufacture      : 2013
PSComputerName         :

 

You get a number of properties returned as an array of numbers. if you look at the property with Get-CimClass they unsigned 16 bit integers

Name               : ManufacturerName
Value              :
CimType            : UInt16Array
Flags              : Property, ReadOnly, NullValue
Qualifiers         : {MAX, read, WmiDataId}
ReferenceClassName :

 

Probably the easiest way to deal with them is a very simple function and calculated fields

 

function Convert-ArrayToName {
param ($array)

($array | foreach { [char][byte]$_} ) -join ''

}

Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorID |
select Active,
@{N='Manufacturer'; E={Convert-ArrayToName -array $_.ManufacturerName }},
@{N='ProductCode'; E={Convert-ArrayToName -array $_.ProductCodeID}},
@{N='SerialNumber'; E={Convert-ArrayToName -array $_.SerialNumberID}},
@{N='UserFriendlyName'; E={Convert-ArrayToName -array $_.UserFriendlyName}},
WeekOfManufacture,YearOfManufacture

 

The function Convert-ArrayToName accepts an array.  Using foreach-object the integers are converted to bytes and then to chars. Join the resultant array of chars and you get the string versions of the property

 

Call the function in a calculated field to convert the numeric array to a string – repeat for all relevant properties. You could create an object rather than just using select if you wish

 

Run the code and

Active                 : True
InstanceName           : DISPLAY\GSM598F\4&19086f00&0&UID200195_0
ManufacturerName       : {71, 83, 77, 0...}
ProductCodeID          : {53, 57, 56, 70...}
SerialNumberID         : {51, 48, 52, 78...}
UserFriendlyName       : {50, 50, 69, 65...}
UserFriendlyNameLength : 13
WeekOfManufacture      : 4
YearOfManufacture      : 2013
PSComputerName         :

 

Active                 : True
InstanceName           : DISPLAY\SEC3242\4&19086f00&0&UID265988_0
ManufacturerName       : {83, 69, 67, 0...}
ProductCodeID          : {51, 50, 52, 50...}
SerialNumberID         : {48, 0, 0, 0...}
UserFriendlyName       :
UserFriendlyNameLength : 0
WeekOfManufacture      : 0
YearOfManufacture      : 2012
PSComputerName         :

 

 

becomes

 

Active            : True
Manufacturer      : GSM            
ProductCode       : 598F           
SerialNumber      : 304NDJX51788   
UserFriendlyName  : 22EA63      
WeekOfManufacture : 4
YearOfManufacture : 2013

 

Active            : True
Manufacturer      : SEC            
ProductCode       : 3242           
SerialNumber      : 0              
UserFriendlyName  : 
WeekOfManufacture : 0
YearOfManufacture : 2012

Optimising WMI calls–part 3

The next change just uses 1 call to get the disk information instead of 2

 

Measure-Command -Expression {

$srvs = 'W16TP5TGT01', 'W16TP5TGT02'

for ($i=1; $i -le 150; $i++){

foreach ($srv in $srvs) {
$cs = New-CimSession -ComputerName $srv
$bootupMemory = Get-CimInstance -Query "SELECT * FROM Win32_OperatingSystem" -CimSession $cs
$cpuLoad = Get-CimInstance -Query "SELECT * FROM Win32_Processor" -CimSession $cs

$tSessions = Get-CimInstance -Query "SELECT * FROM Win32_TerminalService" -CimSession $cs

$sfilt = "Name='imaservice' OR Name='mfcom' OR Name='cpsvc' OR Name='msmq'"
$reqserv = Get-CimInstance -ClassName Win32_Service -Filter $sfilt -CimSession $cs
 
$ima = $reqserv | where Name -eq 'imaservice'
$mfcom = $reqserv | where Name -eq 'mfcom'
$ctxPrintMgr = $reqserv | where Name -eq 'cpsvc'
$msmqstatus = $reqserv | where Name -eq 'msmq'

$dfilt = "Deviceid='c:' OR Deviceid='D:'"
$drives = Get-CimInstance -ClassName Win32_Logicaldisk -Filter $dfilt -CimSession $cs
 
$cDrive = $drives | where deviceid -eq 'c:'
$dDrive = $drives | where deviceid -eq 'd:'
Remove-CimSession -CimSession $cs
}
}
}

 

Time now becomes

Days              : 0
Hours             : 0
Minutes           : 6
Seconds           : 36
Milliseconds      : 923
Ticks             : 3969235528
TotalDays         : 0.00459402260185185
TotalHours        : 0.110256542444444
TotalMinutes      : 6.61539254666667
TotalSeconds      : 396.9235528
TotalMilliseconds : 396923.5528

 

Not such a dramatic change but overall we’re now taking 26.4% less time to run the code. 

Optimising WMI calls–part 2

Last time we looked at using CIM sessions to make a set of WMI calls run quicker. This time we’ll reduce the number of calls.  I’m deliberately just reducing the number of calls to the Win32_Service class.  We’ll look at the disks another time

 

Our code becomes

Measure-Command -Expression {

$srvs = 'W16TP5TGT01', 'W16TP5TGT02'

for ($i=1; $i -le 150; $i++){

foreach ($srv in $srvs) {
$cs = New-CimSession -ComputerName $srv
$bootupMemory = Get-CimInstance -Query "SELECT * FROM Win32_OperatingSystem" -CimSession $cs
$cpuLoad = Get-CimInstance -Query "SELECT * FROM Win32_Processor" -CimSession $cs

$tSessions = Get-CimInstance -Query "SELECT * FROM Win32_TerminalService" -CimSession $cs

$sfilt = "Name='imaservice' OR Name='mfcom' OR Name='cpsvc' OR Name='msmq'"
$reqserv = Get-CimInstance -ClassName Win32_Service -Filter $sfilt -CimSession $cs
 
$ima = $reqserv | where Name -eq 'imaservice'
$mfcom = $reqserv | where Name -eq 'mfcom'
$ctxPrintMgr = $reqserv | where Name -eq 'cpsvc'
$msmqstatus = $reqserv | where Name -eq 'msmq'

$cDrive = Get-CimInstance -Query "SELECT * FROM Win32_Logicaldisk WHERE deviceid='c:'" -CimSession $cs
$dDrive = Get-CimInstance -Query "SELECT * FROM Win32_Logicaldisk WHERE deviceid='d:'" -CimSession $cs
Remove-CimSession -CimSession $cs
}
}
}

 

The change is to create a filter that pulls back JUST the services we want. Use that to create a collection of Win32_Service objects and then populate the variables with the required service data

 

Time drops dramatically

Days              : 0
Hours             : 0
Minutes           : 6
Seconds           : 50
Milliseconds      : 133
Ticks             : 4101339515
TotalDays         : 0.0047469207349537
TotalHours        : 0.113926097638889
TotalMinutes      : 6.83556585833333
TotalSeconds      : 410.1339515
TotalMilliseconds : 410133.9515

 

Total time goes from 539.42 seconds to 410.13 seconds.  That’s reduced the time by 23.96%

 

These are just simple coding changes remember- we’re not performing any clever parallel processing here

Optimising WMI calls–part 1

Recently saw some code where user was running this

$bootupMemory = gwmi -Query "SELECT * FROM Win32_OperatingSystem" -ComputerName $srv
$cpuLoad = gwmi -Query "SELECT * FROM Win32_Processor" -ComputerName $srv

$tSessions = gwmi -Query "SELECT * FROM Win32_TerminalService" -ComputerName $srv

$ima = gwmi -Query "SELECT * FROM Win32_Service WHERE name='imaservice'" -ComputerName $srv
$mfcom = gwmi -Query "SELECT * FROM Win32_Service WHERE name='mfcom'" -ComputerName $srv
$ctxPrintMgr = gwmi -Query "SELECT * FROM Win32_Service WHERE name='cpsvc'" -ComputerName $srv
$msmqstatus = gwmi -Query "SELECT * FROM Win32_Service WHERE name='msmq'" -ComputerName $srv

$cDrive = gwmi -Query "SELECT * FROM Win32_Logicaldisk WHERE deviceid='c:'" -ComputerName $srv
$dDrive = gwmi -Query "SELECT * FROM Win32_Logicaldisk WHERE deviceid='d:'" -ComputerName $srv

 

against 300 machines.  There were some more calls they involved WMI classes installed by Citrix which I don’t use in my lab

 

Question was why was it running slow

 

Two thoughts initially are that repeated calls to Get-WmiObject involve creating, using and removing DCOM connections. This assumes that DCOM isn’t blocked by a firewall or the network. Using a single CIM session should speed up the process.

 

Secondly making multiple calls to the same class is inefficient.

 

In addition using the WQL query involves more typing which makes things more difficult to maintain.

 

Last point is that everyone knows how much I love aliases so you won’t be surprised if I point out that using them is BAD

 

I don’t have 300 servers in my lab – though using nano server VMs on a machine with 64GB ram you could do that – so I used some PowerShell looping to get round that

Measure-Command -Expression {

$srvs = 'W16TP5TGT01', 'W16TP5TGT02'

for ($i=1; $i -le 150; $i++){

foreach ($srv in $srvs) {
$bootupMemory = gwmi -Query "SELECT * FROM Win32_OperatingSystem" -ComputerName $srv
$cpuLoad = gwmi -Query "SELECT * FROM Win32_Processor" -ComputerName $srv

$tSessions = gwmi -Query "SELECT * FROM Win32_TerminalService" -ComputerName $srv

$ima = gwmi -Query "SELECT * FROM Win32_Service WHERE name='imaservice'" -ComputerName $srv
$mfcom = gwmi -Query "SELECT * FROM Win32_Service WHERE name='mfcom'" -ComputerName $srv
$ctxPrintMgr = gwmi -Query "SELECT * FROM Win32_Service WHERE name='cpsvc'" -ComputerName $srv
$msmqstatus = gwmi -Query "SELECT * FROM Win32_Service WHERE name='msmq'" -ComputerName $srv

$cDrive = gwmi -Query "SELECT * FROM Win32_Logicaldisk WHERE deviceid='c:'" -ComputerName $srv
$dDrive = gwmi -Query "SELECT * FROM Win32_Logicaldisk WHERE deviceid='d:'" -ComputerName $srv
}
}
}

Measure command will run the commands but it reports the time taken rather than the results

 

I have 2 servers and loop 150 times round connecting to them and getting the results

Some caching of connectivity information occurs but this is close enough

 

Days              : 0
Hours             : 0
Minutes           : 8
Seconds           : 59
Milliseconds      : 419
Ticks             : 5394194858
TotalDays         : 0.00624328108564815
TotalHours        : 0.149838746055556
TotalMinutes      : 8.99032476333333
TotalSeconds      : 539.4194858
TotalMilliseconds : 539419.4858

 

A time of just under 9 minutes isn’t bad. This is fast enough to run during the day and can definitely be run over night

 

But we should be able to do better than that

 

Lets use a CIM session instead of individual DCOM sessions

Measure-Command -Expression {

$srvs = 'W16TP5TGT01', 'W16TP5TGT02'

for ($i=1; $i -le 150; $i++){

foreach ($srv in $srvs) {
$cs = New-CimSession -ComputerName $srv
$bootupMemory = Get-CimInstance -Query "SELECT * FROM Win32_OperatingSystem" -CimSession $cs
$cpuLoad = Get-CimInstance -Query "SELECT * FROM Win32_Processor" -CimSession $cs

$tSessions = Get-CimInstance -Query "SELECT * FROM Win32_TerminalService" -CimSession $cs

$ima = Get-CimInstance -Query "SELECT * FROM Win32_Service WHERE name='imaservice'" -CimSession $cs
$mfcom = Get-CimInstance -Query "SELECT * FROM Win32_Service WHERE name='mfcom'" -CimSession $cs
$ctxPrintMgr = Get-CimInstance -Query "SELECT * FROM Win32_Service WHERE name='cpsvc'" -CimSession $cs
$msmqstatus = Get-CimInstance -Query "SELECT * FROM Win32_Service WHERE name='msmq'" -CimSession $cs

$cDrive = Get-CimInstance -Query "SELECT * FROM Win32_Logicaldisk WHERE deviceid='c:'" -CimSession $cs
$dDrive = Get-CimInstance -Query "SELECT * FROM Win32_Logicaldisk WHERE deviceid='d:'" -CimSession $cs
Remove-CimSession -CimSession $cs
}
}
}

 

Time is now

Days              : 0
Hours             : 0
Minutes           : 8
Seconds           : 31
Milliseconds      : 839
Ticks             : 5118397269
TotalDays         : 0.00592407091319444
TotalHours        : 0.142177701916667
TotalMinutes      : 8.530662115
TotalSeconds      : 511.8397269
TotalMilliseconds : 511839.7269

 

That’s a 5% speed increase for minimal coding effort

 

Next step is to remove the redundant WMI calls which we’ll do in the next post

WMI classes and Storage cmdlets

There is a hierarchy of objects to work through when dealing with disks

First you have the physical disk

PS>  Get-CimInstance -ClassName Win32_DiskDrive | fl

Partitions : 5
DeviceID   : \\.\PHYSICALDRIVE0
Model      : HFS256G3AMNB-2200A
Size       : 256052966400
Caption    : HFS256G3AMNB-2200A

 

A physical disk can have 1 or more partitions:

PS>  Get-CimInstance -ClassName Win32_DiskPartition | fl

NumberOfBlocks   : 716800
BootPartition    : False
Name             : Disk #0, Partition #0
PrimaryPartition : False
Size             : 367001600
Index            : 0

 

NumberOfBlocks   : 409600
BootPartition    : True
Name             : Disk #0, Partition #1
PrimaryPartition : True
Size             : 209715200
Index            : 1

 

NumberOfBlocks   : 485312512
BootPartition    : False
Name             : Disk #0, Partition #2
PrimaryPartition : True
Size             : 248480006144
Index            : 2

 

NumberOfBlocks   : 921600
BootPartition    : False
Name             : Disk #0, Partition #3
PrimaryPartition : False
Size             : 471859200
Index            : 3

 

NumberOfBlocks   : 12492800
BootPartition    : False
Name             : Disk #0, Partition #4
PrimaryPartition : False
Size             : 6396313600
Index            : 4

 

next step down is logical disks

PS>  Get-CimInstance -ClassName Win32_LogicalDisk | fl

DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 108900372480
Size         : 248480002048
VolumeName   : Windows

 

The classes Win32_DiskDriveToDiskPartition and Win32_LogicalDiskToPartition  link physical disks to partitions and partitions to logical disks respectively.

 

Then you’ve got volumes – which is where you actually work with disks for the most part

PS>  Get-CimInstance -ClassName Win32_Volume | fl Caption, Label

Caption : C:\
Label   : Windows

 

Caption : \\?\Volume{524b798f-a072-4ecc-8cfe-fb823e10a5e7}\
Label   : Windows RE tools

 

Caption : \\?\Volume{4ea44e2e-dd30-4cd9-bfd1-c991be836d97}\
Label   :

 

Caption : \\?\Volume{c671d23c-f5e5-473d-b6c4-fecb4a99e5b3}\
Label   : Recovery image

 

The Storage module introduced with Windows 8 has cmdlets for some of these tasks:

PS>  Get-PhysicalDisk | fl FriendlyName, SerialNumber, CanPool, OperationalStatus, HealthStatus, Usage
, Size

FriendlyName      : HFS256G3AMNB-2200A
SerialNumber      : EI3AN118813AM3740
CanPool           : False
OperationalStatus : OK
HealthStatus      : Healthy
Usage             : Auto-Select
Size              : 256060514304

 

Partitions:

PS>  Get-Partition | fl PartitionNumber, DriveLetter, Offset, Size, Type

PartitionNumber : 1
DriveLetter     :
Offset          : 1048576
Size            : 367001600
Type            : Recovery

 

PartitionNumber : 2
DriveLetter     :
Offset          : 368050176
Size            : 209715200
Type            : System

 

PartitionNumber : 3
DriveLetter     :
Offset          : 577765376
Size            : 134217728
Type            : Reserved

 

PartitionNumber : 4
DriveLetter     : C
Offset          : 711983104
Size            : 248480006144
Type            : Basic

 

PartitionNumber : 5
DriveLetter     :
Offset          : 249191989248
Size            : 471859200
Type            : Recovery

 

PartitionNumber : 6
DriveLetter     :
Offset          : 249663848448
Size            : 6396313600
Type            : Recovery

 

Get-Disk returns similar, but not identical, information to Get-PhysicalDisk

 

The Get-Disk cmdlet gets one or more Disk objects visible to the operating system, or optionally a filtered list.

The Get-Disk cmdlet gets one or more Disk objects visible to the operating system, or optionally a filtered list.

 

There isn’t a cmdlet to get logical disks

 

For volumes:

PS>  Get-Volume | fl DriveLetter, FileSystemLabel, FileSystem, DriveType, HealthStatus, OperationalSta
tus, SizeRemaining, Size

DriveLetter       : C
FileSystemLabel   : Windows
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 108473528320
Size              : 248480002048

 

DriveLetter       :
FileSystemLabel   :
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 122884096
Size              : 471855104

 

DriveLetter       :
FileSystemLabel   : Windows RE tools
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 61980672
Size              : 366997504

 

DriveLetter       :
FileSystemLabel   : Recovery image
FileSystem        : NTFS
DriveType         : Fixed
HealthStatus      : Healthy
OperationalStatus : OK
SizeRemaining     : 476807168
Size              : 6396309504

 

As you can see from this quick comparison the same sorts of information is available from the storage cmdlets and WMI. In fact under the hood the storage cmdlets are using WMI – but a set of new classes defined in ROOT/Microsoft/Windows/Storage

WMI Filters

A common mistake with WMI/CIM filters is:

PS>  Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceId=C:"
Get-WmiObject : Invalid query "select * from Win32_LogicalDisk where DeviceId=C:"
At line:1 char:1
+ Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceId=C:"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-WmiObject], ManagementException
    + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCo
   mmand

 

The clue is in the invalid query error message

When you use the –Filter parameter and are testing a property of type string the value you are testing against has to be in quotes

PS>  Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceId='C:'"

DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 108999528448
Size         : 248480002048
VolumeName   : Windows

 

The filter is defined as a string so you need to use single quotes inside the double quotes. You could mess around with all single quotes but then you have to escape the inner set of quotes – good luck with that – its an unnecessary exercise in frustration

 

How do you know the property is a string?

PS>  (Get-CimClass -ClassName Win32_LogicalDisk).CimClassProperties['DeviceId']

Name               : DeviceID
Value              :
CimType            : String
Flags              : Property, Key, ReadOnly, NullValue
Qualifiers         : {CIM_Key, read, key, MappingStrings...}
ReferenceClassName :

 

The same rules for –Filter apply to Get-CimInstance

Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceId='C:'"

Cim session oddity

The CIM cmdlets were introduced with PowerShell 3.0.  You can use the –ComputerName parameter to access a remote machine or, if you need to run multiple commands to the remote machine, you can create a CIM session.

 

CIM sessions are analogous to PowerShell remoting sessions and use WSMAN by default to connect to the remote machine:

PS> $c12 = New-CimSession -ComputerName W12R2SUS

PS> Get-CimInstance -CimSession $c12 -ClassName Win32_OperatingSystem | fl

SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 9600
RegisteredUser  : Windows User
SerialNumber    : 00252-00107-57895-AA282
Version         : 6.3.9600
PSComputerName  : W12R2SUS

 

In this case I’m accessing a Windows 2012 R2 system

 

If you try to create a CIM session to a machine running PowerShell 2.0 it will appear to work but you’ll get an error when you try to access the session:

PS> $c8 = New-CimSession -ComputerName W8R2STD01
PS> Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem | fl
Get-CimInstance : The WS-Management service cannot process the request. A DMTF resource URI was used to access a non-DMTF class. Try again using a non-DMTF resource URI.
At line:1 char:1
+ Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem | fl
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (root\cimv2:Win32_OperatingSystem:String) [Get-CimInstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80338139,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand
    + PSComputerName        : W8R2STD01

 

The reason is that the version of WSMAN installed with with PowerShell 2.0 (WSMAN 2.0) isn’t compatible with CIM sessions which expect WSMAN 3.0

 

One option is to use a DCOM based session:

PS> $opt = New-CimSessionOption -Protocol Dcom
PS> $c8D = New-CimSession -ComputerName W8R2STD01 -SessionOption $opt
PS> Get-CimInstance -CimSession $c8D -ClassName Win32_OperatingSystem | fl

SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 7601
RegisteredUser  : Windows User
SerialNumber    : 00477-179-0000007-84050
Version         : 6.1.7601
PSComputerName  : W8R2STD01

 

PowerShell MVP Jeff Hicks discovered that if you use a filter parameter with Get-CimInstance you can access PowerShell 2.0 machines using a WSMAN based CIM session

PS> Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem -Filter "Caption LIKE '%'"  | fl

SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 7601
RegisteredUser  : Windows User
SerialNumber    : 00477-179-0000007-84050
Version         : 6.1.7601
PSComputerName  : W8R2STD01

 

In this case you’re filtering on the Caption being like any characters

 

I stood in for a speaker who was ill at the recent European PowerShell conference and part of the session was on using CIM sessions. This issue came up and I decided to investigate a bit closer

Without a filter:

PS> Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem -Verbose
VERBOSE: Perform operation 'Enumerate CimInstances' with following parameters, ''namespaceName' =
root\cimv2,'className' = Win32_OperatingSystem'.
Get-CimInstance : The WS-Management service cannot process the request. A DMTF resource URI was used to access a
non-DMTF class. Try again using a non-DMTF resource URI.
At line:1 char:1
+ Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem -Ver ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (root\cimv2:Win32_OperatingSystem:String) [Get-CimInstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80338139,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand
    + PSComputerName        : W8R2STD01

VERBOSE: Operation 'Enumerate CimInstances' complete.

 

An attempt is made to enumerate the instances of the Win32_OperatingSystem class

 

If you use a filter

PS> Get-CimInstance -CimSession $c8 -ClassName Win32_OperatingSystem -Filter "Caption LIKE '%'"  -Verbose | fl
VERBOSE: Perform operation 'Query CimInstances' with following parameters, ''queryExpression' = SELECT * FROM
Win32_OperatingSystem WHERE Caption LIKE '%','queryDialect' = WQL,'namespaceName' = root\cimv2'.

SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 7601
RegisteredUser  : Windows User
SerialNumber    : 00477-179-0000007-84050
Version         : 6.1.7601
PSComputerName  : W8R2STD01

VERBOSE: Operation 'Query CimInstances' complete.

 

You’re sending a WQL  query to the remote machine.

 

My current theory is that Get-CimInstance is trying to enumerate the instances of a particular class (in a similar way to Get-WSmnaInstance does) and that fails due to the WSMAN version mismatch.  Using the Filter bypasses the enumeration allowing it to work.

 

This is a totally undocumented feature and there is no guarantee it will continue to work in future versions. Until PowerShell 2.0 is gone from you environment be aware that its an option but be careful

Folder creation dates from WMI

A question on the powershell.org about finding the creation date of folders raises some interesting points

 

To find a folder’s creation date use:

Get-WmiObject -Class Win32_Directory -Filter "Drive='C:' AND Path = '\\users\\$user\\'" | select Name, @{N='Creation date'; E={$_.ConvertToDateTime($_.CreationDate)}}

 

OR

 

Get-CimInstance -ClassName Win32_Directory -Filter "Drive='C:' AND Path = '\\users\\$user\\'" | select Name, CreationDate

 

If you use Get-WmiObject the date is returned in the form

20160128110039.938756+000

 

Which is why you need to perform the conversion using the ConvetToDateTime method that PowerShell adds to every WMI object.

 

Get-CimInstance automatically performs the conversion for you.

 

The other interesting part is the filter

"Drive='C:' AND Path = '\\users\\$user\\'"

 

Note that it’s wrapped in double quotes. Each of the values is a string so HAS to be in single quotes. Also note that you need to double the \ characters as WMI treats a single \ as an escape character so you have to escape the escape character.

Monitor Info

A question on the forum about combining information from 2 CIM classes produced this:

 

function Get-MonitorInfo {
    [CmdletBinding()]
    param(
        $computername = $env:COMPUTERNAME
    )

    $cs = New-CimSession -ComputerName $computername
   
    $monitors =  Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorId -Filter "Active = '$true'" -CimSession $cs
   
    foreach ($monitor in $monitors) {
       
        $in = ($monitor.InstanceName).Replace('\', '\\')
        Write-Verbose -Message $in
        $dp = Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorBasicDisplayParams -Filter "InstanceName = '$in'" -CimSession $cs
       
        $name = ''

        foreach ($c in $monitor.UserFriendlyName){
            if ($c -ne '00'){$name += [char]$c}
        }

        $type = 'Unknown'
        switch ($dp.VideoInputType){
            0 {$type = 'Analog'}
            1 {$type = 'Digital'}
        }
       
        New-Object -TypeName PSObject -Property @{
            Name = $name
            Type = $type
        }
    }
   
    Remove-CimSession -CimSession $cs
}

 

Create a CIM session to the computer. Get the instances of the WmiMonitorId class. Iterate through them and find the matching WmiMonitorBasicDisplayParams class instance.

 

The InstanceName of the monitor will look like this:

DISPLAY\GSM598F\4&19086f00&0&UID200195_0

you need to replace \ by \\ to use the value in a CIM query because \ is treated as the escape character and you have to escape it to use it

 

Translate the UserFriendly name by converting the byte array to a string and determine the VideoInputType using the switch.

 

Create an object and output

IIS information

In my recent post about getting server information from a IIS web server I said I post about getting similar information from later machines.

 

You still have the root\MirosoftIISv2 namespace available if you install the IIS 6.0 tools but one question to keep in mind – how long will they continue to be available?

 

Your alternative is the root\webadministration names space. You can use the Site class to get the relevant information

 

$serverdata = @()
Get-CimInstance -Namespace root\webadministration -ClassName Site -ComputerName $env:COMPUTERNAME |
foreach {

$serverdata += New-Object -TypeName PSObject -Property @{
Port = [string]::Join(',', ($_.Bindings | select -ExpandProperty BindingInformation))
SiteName = $_.Name
SiteId = $_.id
PSComputerName = $_.PSComputerName
Status = Invoke-CimMethod -InputObject $_ -MethodName GetState | select -ExpandProperty ReturnValue
}

}
$serverdata

 

Remember that COM objects are inert so you can’t call the method directly on the object. otherwise the info is about the same