The PowerShell script described in this post creates a member list of the local administrator group and export the information to a CSV file.

Systems admins are frequently asked to generate a list of the users/groups who are in the local administrators group. Whereas some people use the net localgroup command to query the members, others use little VB scripts. Since this is a frequent activity for a Windows Administrator, I came up with a PowerShell script that can serve the purpose in an easy way.

The script that I am going to describe helps you query a given number of servers and generate a report in CSV format. This script handles conditions such as server offline and query failures, which it also includes in the CSV. Along with the Group member name, the script also informs you what kind of object it is (user or group) and whether it is a domain object or a local object. For domain objects, the script can also tell which domain (NetBIOS name) the object belongs to.

Script description

The good thing with this script is that it can query any local group on a remote computer. By default, it queries the Administrators group; however, if you want to generate a report for the Remote Desktop Users group, you can do that by passing the group name to the LocalGroupNameparameter of the script.

The code below is the key for the script. As you can see, I am using ADSI WinNT provider to query the group membership of a local group on a remote computer. Using the object returned from the query, I invoke a method that returns the members of the queried group.

$group = [ADSI]"WinNT://$Computer/$LocalGroupName"
$members = @($group.Invoke("Members"))

After I retrieve the list of members in the group, I loop through each member and query their details, such as name, class information, and ADS path. Below is the code that queries this information.

$MemberName = $member.GetType().Invokemember("Name","GetProperty",$null,$member,$null)
$MemberType = $member.GetType().Invokemember("Class","GetProperty",$null,$member,$null)
$MemberPath = $member.GetType().Invokemember("ADSPath","GetProperty",$null,$member,$null)

I used a few regex statements against the ADS path to retrieve domain information for each object. Based on the regex output, the report shows whether each returned object is a domain-level object or a local object. If you want to learn more about regular expressions in PowerShell, several resources are available on the Internet. I referred to the PowerShell cook book to build the regex I used in the script.

The remaining code part of the script is mostly straightforward. Throughout the script, I used Add-Content cmdlets to write the data into a CSV file with comma (,) separation. I feel that is the best way to format the data if you want to import it into Excel to format it further.

PowerShell script

<#
        .Synopsis 
            Gets membership information of local groups in remote computer

        .Description
            This script by default queries the membership details of local administrators group on remote computers. 
			It has a provision to query any local group in remote server, not just administrators group.

        .Parameter ComputerName
            Computer Name(s) which you want to query for local group information

		.Parameter LocalGroupName
			Name of the local group which you want to query for membership information. It queries 'Administrators' group when
			this parameter is not specified

		.Parameter OutputDir
			Name of the folder where you want to place the output file. It creates the output file in c:\temp folder
			this parameter is not used.

        .Example
            Get-LocalGroupMembers.ps1 -ComputerName srvmem1, srvmem2

            Queries the local administrators group membership and writes the details to c:\temp\localGroupMembers.CSV

        .Example
			Get-LocalGroupMembers.ps1 -ComputerName (get-content c:\temp\servers.txt)

		.Example
			Get-LocalGroupMembers.ps1 -ComputerName srvmem1, srvmem2

        .Notes
			Author : Sitaram Pamarthi
			WebSite: http://techibee.com

#>
[CmdletBinding()]
Param(
	[Parameter(	ValueFromPipeline=$true,
				ValueFromPipelineByPropertyName=$true
				)]
	[string[]]
	$ComputerName = $env:ComputerName,

	[Parameter()]
	[string]
	$LocalGroupName = "Administrators",

	[Parameter()]
	[string]
	$OutputDir = "c:\temp"
)

Begin {

	$OutputFile = Join-Path $OutputDir "LocalGroupMembers.csv"
	Write-Verbose "Script will write the output to $OutputFile folder"
	Add-Content -Path $OutPutFile -Value "ComputerName, LocalGroupName, Status, MemberType, MemberDomain, MemberName"
}

Process {
	ForEach($Computer in $ComputerName) {
		Write-host "Working on $Computer"
		If(!(Test-Connection -ComputerName $Computer -Count 1 -Quiet)) {
			Write-Verbose "$Computer is offline. Proceeding with next computer"
			Add-Content -Path $OutputFile -Value "$Computer,$LocalGroupName,Offline"
			Continue
		} else {
			Write-Verbose "Working on $computer"
			try {
				$group = [ADSI]"WinNT://$Computer/$LocalGroupName"
				$members = @($group.Invoke("Members"))
				Write-Verbose "Successfully queries the members of $computer"
				if(!$members) {
					Add-Content -Path $OutputFile -Value "$Computer,$LocalGroupName,NoMembersFound"
					Write-Verbose "No members found in the group"
					continue
				}
			}		
			catch {
				Write-Verbose "Failed to query the members of $computer"
				Add-Content -Path $OutputFile -Value "$Computer,,FailedToQuery"
				Continue
			}
			foreach($member in $members) {
				try {
					$MemberName = $member.GetType().Invokemember("Name","GetProperty",$null,$member,$null)
					$MemberType = $member.GetType().Invokemember("Class","GetProperty",$null,$member,$null)
					$MemberPath = $member.GetType().Invokemember("ADSPath","GetProperty",$null,$member,$null)
					$MemberDomain = $null
					if($MemberPath -match "^Winnt\:\/\/(?<domainName>\S+)\/(?<CompName>\S+)\/") {
						if($MemberType -eq "User") {
							$MemberType = "LocalUser"
						} elseif($MemberType -eq "Group"){
							$MemberType = "LocalGroup"
						}
						$MemberDomain = $matches["CompName"]

					} elseif($MemberPath -match "^WinNT\:\/\/(?<domainname>\S+)/") {
						if($MemberType -eq "User") {
							$MemberType = "DomainUser"
						} elseif($MemberType -eq "Group"){
							$MemberType = "DomainGroup"
						}
						$MemberDomain = $matches["domainname"]

					} else {
						$MemberType = "Unknown"
						$MemberDomain = "Unknown"
					}
				Add-Content -Path $OutPutFile -Value "$Computer, $LocalGroupName, SUCCESS, $MemberType, $MemberDomain, $MemberName"
				} catch {
					Write-Verbose "failed to query details of a member. Details $_"
					Add-Content -Path $OutputFile -Value "$Computer,,FailedQueryMember"
				}

			} 
		}

	}

}
End {}

Script usage

Get local administrator group details for a remote server/desktop and generate output in the c:\temp folder:

Get-LocalGroupMembers.ps1 -ComputerName SRVMEM1

Get local administrator group details for a list of computers in a text file and save the output in the c:\local folder:

Get-LocalGroupMembers.ps1 -ComputerName (Get-Content c:\temp\servers.txt) -OutputDir c:\local

Get Remote Desktop Users group membership details for a list of computers:

Get-LocalGroupMembers.ps1 -ComputerName (Get-Content c:\temp\servers.txt) -LocalGroupName “Remote Desktop Users”

Script output

When you run the script, the output will be similar to the following.

Console output of the PowerShell script

Console output of the PowerShell script

And below is the output CSV report in Excel generated from the above execution. In this output, AD is the NetBIOS name of my test domain.

List of the local administrators in Excel

List of the local administrators in Excel

47 Comments
  1. Derek 7 years ago

    I was having issues getting the script to read a list of computer names from a text file, inspired by Erik’s complaint above I came up with a way to read the computer objects from AD directly into the script.  This also allowed the script to work properly for me!

    Get-ADComputer -filter * | select-object Name | Foreach-Object {.\Get-LocalGroupMembers.ps1 -ComputerName $_.Name}

    Thanks to Sitaram, hope my variant here helps someone else as well.

    avatar
  2. Althaf 7 years ago

    EXTRACT GROUP MEMBERS DETAILS USING POWERSHELL

    =============================

    To get from Same domain

    Get-ADGroupMember “domain admins.contoso.com” | select-object name >>groupdetails.csv

    Get-ADGroupMember “domain admins” | select-object name >>groupdetails.csv

     

    To Get from another trusted domain

    Get-ADGroupMember “Remote Admin” -Server “eu.contoso.com” | select-object name,objectclass >>groupdetails1.csv

  3. Dan 7 years ago

    I left for Ole, but if anyone can assist I would greatly appreciate it. I will post the same here so sorry for duplicate:

    I see you got it working so I am hoping you can help me. I can run the script as is and it lists what I need for MY computer, but I have a CSV file I want to use, but cannot figure out what to put where in the script.

    Can you tell me what to put and where in the existing code? For this example assume I have c:\list\computers.csv and it looks like

    computer1

    computer2

     

    Thank you in advance

  4. Thajudeen 7 years ago

    It is working good. Thanks a lot

    • ArthurOLL 7 years ago

      Hello Thajudeen,

      The script does not find my pc administrator accounts and makes me an empty file.
      can you give me the script that you used with your Setup?

       

      Thanks .

  5. Rupesh 7 years ago

    Excellent post. it worked for me what i need.

  6. Don 7 years ago

    Hi Sitaram!

    Thanks for this very helpful post.

    However, when I run the below script on powershell, it only gets the local group accounts on the server itself.

    #>
    [CmdletBinding()]
    Param(
        [Parameter(    ValueFromPipeline=$true,
                    ValueFromPipelineByPropertyName=$true
                    )]
        [string[]]
        $ComputerName = $env:ComputerName,
    
        [Parameter()]
        [string]
        $LocalGroupName = "Administrators",
    
        [Parameter()]
        [string]
        $OutputDir = "c:\temp"
    )
    
    Begin {
    
        $OutputFile = Join-Path $OutputDir "LocalGroupMembers.csv"
        Write-Verbose "Script will write the output to $OutputFile folder"
        Add-Content -Path $OutPutFile -Value "ComputerName, LocalGroupName, Status, MemberType, MemberDomain, MemberName"
    }
    
    Process {
        ForEach($Computer in $ComputerName) {
            Write-host "Working on $Computer"
            If(!(Test-Connection -ComputerName $Computer -Count 1 -Quiet)) {
                Write-Verbose "$Computer is offline. Proceeding with next computer"
                Add-Content -Path $OutputFile -Value "$Computer,$LocalGroupName,Offline"
                Continue
            } else {
                Write-Verbose "Working on $computer"
                try {
                    $group = [ADSI]"WinNT://$Computer/$LocalGroupName"
                    $members = @($group.Invoke("Members"))
                    Write-Verbose "Successfully queries the members of $computer"
                    if(!$members) {
                        Add-Content -Path $OutputFile -Value "$Computer,$LocalGroupName,NoMembersFound"
                        Write-Verbose "No members found in the group"
                        continue
                    }
                }        
                catch {
                    Write-Verbose "Failed to query the members of $computer"
                    Add-Content -Path $OutputFile -Value "$Computer,,FailedToQuery"
                    Continue
                }
                foreach($member in $members) {
                    try {
                        $MemberName = $member.GetType().Invokemember("Name","GetProperty",$null,$member,$null)
                        $MemberType = $member.GetType().Invokemember("Class","GetProperty",$null,$member,$null)
                        $MemberPath = $member.GetType().Invokemember("ADSPath","GetProperty",$null,$member,$null)
                        $MemberDomain = $null
                        if($MemberPath -match "^Winnt\:\/\/(?\S+)\/(?\S+)\/") {
                            if($MemberType -eq "User") {
                                $MemberType = "LocalUser"
                            } elseif($MemberType -eq "Group"){
                                $MemberType = "LocalGroup"
                            }
                            $MemberDomain = $matches["CompName"]
    
                        } elseif($MemberPath -match "^WinNT\:\/\/(?\S+)/") {
                            if($MemberType -eq "User") {
                                $MemberType = "DomainUser"
                            } elseif($MemberType -eq "Group"){
                                $MemberType = "DomainGroup"
                            }
                            $MemberDomain = $matches["domainname"]
    
                        } else {
                            $MemberType = "Unknown"
                            $MemberDomain = "Unknown"
                        }
                    Add-Content -Path $OutPutFile -Value "$Computer, $LocalGroupName, SUCCESS, $MemberType, $MemberDomain, $MemberName"
                    } catch {
                        Write-Verbose "failed to query details of a member. Details $_"
                        Add-Content -Path $OutputFile -Value "$Computer,,FailedQueryMember"
                    }
    
                }
            }
    
        }
    
    }
    End {}

    The output that I’m trying to get is all local administrator accounts of all servers or computer s connected to a domain controller. Which will be recorded to “localGroupMembers.CSV” file. Is this possible to retrieve?

    Hoping for your prompt response.

    Thanks in advance.

  7. ITGal 7 years ago

    This was exactly what I was looking for and worked perfectly. However, does anyone know how to get the status of the account (i.e. enabled, disabled)?

     

  8. Kit 6 years ago
    PS C:\scripts> $serv = (Get-adcomputer -Searchbase "OU=Servers,DC=domain,DC=local" -Filter {OperatingSystem -like "*Windows*"}).Name
    PS C:\scripts> .\checklocaladminsonservers.ps1 -ComputerName ($serv) -OutputDir C:\scripts
    

  9. Kevin 6 years ago

    Hello, this script will be very useful to me, but I get errors I don’t understand.  I have taken the script exactly as it is shown above.

    I get this result:

    PS D:\ServerAdmins\Audit> .\Get-LocalGroupMembers.ps1 -ComputerName (get-content D:\ServerAdmins\Audit\servers.txt
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:37 char:7
    + Param(
    + ~
    Missing ‘)’ in function parameter list.
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:39 char:70
    +                 ValueFromPipelineByPropertyName=$true
    + ~
    Missing closing ‘)’ in expression.
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:40 char:33
    +                 )]
    + ~
    Unexpected token ‘)’ in expression or statement.
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:41 char:4
    + )
    + ~
    Unexpected token ‘)’ in expression or statement.
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:45 char:20
    +     [Parameter()]
    + ~
    An expression was expected after ‘(‘.
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:49 char:20
    +     [Parameter()]
    + ~
    An expression was expected after ‘(‘.
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:52 char:1
    + )
    + ~
    Unexpected token ‘)’ in expression or statement.
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:62 char:27
    +     ForEach($Computer in $ComputerName) {
    + ~~
    Unexpected token ‘in’ in expression or statement.
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:62 char:26
    +     ForEach($Computer in $ComputerName) {
    + ~
    Missing closing ‘)’ in expression.
    At D:\ServerAdmins\Audit\Get-LocalGroupMembers.ps1:61 char:9
    + Process {
    + ~
    Missing closing ‘}’ in statement block or type definition.
    Not all parse errors were reported. Correct the reported errors and try again.
    + CategoryInfo : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : MissingEndParenthesisInFunctionParameterList

    PS D:\ServerAdmins\Audit>

    Any thoughts?

  10. sahayarajan subramani 6 years ago

    Hi Bro,

    This is really helpful to achieve the Local Admin Group members details. Really appropriated and Hats of to you !!

  11. Abe Adriano 6 years ago

    Hi Sitaram,

    Thank you for this script.

    How can I modify this script to give me report for all localgroups and not just the administrators without specifying each group?

  12. Travis 5 years ago

    Is it possible to also list the members of the domain groups in the output as well?

  13. geek 5 years ago

    Hi Guys,

    No Doubt this script is awesome & fulfilling our needs.

    But can someone help me to get Group Description Details whether it is local or domain but i need group details to be printed in excel output in a different cell of excel.

    Thanks in Advance

     

  14. john 5 years ago

    Hi,

    Thanks for the awesome post. Is there a way to get the nested groups and users?

    • @john

      Since this post has been published in 2014, new cmdlets have been created.

      Get-LocalGroupMember is one of them.

      You can combine the result with the Get-ADGroupMember cmdlet which has a -Recursive parameter.

      Here is a concrete example:

      $ComputerList = @(
          'Computer2'
          'Computer2'
      )
      
      $DomainName = (Get-ADDomain).NetBIOSName
      
      Invoke-Command -ComputerName $ComputerList -ScriptBlock {Get-LocalGroupMember -Name Administrators} |
          ForEach-Object -Process {
              if($PSItem.ObjectClass -eq 'User'){
                  $PSItem | Select-Object -Property PSComputerName,Name
              }
              else{
                  $ComputerName = $PSItem.PSComputerName
                  $NameOnly = $PSItem.Name -replace '^.+?\\'
                  Get-ADGroupMember -Identity $NameOnly -Recursive |
                      Select-Object -Property @{
                              Label = 'PSComputerName'
                              Expression = {$ComputerName}
                          }, @{
                              Label = 'Name'
                              Expression = {'{0}\{1}' -f $DomainName, $PSItem.SamAccountName}
                          }
              }
          }

      If you want to run this script against all computers of your domain, instead of providing the computer list you can use the following:

      $ComputerList = Get-ADComputer -Filter *

  15. Working perfectly !

    Thanks for your support

  16. trey 2 years ago

    Is there any way to have it pull all groups instead of just Administrators group?

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account