Arbelac
Arbelac

Reputation: 1904

Getting account status via WMI query

I will to use WMI to query the servers for the administrators group memberships. My question is : I want to fetch account status via WMI query too. Btw , no required status for Group such as domain admins and so on.

My desired output:

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

Script :

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

Upvotes: 0

Views: 1631

Answers (2)

FoxDeploy
FoxDeploy

Reputation: 13567

To answer your comments...

You said

How can I optimize this script, there are 200 machines, it will take a century...

...So , how can I write via the directoryservices .NET class instead of WMI ? is it possible? Do you have any information on this? – Arbelac 6 hours ago

If you really need to run one command on a boat load of machines and you have remote access rights, you can look at ForEach-Parallel or Invoke-Parallel, two different options for running one command on multiple machines.

However you will need to deal with remote state, like worrying about what to do if a remote machine is offline, or you what if a machine isn't reachable when you try to contact it.

An Easier alternative

You have an environment already, well, let's think of other ways to achieve the same goal.

You could instead use Group Policy to run this command locally on each machine as a log on script. The machines can then write their output to a Json or Csv file in some central share, maybe ADFS if you have it.

You could also use any remote agent you have installed on the machines as well, like System Center Configuration Manager, or Shalvik, or any of the other apps you might have.

So to boil down my answer, you can do this with PowerShell Remoting but it is starting to sound like that will cause you problems, and you might want to think about distributing the job out using some other system.

Source: was a sysadmin and automation engineer for years. I would probably not use PowerShell remoting like this.

Upvotes: 0

postanote
postanote

Reputation: 16106

Continuing from my comment.

You can use WMI or ADSI to do this, but PowerShell v5 and higher already have cmdlets for this use case.

All of the below are using the raw property names from the classes. Of course, if you want a different name, you can use a hash table, PSCustomObject, or calculated property to do that.

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

If you are on a PSVersion that does not have this by default, you can get one here:

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

If you are stuck with WMI, then...

Discover what you have to work with and any associated class relationships

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

The below will get you all users and the groups to which that belong by select the user first, which is what you are showing in your post, but the is really short-circuit logic since you are only looking for uses in your query in Administrators. So, this...

Select only what you need. Note the focus regarding the SIDType code as a filter

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

Of course, you can use RegEx to get rid of the braces if that's your thing. Lastly, remove the Where-Object line, the SIDType filter and you get it all.

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

Reverse the request --- Select only what you need, SIDType not needed.

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

If PSRemoting (either domain or workgroup mode) is properly setup, hitting remote systems is straightforward.

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

As for your particulars

<#
"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}
#>

Update as per your comment.

As for this...

can I write via the directoryservices .NET class instead of WMI

Sure you can, as detailed here...

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

... but using this does not give you back the output you list.

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

Upvotes: 2

Related Questions