Monthly Archive

CIM

WMF 5.0 April 2015 preview – – software inventory logging

A software inventory module is now included with the April 2015 WMF 5.0 preview

£> Get-Command -Module SoftwareInventoryLogging | select Name

Name
----
Get-SilComputer
Get-SilComputerIdentity
Get-SilData
Get-SilLogging
Get-SilSoftware
Get-SilUalAccess
Get-SilWindowsUpdate
Publish-SilData
Set-SilLogging
Start-SilLogging
Stop-SilLogging

 

Windows updates are always a good place to start poking into your systems

£> Get-Command Get-SilWindowsUpdate -Syntax

Get-SilWindowsUpdate [[-ID] <string[]>] [-CimSession <CimSession[]>]
[-ThrottleLimit <int>] [-AsJob] [<CommonParameters>]

£> Get-SilWindowsUpdate

ID          : KB3055381
InstallDate : 4/30/2015

etc

 

The parameters for Get-SilWindowsUpdate look like those I’d expect from a CDXML module. Inspection of C:\Windows\System32\WindowsPowerShell\v1.0\modules\SoftwareInventoryLogging\

shows  a number of cdxml files

MsftSil_Computer.cdxml
MsftSil_ComputerIdentity.cdxml
MsftSil_Data.cdxml
MsftSil_ManagementTasks.psm1
MsftSil_Software.cdxml
MsftSil_UalAccess.cdxml
MsftSil_WindowsUpdate.cdxml
Msft_MiStreamTasks.cdxml

 

The WMF 5,0 release notes supply a link to further data of software inventory logging – interestingly its flagged as a Windows Server 2012 R2 page.

Trying the cmdlet against a Windows Server 2012 R2 system running WMF 4.0 (with the November 2014 roll up)

$cs = New-CimSession -ComputerName W12R2SUS
Get-SilWindowsUpdate -CimSession $cs

£> Get-SilWindowsUpdate -CimSession $cs

ID             : KB3006193
InstallDate    : 1/5/2015
PSComputerName : W12R2SUS

etc

 

This means the class is on our Windows Server 2012 R2 box so we could use it directly

£> Get-CimInstance -Namespace root/InventoryLogging -ClassName  MsftSil_WindowsUpdate | Format-Table -a

ID                  InstallDate                                PSComputerName
--                   -----------                                 --------------
KB3006193 1/5/2015 12:00:00 AM
KB2894856 9/14/2014 12:00:00 AM

etc

 

This module supplies a useful way to find out the software installed on our systems – I’ll be digging into this over a few more posts

Blocksize missing?

I recently had a question asking why the Bloacksize property on Win32_LogicalDisk is empty but is populated on Win32_Volume.

The thing is to remember the underlying thing that these 2 CIM classes represent.

 

A logical disk is a subdivision of an extended partition. An extended partition can hold one or more logical disks.

 

When you create a volume on a disk you use either a primary partition or a logical disk from an extended partition. Until you format the volume you can’t have a block size as that is determined by the parameters you use to perform the format.

 

The documentation for Win32_LogicalDisk states that block size is not populated for logical disks https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx.

OMI/CIM/WMI dictionary

Don Jones provides a very good summary of the similarities and differences between WMI, CIM and OMI http://powershell.org/wp/2015/04/24/management-information-the-omicimwmimidmtf-dictionary/

Recommended reading if you’re using these technologies

Query vs Filter

I’ve tended to advocate using the –Filter parameter rather than the –Query parameter with the CIM (and WMI) cmdlets but a recent post on the Windows Management Infrastructure blog has me questioning that decision.

 

Using Measure-Command I tried various pairs of commands – such as:

 

Measure-Command {Get-CimInstance -ClassName Win32_Directory -Filter "Name = 'C:\\Test2'"}

 

Measure-Command {Get-CimInstance -Query "SELECT * FROM Win32_Directory WHERE Name = 'C:\\Test2'"}

 

The results weren’t conclusive but it seems that at worst there is no significant difference between the approaches and at best using a query is significantly faster. 

 

At the moment my best advice would be use the –Filter parameter if you want to reduce typing but try –Query if speed becomes your main issue.

Copy a file with WMI

A question came up on the forum about copying files with CIM (WMI). I normally use Copy-Item rather than CIM as its easier. The questioner was using CIM_LogicalFile when I’ve normally used CIM_DataFile so I decided to take a look at the class. In reality the two classes are very similar and CIM-datafile could be substituted for CIM_LogicalFile in the code that follows.

 

The obvious starting point is to use the Copy method on the CIM_LogicalFile class

 

$files = Get-WmiObject -Class CIM_LogicalFile -Filter "Path = '\\Test\\' AND Extension = 'txt'"

foreach ($file in $files) {
$newfile = "C:\Test2\$($file.FileName).$($file.Extension)"
 
$file.Copy($newfile)

}

 

Couple of points to note. In the Path part of the filter you have to escape the \ delimiter.  Extension doesn’t include the ‘.’

You have to give the full path – including file name - to the loaction to which you want to copy the file. In this case you don’t have to escape the \ delimiter. Consistency is a wonderful thing and usually absent from WMI.

 

You can also use Invoke-WmiMethod

 

$files = Get-WmiObject -Class CIM_LogicalFile -Filter "Path = '\\Test\\' AND Extension = 'txt'"

foreach ($file in $files) {
$newfile = "C:\Test2\$($file.FileName).$($file.Extension)"
 
Invoke-WmiMethod -InputObject $file  -Name Copy -ArgumentList $newfile

}

 

OR

use the new CIM cmdlets

 

$files = Get-CimInstance -ClassName CIM_LogicalFile -Filter "Path = '\\Test\\' AND Extension = 'txt'"

foreach ($file in $files) {
$newfile = "C:\Test2\$($file.FileName).$($file.Extension)"
 
Invoke-CimMethod -InputObject $file  -MethodName Copy -Arguments @{Filename = $newfile}

}

 

In this case you have to give the argument name for the method as well as its value. You can discover the method parameters using Get-CimClass

 

$class = Get-CimClass CIM_LogicalFile

£> $class.CimClassMethods["Copy"].Parameters

Scripting Guy CDXML series finished

My CDXML series on the Scripting Guy blog finished today.  The 4 articles are:

 

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/02/registry-cmdlets-manage-the-registry.aspx

 

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/03/registry-cmdlets-first-steps-with-cdxml.aspx

 

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/04/registry-cmdlets-advanced-cdxml.aspx

 

http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/05/registry-cmdlets-using-advanced-cdxml.aspx

Scripting Guy CDXML series

Today starts a four part series I’ve written for the Scripting Guy blog on using CDXML to create a module to work with the registry.  Don’t know what CDXML is – you will when you’ve read the series

 

The first post is at http://blogs.technet.com/b/heyscriptingguy/archive/2015/02/02/registry-cmdlets-manage-the-registry.aspx

Testing for a hotfix

KB3000850 – the November roll up for Windows 2012 R2 contains some very useful updates.

I’ve installed it on some machines in my lab but not all. The update is huge so I’m installing it manually rather than through WSUS.

 

I need to test a remote machine to determine if the update  is installed.

If it is installed you get a this back

£> Get-HotFix -Id KB3000850 -ComputerName w12r2dsc

Source        Description             HotFixID         InstalledBy      
------          -----------                --------          -----------      
W12R2DSC      Update           KB3000850      MANTICORE\Richard

 

But if its not installed you get this

£> Get-HotFix -Id KB3000850 -ComputerName w12r2od01
Get-HotFix : Cannot find the requested hotfix on the 'w12r2od01' computer. Verify the input and run the command again.
At line:1 char:1
+ Get-HotFix -Id KB3000850 -ComputerName w12r2od01
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (:) [Get-HotFix], ArgumentException
    + FullyQualifiedErrorId : GetHotFixNoEntriesFound,Microsoft.PowerShell.Commands.GetHotFixCommand

Get-Hotfix actually uses the Win32_QuickFixEngineering CIM class so you need to have DCOM open on the remote system otherwise you get a

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

error.

 

You need to wrap the call to Get-Hotfix in a try catch. You only need to know if the update is installed so creating a specialised output object manages that for you

Get-VM |
where State -eq 'Running' |
foreach {
 
$props = @{
   Machine = $($psitem.Name)
   Present = $false
}
 
try {
   $hf = Get-HotFix -Id KB3000850 -ComputerName $psitem.Name -ErrorAction Stop
   $props.Present = $true
}
catch {
   $props.Present = $false
}
 
New-Object -TypeName PSObject -Property $props
 
}

 

Substitute any other method of getting a list of computer names, for my call to Get-VM, to match your environment.

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 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