Categories

PowerShell and WMI

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

CIM or WMI – – accessing remote machines

I much prefer the CIM cmdlets for accessing remote machines. The WMI cmdlets use DCOM which is firewall unfriendly and can often be unavailable of a server – cue the dreaded RPC server is unavailable error messages.

By contrast the CIM cmdlets use WSMAN.

For one off access to a remote machine use the computername parameter

Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName RSSURFACEPRO2

 

If you want to access a machine multiple times in the session create a CIM session – analagous to a remoting session

 

$cs = New-CimSession -ComputerName RSSURFACEPRO2
Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $cs

 

By default a CIM session uses WSMAN

£> $cs


Id           : 1
Name         : CimSession1
InstanceId   : 30c2b530-4ff7-448e-b68d-1f1282890e6a
ComputerName : RSSURFACEPRO2
Protocol     : WSMAN

 

though you can configure them to use DCOM if need be

$opt = New-CimSessionOption -Protocol DCOM
$csd = New-CimSession -ComputerName RSSURFACEPRO2 -SessionOption $opt
Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $csd

 

When would you need to use DCOM – if you are accessing a machine with PowerShell 2 installed. The CIM cmdlets want to use WSMAN 3 and will error if you access a machine with WSMAN 2 installed however if you include a –Filter they will work

So

Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName computer1

will fail if computer1 is running WSMAN 2 (PowerShell 2)

However, if you change the command to include a filter

Get-CimInstance -ClassName Win32_OperatingSystem -Filter "Manufacturer LIKE 'Microsoft%'" -ComputerName computer1

 

Even if, as in this case, the filter doesn’t actually do anything

Finding a CIM class

I was investigating something on my disks and started to look at the partitions:

£> Get-CimInstance -ClassName Win32_Partition
Get-CimInstance : Invalid class
At line:1 char:1
+ Get-CimInstance -ClassName Win32_Partition
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : MetadataError: (root\cimv2:Win32_Partition:String) [Get-CimInstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80041010,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand

OK so my memory isn’t what it was and I got the class name wrong. How to find the correct class?

£> Get-CimClass *Partition*


   NameSpace: ROOT/cimv2

CimClassName
------------
CIM_DiskPartition
Win32_DiskPartition
Win32_SystemPartitions
CIM_LogicalDiskBasedOnPartition
Win32_LogicalDiskToPartition
CIM_RealizesDiskPartition
Win32_DiskDriveToDiskPartition
Win32_PerfFormattedData_HvStats_...
Win32_PerfRawData_HvStats_HyperV...
Win32_PerfFormattedData_HvStats_...
Win32_PerfRawData_HvStats_HyperV...
Win32_PerfFormattedData_VidPerfP...
Win32_PerfRawData_VidPerfProvide...

 

I’ve truncated the display horizontally as not interested in methods & properties at this point


So the class I want is Win32_DiskPartition.

Get-CimClass is one of the biggest benefits from PowerShell 3.0

CIM or WMI?

Working with WMI became a whole easier when PowerShell came on the scene. If you ever spent hours typing all of the Echo commands that were required with VBScript to produce output you’ll be aware of what I mean.  There are still a few awkward areas in the WMI cmdlets. One of the most awkward is date/time handling.

Consider looking for the last boot up time of your favourite computer:

£> Get-WmiObject -Class Win32_OperatingSystem | select -ExpandProperty LastBootUpTime
20140702083855.487133+060

 

That breaks down as year (2014), month (07), day (02), hour (08), minute (38), second (55) and fraction of a second (.487133). The +060 denotes the minutes offset from GMT (UTC if you prefer) – I’m in the UK on daylight saving time.  So, once you know how to read it the answer is understandable but not easy to work with.

The PowerShell team introduced a method on Get-WmiObject that will convert the date to a more understandable format:

 

£> $os = Get-WmiObject -Class Win32_OperatingSystem
£> $os.ConvertToDateTime($os.LastBootUpTime)

02 July 2014 08:38:55

 

You can also use the method in select-object or the format cmdlets by using a calculated field:

£> Get-WmiObject -Class Win32_OperatingSystem | Format-List PSComputerName, Caption, @{N='BootTime'; E={$_.ConvertToDate
Time($_.LastBootUpTime)}}


PSComputerName : RSSURFACEPRO2
Caption        : Microsoft Windows 8.1 Pro
BootTime       : 02/07/2014 08:38:55

 

There is an easier way – use the CIM cmdlets:

£> Get-CimInstance -ClassName Win32_OperatingSystem | select -ExpandProperty LastBootUpTime

02 July 2014 08:38:55

 

The automatic date conversion is more than sufficient incentive for me to use Get-CimInstance in preference to Get-WmiObject.

Share Permissions – setting deny

The last change to the share permissions functions to modify the Set-SharePermissions functions to enable the application of Deny permissions.

The function becomes:

#requires -Version 3.0
function Set-SharePermission {
[CmdletBinding()]
param (
  [Parameter(Mandatory=$true)]
  [string]$sharename,

  [string]$domain = $env:COMPUTERNAME,

  [Parameter(Mandatory=$true)]
  [string]$trusteeName,

  [Parameter(Mandatory=$true)]
  [ValidateSet("Read", "Change", "FullControl")]
  [string]$permission = "Read",

  [string]$computername = $env:COMPUTERNAME,

  [parameter(ParameterSetName="AllowPerm")]
  [switch]$allow,

  [parameter(ParameterSetName="DenyPerm")]
  [switch]$deny
)

switch ($permission) {
   'Read'        {$accessmask = 1179817}
   'Change'      {$accessmask = 1245631}
   'FullControl' {$accessmask = 2032127}
}


$tclass = [wmiclass]"\\$computername\root\cimv2:Win32_Trustee"
$trustee = $tclass.CreateInstance()
$trustee.Domain = $domain
$trustee.Name = $trusteeName

$aclass = [wmiclass]"\\$computername\root\cimv2:Win32_ACE"
$ace = $aclass.CreateInstance()
$ace.AccessMask = $accessmask

switch ($psCmdlet.ParameterSetName) {
"AllowPerm"  {$ace.AceType = 0}
"DenyPerm"  {$ace.AceType = 1}
default {Write-Host "Error!!! Should not be here" }
}

$ace.Trustee = $trustee

$shss = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter "Name='$sharename'" -ComputerName $computername
$sd = Invoke-WmiMethod -InputObject $shss -Name GetSecurityDescriptor |
select -ExpandProperty Descriptor

$sclass = [wmiclass]"\\$computername\root\cimv2:Win32_SecurityDescriptor"
$newsd = $sclass.CreateInstance()
$newsd.ControlFlags = $sd.ControlFlags

foreach ($oace in $sd.DACL){

if (($oace.Trustee.Name -eq $trusteeName) -AND ($oace.Trustee.Domain -eq $domain) ) {
    continue
}
else
{
    $newsd.DACL += $oace
}
}
$newsd.DACL += $ace

$share = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter "Name='$sharename'" -ComputerName $computername
$share.SetSecurityDescriptor($newsd)

} # end function

The changes are to add two switches –allow & –deny.  Put them in different parametersets to ensure mutual exclusivity.

As you are using parametersets you can use a switch based on the parameterset name to set the ACE type.

switch ($psCmdlet.ParameterSetName) {
"AllowPerm"  {$ace.AceType = 0}
"DenyPerm"  {$ace.AceType = 1}
default {Write-Host "Error!!! Should not be here" }
}

Everything else remains the same.