Búsqueda de propiedades de objetos complejos
style="letter-spacing: 0px; text-transform: unset; background-color: transparent;">Mientras trabajaba con PowerShell, probablemente haya encontrado objetos con muchas propiedades. Es bastante difícil encontrar valores entre ellos. Los más comunes son objetos como los objetos WMI y los objetos de Active Directory. ¿Cómo podemos encontrar todas las propiedades que contienen una determinada cadena o patrón de cadena? ¿Cómo podemos encontrar las propiedades de un objeto WMI que contiene el nombre de nuestro servidor? ¿Qué atributos de AD contienen el apellido de un usuario? ¡Creemos una función de PowerShell para ayudar a responder estas preguntas!
Enumerar las propiedades de los objetos.
Uno de los primeros comandos que aprende un scripter principiante de PowerShell es Get-Member. Este comando nos muestra los miembros de los objetos, incluidos los miembros de propiedades. Aunque nuestra función puede muy bien construirse sobre ella, en este caso echemos un vistazo a una alternativa, los atributos ocultos de los objetos 'psobject.properties'. La siguiente expresión enumera las propiedades del objeto de proceso actual de PowerShell (el resultado se recorta para mejorar la legibilidad):
Aquí vemos todas las propiedades con sus nombres, tipos y valores. Hay algunas ‘propiedades de propiedades’ más, pero debemos centrarnos en estas tres. Si nos fijamos en los tipos de propiedades, podemos notar que hay un tipo ‘AliasProperty’. Realmente no necesitamos ese tipo de propiedades porque solo almacenan información redundante de otras propiedades.
Creemos una expresión que enumere las propiedades de nuestro objeto de proceso de PowerShell que contiene la cadena "Windows":
Crear la función de propiedad de búsqueda
Como ya tenemos una solución rudimentaria, coloquemos eso dentro de una función y le asignaremos parámetros, a saber, ‑object y -patrón. Podemos hacer que nuestros objetos busquen a través de la canalización, por lo que la función debería aceptar la entrada de la canalización. También necesitamos un bucle ForEach, para que nuestra función también pueda manejar una serie de objetos:
function Search-Property {
param(
[parameter(ValueFromPipeline)][psobject[]] $object,
[string] $pattern = "."
)
process{
foreach($o in $object){
$o.psobject.properties |
Where-Object {
$_.membertype -ne 'AliasProperty' -and
$_.value -match $pattern
} | Select-Object -Property @{n = "Object"; e = {$o.tostring()}}, Name, Value
}
}
}
Los resultados se amplían con una nueva columna que contiene la representación de cadena del objeto de entrada, para que podamos ver a qué objeto pertenece una determinada propiedad. Esto es importante en caso de que introduzcamos varios objetos. Si ejecutamos esto, obtenemos los resultados deseados:
Hacer la función más capaz
Con un poco de esfuerzo, podemos mejorar aún más la función. Primero, es posible que queramos excluir ciertas propiedades del conjunto de resultados. Por ejemplo, cuando buscamos las apariciones del "apellido" entre las propiedades de una cuenta de usuario de AD, solo queremos aquellas que puedan actualizarse mediante el cmdlet Set-ADUser. Probablemente queramos excluir el nombre distinguido y el CN, ya que necesitamos otro comando, Rename-ADObject, para actualizarlos. Para excluir ciertas propiedades, introduzcamos el parámetro -ExcludeProperty que puede aceptar una serie de cadenas con comodines pares.
Es posible que también queramos restringir el alcance de las propiedades en las que queremos buscar. Por ejemplo, queremos buscar solo dentro de las propiedades que contienen la cadena "Nombre". Tengamos el parámetro -Propiedad que también puede aceptar una serie de cadenas con comodines para definir el conjunto de propiedades en las que buscar. Si no usamos esto parámetro, el valor predeterminado son todas las propiedades.
Es posible que también queramos aplicar nuestro patrón de búsqueda a los nombres de las propiedades. Para eso podemos usar un parámetro de cambio SearchInPropertyNames. O tal vez queramos buscar solo entre los nombres de las propiedades, no en los valores de las propiedades, así que tengamos también un parámetro de cambio -ExcludeValues.
Si no queremos molestarnos con expresiones regulares sino usar búsquedas literales, podemos usar también un parámetro de cambio -LiteralSearch .
Aquí está la función mejorada:
function Search-Property {
param(
[parameter(ValueFromPipeline)][psobject[]] $object,
[string] $pattern = ".",
[switch] $SearchInPropertyNames,
[switch] $ExcludeValues,
[switch] $LiteralSearch,
[string[]] $Property = "*",
[string[]] $ExcludeProperty
)
begin{
if($LiteralSearch -and $pattern -ne "."){
$pattern = [regex]::Escape($pattern)
}
}
process{
foreach($o in $object){
$o.psobject.properties |
Where-Object {
$propname = $_.name
$_.membertype -ne 'AliasProperty' -and
(
$(if(!$ExcludeValues){$_.value -match $pattern}) -or
$(if($SearchInPropertyNames){$_.name -match $pattern})
) -and
!($ExcludeProperty | Where-Object {$propname -like $_}) -and
($Property | Where-Object {$propname -like $_})
} | Select-Object -Property @{n = "Object"; e = {$o.tostring()}}, Name, Value
}
}
}
La nueva función es solo un par de líneas más larga que la original, su esencia sigue siendo la misma, por lo que valió la pena reflexionar sobre los posibles escenarios más complejos en los que podríamos usar nuestra función y agregar algunas capacidades nuevas.
Y aquí está la nueva función en acción. En el primer ejemplo, queremos ver esas propiedades de los objetos WMI Win32_OperatingSystem y Win32_ComputerSystem que contienen la cadena "DNS" o el nombre de nuestra computadora. Además de eso, queremos excluir aquellas propiedades que comienzan con 2 guiones bajos, porque son solo parte del esquema WMI:
En el siguiente ejemplo, volvemos al problema original. Veamos esos atributos de usuario de AD de la cuenta de usuario que contienen el apellido, pero excluimos el nombre distinguido y otros nombres que necesitan el cmdlet Rename-ADObject para actualizarlos:
¿Qué sigue?
Por supuesto, podríamos mejorar aún más la función agregando ayuda, ejemplos o explicaciones de los parámetros. Podría haber una función para buscar aquellas propiedades que contienen valores de otras propiedades de forma dinámica. Por ejemplo, busque todas las propiedades de todas las cuentas de usuario que contengan el apellido del usuario real.
Esperamos que haya aprendido algo nuevo, o que al menos tenga una nueva herramienta útil en su kit de herramientas de PowerShell. ¿Cuáles son sus ideas sobre cómo mejorar esta función?





