powershell: obtención del estado de la cuenta mediante consulta WMI

CorePress2024-01-25  10

Usaré WMI para consultar en los servidores las membresías del grupo de administradores. Mi pregunta es: también quiero obtener el estado de la cuenta mediante una consulta WMI. Por cierto, no se requiere un estado para el grupo, como administradores de dominio, etc.

Mi resultado deseado:

"UserName","Fullname","Machinename","DomainName","Account Status"
"localuser","MACHINE\localuser","MACHINE","MACHINE","OK"
"Domain Admins","CONTOSO\Domain Admins","MACHINE","CONTOSO"
"domain_user_01","CONTOSO\domain_user_01","MACHINE","CONTOSO","Degraded"

Guión:

function get-localadministrators {
    param ([string]$computername=$env:computername)

    $computername = $computername.toupper()
    $ADMINS = get-wmiobject -computername $computername -query "select * from win32_groupuser where GroupComponent=""Win32_Group.Domain='$computername',Name='administrators'""" | % {$_.partcomponent}

    foreach ($ADMIN in $ADMINS) {
                $admin = $admin.replace("\$computername\root\cimv2:Win32_UserAccount.Domain=","") # trims the results for a user
                $admin = $admin.replace("\$computername\root\cimv2:Win32_Group.Domain=","") # trims the results for a group
                $admin = $admin.replace('",Name="',"\")
                $admin = $admin.REPLACE("""","")#strips the last "

                $objOutput = New-Object PSObject -Property @{
                    Machinename = $computername
                    
                    Fullname = ($admin)
                    DomainName  =$admin.split("\")[0]
                    UserName = $admin.split("\")[1]
                }#end object

    $objreport+=@($objoutput)
    }#end for

    return $objreport
}#end function

¿Por qué utilizar WMI? ¿Qué versión del sistema operativo estás ejecutando? ¿Qué versión de PowerShell estás ejecutando? PSv5 y tiene cmdlets para administración local de usuarios y grupos. Hay un módulo en MS powershellgallery.com, que tiene un módulo que puede descargar para versiones anteriores de PowerShell.ns.

- postanota

28 de marzo de 2021 a las 7:46

Tengo máquinas de 2003 y 2008.

Arbelac

28 de marzo de 2021 a las 7:50

1

Vea mi respuesta para usted. Sin embargo, aunque puede hacer lo que está haciendo, esto realmente complica demasiado el caso de uso. puedes hacer estoEstá en una sola línea según mi respuesta proporcionada a continuación, que, por supuesto, puede convertirlo en una función si ese es su plan. Alguien más hizo exactamente esta pregunta en otro sitio de preguntas y respuestas, al que doy la misma respuesta. Entonces, ¿están ustedes dos en la misma empresa tratando de resolver el mismo caso de uso o en la misma clase/taller, y esto es tarea, ya que ambos tenían el mismo estilo de codificación y los mismos errores/problemas, o era usted el del otro sitio? ¿también?.... *** ;-}***

- postanota

28 de marzo de 2021 a las 8:33



------------------------------------

Continuación de mi comentario.

Puedes usar WMI o ADSI para hacer esto, pero PowerShell v5 y superiores ya tienen cmdlets para este caso de uso.

Todo lo siguiente utiliza los nombres de propiedad sin formato de las clases. Por supuesto, si desea un nombre diferente, puede usar una tabla hash, PSCustomObject o una propiedad calculada para hacerlo.

Get-Command  -Module '*local*' | 
Format-Table -AutoSize
# Results
<#
CommandType     Name                                               Version    Source                                                                             
-----------     ----                                               -------    ------                                                                             
Cmdlet          Add-LocalGroupMember                               1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Disable-LocalUser                                  1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Enable-LocalUser                                   1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Get-LocalGroup                                     1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Get-LocalGroupMember                               1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Get-LocalUser                                      1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          New-LocalGroup                                     1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          New-LocalUser                                      1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Remove-LocalGroup                                  1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Remove-LocalGroupMember                            1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Remove-LocalUser                                   1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Rename-LocalGroup                                  1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Rename-LocalUser                                   1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Set-LocalGroup                                     1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                 
Cmdlet          Set-LocalUser                                      1.0.0.0    Microsoft.PowerShell.LocalAccounts
#>

Si tienes una versión de PS que no tiene esto de forma predeterminada, puedes obtener uno aquí:

Find-Module -Name '*local*' | 
Format-Table -AutoSize
# Results
<#
Version Name                                                     Repository Description                                                                          
------- ----                                                     ---------- -----------                                                                          
...
1.6     localaccount                                             PSGallery  A Simple module to allow the management of local users and groups on a computer      
1.0.0.0 Microsoft.PowerShell.LocalAccounts                       PSGallery  Provides cmdlets to work with local users and local groups                           
3.0     LocalUserManagement                                      PSGallery  a module that performs various local user management functions                       
1.3     LocalMachine                                             PSGallery  Simple management functions for accounts and settings on a local machine.            
0.1.1   LocalAccountManagement                                   PSGallery  Manage local and remote user accounts and profiles                                   
...
1.0.1   cLocalGroup                                              PSGallery  The cLocalGroup module contains the cLocalGroup DSC resource that provides a mecha...
2.1.0   ECS.LocalGPO                                             PSGallery  This Windows PowerShell module contains functions used for working with Windows lo...
...
#>

Si estás atascado con WMI, entonces...

Descubre con qué tienes que trabajar y las relaciones de clases asociadas

# Group Detail
Clear-Host
((Get-WmiObject -Class Win32_Group) -match 'Administrators')[0] |
Select-Object -Property '*'
<#
PSComputerName   : 570A5E12-BA93-4
Status           : OK
Name             : Administrators
__GENUS          : 2
__CLASS          : Win32_Group
__SUPERCLASS     : Win32_Account
__DYNASTY        : CIM_ManagedSystemElement
__RELPATH        : Win32_Group.Domain="570A5E12-BA93-4",Name="Administrators"
__PROPERTY_COUNT : 9
__DERIVATION     : {Win32_Account, CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER         : 570A5E12-BA93-4
__NAMESPACE      : root\cimv2
__PATH           : \570A5E12-BA93-4\root\cimv2:Win32_Group.Domain="570A5E12-BA93-4",Name="Administrators"
Caption          : 570A5E12-BA93-4\Administrators
Description      : Administrators have complete and unrestricted access to the computer/domain
Domain           : 570A5E12-BA93-4
InstallDate      : 
LocalAccount     : True
SID              : S-1-5-32-544
SIDType          : 4
... 
#>

Get-WmiObject -Class Win32_Group | 
Select-Object -Property Name, SIDType
# Results
<#
Name                                SIDType
----                                -------
...
Administrators                            4
...
Guests                                    4
...
Users                                     4
#>

# User Detail
Clear-Host
(Get-WmiObject -Class Win32_Account)[0] | 
Select-Object -Property '*'
<#
PSComputerName     : 570A5E12-BA93-4
Status             : Degraded
Caption            : 570A5E12-BA93-4\Administrator
PasswordExpires    : False
__GENUS            : 2
__CLASS            : Win32_UserAccount
__SUPERCLASS       : Win32_Account
__DYNASTY          : CIM_ManagedSystemElement
__RELPATH          : Win32_UserAccount.Domain="570A5E12-BA93-4",Name="Administrator"
__PROPERTY_COUNT   : 16
__DERIVATION       : {Win32_Account, CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER           : 570A5E12-BA93-4
__NAMESPACE        : root\cimv2
__PATH             : \570A5E12-BA93-4\root\cimv2:Win32_UserAccount.Domain="570A5E12-BA93-4",Name="Administrator"
AccountType        : 512
Description        : Built-in account for administering the computer/domain
Disabled           : True
Domain             : 570A5E12-BA93-4
FullName           : 
InstallDate        : 
LocalAccount       : True
Lockout            : False
Name               : Administrator
PasswordChangeable : True
PasswordRequired   : True
SID                : S-1-5-21-2047949552-857980807-821054962-500
SIDType            : 1
...
#>


Get-WmiObject -Class Win32_Account | 
Select-Object -Property Name, SIDType
# Results
<#
Name                                SIDType
----                                -------
Administrator                             1
DefaultAccount                            1
Guest                                     1
WDAGUtilityAccount                        1
Everyone                                  5
...
BUILTIN                                   3
...
Administrators                            4
...
Guests                                    4
...
Users                                     4
#>

Lo siguiente le mostrará todos los usuarios y los grupos a los que pertenecen seleccionando primero al usuario, que es lo que muestra en su publicación, pero en realidad es una lógica de cortocircuito ya que solo está buscando usos en su consulta en Administradores. Entonces, esto...

Seleccione solo lo que necesita. Tenga en cuenta el enfoque con respecto al código SIDType comofiltro

Clear-Host
Get-WmiObject -Class Win32_Account | 
Where-Object -Property SIDType -eq 1 |
Select-Object -Property PSComputerName, Name, Status, 
@{
    Name       = 'Groups'
    Expression = {($PSItem).GetRelated('Win32_Group').Name}
} | 
Format-Table -AutoSize
# Results
<#

PSComputerName  Name               Status   Groups                                
--------------  ----               ------   ------                                
570A5E12-BA93-4 Administrator      Degraded Administrators                        
570A5E12-BA93-4 DefaultAccount     Degraded System Managed Accounts Group         
570A5E12-BA93-4 Guest              Degraded Guests                                
570A5E12-BA93-4 WDAGUtilityAccount OK       {Administrators, Remote Desktop Users}
#>

Por supuesto, puedes usar RegEx para deshacerte de los frenillos si eso es lo que te gusta. Por último, elimine la línea Where-Object, el filtro SIDType y lo obtendrá todo.

Clear-Host
Get-WmiObject -Class Win32_Account | 
Select-Object -Property PSComputerName, Name, Status, 
@{
    Name       = 'Groups'
    Expression = {($PSItem).GetRelated('Win32_Group').Name}
} | 
Format-Table -AutoSize
# Results
<#
PSComputerName  Name                                Status   Groups                                
--------------  ----                                ------   ------                                
570A5E12-BA93-4 Administrator                       Degraded Administrators                        
570A5E12-BA93-4 DefaultAccount                      Degraded System Managed Accounts Group         
570A5E12-BA93-4 Guest                               Degraded Guests                                
570A5E12-BA93-4 WDAGUtilityAccount                  OK       {Administrators, Remote Desktop Users}
570A5E12-BA93-4 Everyone                            OK                                             
...                                         
570A5E12-BA93-4 NETWORK                             OK                                             
570A5E12-BA93-4 BATCH                               OK                                             
570A5E12-BA93-4 INTERACTIVE                         OK       Users                                 
...                                          
570A5E12-BA93-4 SELF                                OK                                             
570A5E12-BA93-4 Authenticated Users                 OK       Users                                 
570A5E12-BA93-4 RESTRICTED                          OK                                             
...                                           
570A5E12-BA93-4 IUSR                                OK       IIS_IUSRS                             
...    
#>

Revertir la solicitud --- Seleccione solo lo que necesita, SIDType no es necesario.

Clear-Host
Get-WmiObject -Class Win32_Group | 
Select-Object -Property PSComputerName, Name, Status, 
@{
    Name       = 'GroupMembers'
    Expression = {
        (
            Get-WmiObject -Class win32_group | 
            Where Name -eq $PSItem.Name).GetRelated('Win32_UserAccount'
        ).Name
    }
} | 
Format-Table -AutoSize
# Results
<#
PSComputerName  Name                                Status GroupMembers                       
--------------  ----                                ------ ------------                       
570A5E12-BA93-4 Access Control Assistance Operators OK                                        
570A5E12-BA93-4 Administrators                      OK     {Administrator, WDAGUtilityAccount}
570A5E12-BA93-4 Backup Operators                    OK                                        
570A5E12-BA93-4 Cryptographic Operators             OK                                        
570A5E12-BA93-4 Device Owners                       OK                                        
570A5E12-BA93-4 Distributed COM Users               OK                                        
570A5E12-BA93-4 Event Log Readers                   OK                                        
570A5E12-BA93-4 Guests                              OK     Guest                              
570A5E12-BA93-4 Hyper-V Administrators              OK                                        
570A5E12-BA93-4 IIS_IUSRS                           OK                                        
570A5E12-BA93-4 Network Configuration Operators     OK                                        
570A5E12-BA93-4 Performance Log Users               OK                                        
570A5E12-BA93-4 Performance Monitor Users           OK                                        
570A5E12-BA93-4 Power Users                         OK                                        
570A5E12-BA93-4 Remote Desktop Users                OK     WDAGUtilityAccount                 
570A5E12-BA93-4 Remote Management Users             OK                                        
570A5E12-BA93-4 Replicator                          OK                                        
570A5E12-BA93-4 System Managed Accounts Group       OK     DefaultAccount                     
570A5E12-BA93-4 Users                               OK                                        
#>

Si PSRemoting (ya sea en modo dominio o grupo de trabajo) está configurado correctamente, acceder a sistemas remotos es sencillo.

# Target a remote computer
Clear-Host
Import-Csv -Path 'D:\Temp\ComputerList.csv' | 
ForEach-Object {
    Get-WmiObject -Class Win32_Account -ComputerName $PSitem.Name -Credential (Get-Credential -Credential WDAGUtilityAccount) | 
    Where-Object -Property SIDType -eq 1 |
    Select-Object -Property PSComputerName, Name, Status, 
    @{
        Name       = 'Groups'
        Expression = {($PSItem).GetRelated('Win32_Group').Name}
    }
} | 
Format-Table -AutoSize
# Results
<#
PSComputerName  Name               Status   Groups                                
--------------  ----               ------   ------                                
570A5E12-BA93-4 Administrator      Degraded Administrators                                    
570A5E12-BA93-4 DefaultAccount     Degraded System Managed Accounts Group         
570A5E12-BA93-4 Guest              Degraded Guests                                
570A5E12-BA93-4 TestUser           OK       Users                                 
570A5E12-BA93-4 WDAGUtilityAccount OK       {Administrators, Remote Desktop Users}
#>

En cuanto a tus detalles

<#
"UserName","Fullname","Machinename","DomainName","Account Status"
"localuser","MACHINE\localuser","MACHINE","MACHINE","OK"
"Domain Admins","CONTOSO\Domain Admins","MACHINE","CONTOSO"
"domain_user_01","CONTOSO\domain_user_01","MACHINE","CONTOSO","Degraded"
#>


# Only the administrators group
Clear-Host
Get-WmiObject -Class Win32_Account | 
Where-Object -Property SIDType -eq 1 |
Select-Object -Property Name, Caption, PSComputerName, Domain, Status, 
@{
    Name       = 'Groups'
    Expression = {($PSItem).GetRelated('Win32_Group').Name}
} | 
Where-Object -Property Groups -EQ 'Administrators'| 
Format-Table -AutoSize
# Results
<#
Name               Caption                            PSComputerName  Domain          Status   Groups                                
----               -------                            --------------  ------          ------   ------                                
Administrator      570A5E12-BA93-4\Administrator      570A5E12-BA93-4 570A5E12-BA93-4 Degraded Administrators                        
WDAGUtilityAccount 570A5E12-BA93-4\WDAGUtilityAccount 570A5E12-BA93-4 570A5E12-BA93-4 OK       {Administrators, Remote Desktop Users}
#>


Clear-Host
Get-WmiObject -Class Win32_Group | 
Select-Object -Property Name, Caption, PSComputerName, Domain, Status, 
@{
    Name       = 'GroupMembers'
    Expression = {
        (
            Get-WmiObject -Class win32_group | 
            Where Name -eq $PSItem.Name).GetRelated('Win32_UserAccount'
        ).Name
    }
} | 
Format-Table -AutoSize
# Results
<#
Name                                Caption                                             PSComputerName  Domain          Status GroupMembers                       
----                                -------                                             --------------  ------          ------ ------------                       
Access Control Assistance Operators 570A5E12-BA93-4\Access Control Assistance Operators 570A5E12-BA93-4 570A5E12-BA93-4 OK                                        
Administrators                      570A5E12-BA93-4\Administrators                      570A5E12-BA93-4 570A5E12-BA93-4 OK     {Administrator, WDAGUtilityAccount}
...                       
#>


# Only the administrators group
Clear-Host
Get-WmiObject -Class Win32_Group | 
Where-Object -Property Name -eq 'Administrators' | 
Select-Object -Property Name, Caption, PSComputerName, Domain, Status, 
@{
    Name       = 'GroupMembers'
    Expression = {
        (
            Get-WmiObject -Class win32_group | 
            Where Name -eq $PSItem.Name).GetRelated('Win32_UserAccount'
        ).Name
    }
}  | 
Format-Table -AutoSize 
# Results
<#
Name           Caption                        PSComputerName  Domain          Status GroupMembers                       
----           -------                        --------------  ------          ------ ------------                       
Administrators 570A5E12-BA93-4\Administrators 570A5E12-BA93-4 570A5E12-BA93-4 OK     {Administrator, WDAGUtilityAccount}
#>

Actualiza según tu comentario.

En cuanto a esto...

¿Puedo escribir a través de la clase .NET de servicios de directorio en lugar de WMI?

Claro que puedes, como se detalla aquí...

https://devblogs.microsoft.com/scripting/the-admins-first-steps-local-group-membership

... pero usar esto no le devuelve el resultado que enumera.

Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ctype   = [System.DirectoryServices.AccountManagement.ContextType]::Machine
$context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $env:COMPUTERNAME
$idtype  = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName
$group   = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($context, $idtype, ‘Administrators’)
$group.Members |
Select-Object -Property '*' -First 1
# Results
<#
GivenName                         : 
MiddleName                        : 
Surname                           : 
EmailAddress                      : 
VoiceTelephoneNumber              : 
EmployeeId                        : 
AdvancedSearchFilter              : System.DirectoryServices.AccountManagement.AdvancedFilters
Enabled                           : False
AccountLockoutTime                : 
LastLogon                         : 
PermittedWorkstations             : {}
PermittedLogonTimes               : {255, 255, 255, 255...}
AccountExpirationDate             : 
SmartcardLogonRequired            : False
DelegationPermitted               : True
BadLogonCount                     : 0
HomeDirectory                     : 
HomeDrive                         : 
ScriptPath                        : 
LastPasswordSet                   : 3/28/2021 10:20:29 AM
LastBadPasswordAttempt            : 
PasswordNotRequired               : False
PasswordNeverExpires              : True
UserCannotChangePassword          : False
AllowReversiblePasswordEncryption : False
Certificates                      : {}
Context                           : System.DirectoryServices.AccountManagement.PrincipalContext
ContextType                       : Machine
Description                       : Built-in account for administering the computer/domain
DisplayName                       : 
SamAccountName                    : Administrator
UserPrincipalName                 : 
Sid                               : S-1-5-21-2047949552-857980807-821054962-500
Guid                              : 
DistinguishedName                 : 
StructuralObjectClass             : 
Name                              : Administrator
#>

7

gracias, Get-WmiObject -Class Win32_Group -ComputerName MÁQUINA | Donde-Objeto -Nombre de propiedad -eq 'Administradores' | Seleccionar-Objeto -Nombre de propiedad, Título, PSComputerName, Dominio, Estado, @{ Nombre = 'GroupMembers' Expresión = { ( Get-WmiObject -Class win32_group | Donde Nombre -eq $PSItem.Name).GetRelated('Win32_UserAccount' ).Name } } | Formato-Tabla -AutoSize

Arbelac

28 de marzo de 2021 a las 8:33

No te preocupes. Ya actualicé mi respuesta para abordar las necesidades y el diseño de su propiedad en particular. Cuídate y mantente a salvo.

- postanota

28 de marzo de 2021 a las 8:35

He probado un script de muestra en una máquina específica. pero tarda mucho en completarse. . ¿Es normal?

Arbelac

28 de marzo de 2021 a las 8:36

Puede, ya que tiene que mirar no sólo cada clase sino cada objeto de la clase, luego filtrar cuál debe ser el resultado final y luego los formateadores lo toman antes de enviarlo a la pantalla o al archivo. Otras cosas afectan la velocidad, eso es todo ambiental. CPU, velocidad del disco, RAM, otros procesos en ejecución, etc. Por ejemplo, en mi sistema WIn10, esto solo toma unos segundos. Sin embargo, tengo 8 núcleos, 64 GB de RAM, todo SSD y estoy totalmente optimizado. Si terminas haciendo esto en máquinas remotas, tomará aún más tiempo, porque tiene que atravesar la red y el entorno del host remoto.configuración del entorno.

- postanota

28 de marzo de 2021 a las 8:40

Entonces, ¿cómo puedo optimizar este script? Porque hay alrededor de 200 máquinas en nuestro entorno. Puede que tarde un siglo :) Por cierto, ¿tengo que usar GetRelated?

Arbelac

28 de marzo de 2021 a las 9:07



------------------------------------

Para responder a tus comentarios...

Dijiste

¿Cómo puedooptimice este script, hay 200 máquinas, tomará un siglo...

... Entonces, ¿cómo puedo escribir a través de la clase .NET de servicios de directorio en lugar de WMI? ¿Es posible? Tienes alguna información sobre esto? – Arbelac hace 6 horas

Si realmente necesita ejecutar un comando en una gran cantidad de máquinas y tiene derechos de acceso remoto, puede consultar ForEach-Parallel o Invoke-Parallel, dos opciones diferentes para ejecutar un comando en varias máquinas.

Sin embargo, tendrás que lidiar con el estado remoto, como preocuparte por qué hacer si una máquina remota está fuera de línea, o qué pasa si no se puede acceder a una máquina cuando intentas comunicarte con ella.

Una alternativa más fácil

Ya tienes un entorno, bueno, pensemos en otras formas de lograr el mismo objetivo.

En su lugar, podrías utilizarPolítica de grupo para ejecutar este comando localmente en cada máquina como un script de inicio de sesión. Luego, las máquinas pueden escribir su salida en un archivo Json o Csv en algún recurso compartido central, tal vez ADFS si lo tiene.

También puede utilizar cualquier agente remoto que haya instalado en las máquinas, como System Center Configuration Manager, Shalvik o cualquiera de las otras aplicaciones que pueda tener.

Para resumir mi respuesta, puedes hacer esto con PowerShell Remoting, pero empieza a parecer que eso te causará problemas y quizás quieras pensar en distribuir el trabajo usando algún otro sistema.

Fuente: fue administrador de sistemas e ingeniero de automatización durante años. Probablemente no usaría la comunicación remota de PowerShell de esta manera.

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare