Agregar ruta del sistema a objetos CIMInstance
La otra noche, cuando presenté para el grupo de usuarios de PowerShell de Mississippi, uno de los miembros mostró código de PowerShell 3.0 usando los cmdlets CIM. La cuestión es cómo los cmdlets CIM manejan las propiedades del sistema WMI como __SERVER y __RELPATH. De forma predeterminada, esas propiedades no se muestran, pero se capturan en la propiedad CimSystemProperties.
El problema es que la propiedad __PATH no se captura cuando se usa Get-CIMInstance, como puede ver en la captura de pantalla. Aparentemente se trata de un problema conocido. El uso de Get-WMIObject todavía funciona.
PS C:\> get-wmiobject win32_service -filter "name='bits'" | Select -expand __PATH
\\SERENITY\root\cimv2:Win32_Service.Name="BITS"La mayoría de las veces esto probablemente no sea gran cosa. Pero quizás haya situaciones en las que necesite la propiedad __PATH. La otra noche vi un código que construía la propiedad __PATH. Me sentí intrigado y decidí resolverlo por mí mismo. La ruta necesita el nombre de la computadora, la ruta de la clase del espacio de nombres, el nombre de la clase, la clave de clase y el valor de esa clave. Ya tengo la mayor parte de esto del
PS C:\> $c.CimSystemProperties.ServerName
SERENITY
PS C:\> $c.CimSystemProperties.Namespace.Replace("/","\")
root\cimv2
PS C:\> $c.CimSystemProperties.ClassName
Win32_ServiceCambié la dirección de la barra en el espacio de nombres. La única parte que me falta es la propiedad clave. Pero puedo encontrarlo mirando los calificadores de las propiedades de clase. Piense en un calificador como una etiqueta que denota un uso especial. Lo realmente interesante de CIMInstance es que se incluye información de clase. Esto significa que puedo ver la clase desde el objeto mismo.
PS C:\> $c.cimclass.CimClassProperties | select name,qualifiers
Todo lo que necesito hacer es encontrar la propiedad con un calificador CLAVE.
PS C:\> $c.cimclass.CimClassProperties | where {$_.qualifiers.name -contains 'key'}
Name : Name
Value :
CimType : String
Flags : Property, Key, ReadOnly, NullValue
Qualifiers : {read, key}
ReferenceClassName :Ahora que sé que la propiedad Nombre es la clave, puedo terminar de construir mi camino.
PS C:\> $Key = $c[0].CimClass.CimClassProperties |
>> where {$_.qualifiers.name -contains "key"} |
>> select -ExpandProperty Name
>>
PS C:\> $c | Select @{Name="__PATH";Expression={
>> '\\{0}\{1}:{2}{3}' -f $_.CimSystemProperties.ServerName,
>> $_.CimSystemProperties.Namespace.Replace("/","\"),
>> $_.CimSystemProperties.ClassName,
>> ".$($key)=""$($_.$key)"""
>> }}
>>
__PATH
------
\\SERENITY\root\cimv2:Win32_Service.Name="BITS"Y esta es la ruta exacta que obtendría usando Get-WMIObject.
PS C:\> Get-WmiObject win32_service -filter "name='bits'" | select __Path
__PATH
------
\\SERENITY\root\cimv2:Win32_Service.Name="BITS"Por supuesto, no quiero tener que escribir tanto, así que creé una función para que haga el trabajo por mí.
#requires -version 3.0
Function Add-CIMPath {
<#
.Synopsis
Add the __PATH property to a CIMInstance object
.Description
The Get-CIMInstance cmdlet by default doesn't display the WMI system properties
like __SERVER. The properties are available in the CimSystemProperties property
except for __PATH. This function will construct the __PATH property and add it
to a CIMInstance object.
.Example
PS C:\> get-ciminstance win32_memorydevice | add-cimpath | select __Path
__PATH
------
\\SERVER01\root\cimv2:Win32_MemoryDevice.DeviceID="Memory Device 0"
\\SERVER01\root\cimv2:Win32_MemoryDevice.DeviceID="Memory Device 1"
.Example
PS C:\> get-ciminstance win32_bios -computer netbk8 | add-cimpath | format-list __Path,PSComputername
__PATH : \\NETBK8\root\cimv2:Win32_BIOS.Name="Rev 1.0
",SoftwareElementID="Rev 1.0
",SoftwareElementState="3",TargetOperatingSystem="0",Version="LENOVO - 6040000"
PSComputerName : netbk8
.Inputs
CIMInstance
.Outputs
CIMInstance
.Link
Get-CIMInstance
#>
[cmdletbinding()]
Param (
[Parameter(Position=0,ValueFromPipeline=$True)]
[ValidateNotNullorEmpty()]
[ciminstance]$Inputobject
)
Begin {
Write-Verbose -Message "Starting $($MyInvocation.Mycommand)"
} #begin
Process {
#get the key class property
Write-Verbose "Processing $($Inputobject.CimClass.CimClassName)"
$Key = $Inputobject.CimClass.CimClassProperties |
where {$_.qualifiers.name -contains "key"} |
select -ExpandProperty Name
Write-Verbose "Creating __PATH Using key property $key"
$Inputobject | Add-Member -PassThru -MemberType NoteProperty -Name __PATH -Value (
'\\{0}\{1}:{2}{3}' -f $_.CimSystemProperties.ServerName.ToUpper(),
$_.CimSystemProperties.Namespace.Replace("/","\"),
$_.CimSystemProperties.ClassName,
$(
if ($key -is [array]) {
#create a string with the array of key names and values
[string]$s = ".$($key[0])=""$($_.($key[0]))"""
#add each additional key separated by comma
for ($i=1;$i -lt $key.count;$i++) {
$s+= ",$($key[$i])=""$($_.($key[$i]))"""
}
$s
}
elseif ($Key) {
#just a single key
".$($key)=""$($_.$key)"""
}
else {
#no key
'=@'
})
) #value
} #process
End {
Write-Verbose -Message "Ending $($MyInvocation.Mycommand)"
} #end
} #end Add-CIMPathLa parte principal de la función es esencialmente lo que acabo de demostrar. Sin embargo, hay algunos casos especiales en los que no hay ninguna propiedad de clave o hay varias claves, por lo que tuve que agregar algo de lógica para tener eso en cuenta. Escribí la función asumiendo que le conectarías un objeto CIMInstance y deseas agregar __PATH.
PS C:\> get-ciminstance win32_logicaldisk | Add-CIMPath | select __Path
__PATH
------
\\SERENITY\root\cimv2:Win32_LogicalDisk.DeviceID="C:"
\\SERENITY\root\cimv2:Win32_LogicalDisk.DeviceID="D:"
\\SERENITY\root\cimv2:Win32_LogicalDisk.DeviceID="E:"
\\SERENITY\root\cimv2:Win32_LogicalDisk.DeviceID="F:"
\\SERENITY\root\cimv2:Win32_LogicalDisk.DeviceID="G:"
\\SERENITY\root\cimv2:Win32_LogicalDisk.DeviceID="Z:"Y así es como se ven los casos especiales.
Seré el primero en admitir que esto es un truco de fuerza bruta y no puedo garantizar que haya cubierto todos los casos de uso extraños. Entonces, si intentas esto y te encuentras con una clase que no proporciona el __PATH correcto, espero que me lo hagas saber.


