Jump to content

List of Local Administrators


Recommended Posts

I have a PowerShell script that lists members of the (local) Administrators user group. The script produces 1-line of output (when run locally) but results in NULL output when assigned to a custom field. What am I doing wrong?

$listOfAdministrators = $(Get-LocalGroupMember -Group "Administrators").name
$usernames = @()

foreach ($name in $listOfAdministrators) {
    $position = $name.IndexOf("\")
    $usernames += $name.Substring($position + 1)

# Join the usernames array into a single string separated by spaces
$usernamesString = $usernames -join ' '

Write-Output $usernamesString


Link to comment
Share on other sites

This is an alternative script that does the same thing within a Powershell 32-bit environment: 

# Function to get the substring after the backslash using Microsoft.VisualBasic.Strings.Mid
function Get-SubstringAfterBackslash($str) {
    Add-Type -AssemblyName Microsoft.VisualBasic
    [Microsoft.VisualBasic.Strings]::Mid($str, $str.IndexOf("\") + 2)

# Get the list of administrators using the net localgroup command
$administrators = net localgroup administrators | Where-Object {$_ -and $_ -notmatch "^The command completed successfully|^Alias name|^Comment|^Members|^-"} | ForEach-Object { Get-SubstringAfterBackslash $_ }

# Join the usernames array into a single string separated by spaces
$usernamesString = $administrators -join ' '
Write-Output $usernamesString


Here is the script that I'm using with macOS clients. It requires (1) launch argument. (The group name.)

I have the launch argument set to: admin


# members -- list all members of a group
#   members groupname
# Source: http://superuser.com/questions/279891/list-all-members-of-a-group-mac-os-x
# Expected to work on Mac OS 10.5 and newer, tested on 10.6 and 10.7.
# It could be rewritten to work on 10.4 by using "dseditgroup -o checkmember"
# instead of "dsmemberutil checkmembership".
# By using dseditgroup, the script could also be extended to handle
# other Directory Service nodes than the default local node.

# Input check and usage
  if [[ $# != 1 || $1 == -* || $1 =~ [[:space:]] ]]; then
    echo "Syntax: ${0##*/} GroupName_GoesHere" >&2
    exit 64
  elif (dsmemberutil checkmembership -U root -G "$the_group" 2>&1 \
    | grep "group .* cannot be found") >&2; then
    exit 1

# Check every user
exec dscl . -list /Users \
  | while read each_username
    printf "$each_username "
    dsmemberutil checkmembership -U "$each_username" -G "$the_group"
  done \
    | grep "is a member" | cut -d " " -f 1

# eof


Link to comment
Share on other sites

  • Moderators

I would recommend, when building out Custom Fields for both Windows and macOS, that the format returned matches.  Looking at the scripts, it appears that windows will provide a space separated list, whilst macOS will provide a list with each item on a new line.  It comes into its element when using Inventory Queries and differing string formats may come to haunt later on.  Set up this way, the above is relying upon FileWave to reformat the newline characters.

For example, rather than rely upon FileWave managing my separator, I explicitly define one in my Custom Field script, which in this example provides a comma separated output for both:

# Windows
$(foreach($line in ((Gwmi win32_groupuser | Where-Object groupcomponent -like '*"Administrators"').PartComponent)){$line.Split('"')[-2]}) -Join ","

# macOS
/usr/bin/dscl . -read /Groups/admin GroupMembership | awk -F ": " '{ print $2}'  | tr " " ","

Now, I say that is what my Custom Field is, but I take this a step further.

When dealing with multiple pieces of information within the same line, searching for one becomes awkward.  Imagine you have users with names that contain other users, e.g. 

  • macOS01: root,groot,roo
  • macOS02: root,groot,roopak
  • macOS03 root,roo
  • macOS04 root,roopak

Disclaimer: 'I am not Groot', but 'I am Groot'!

If it was desirable to find those devices who have an admin whose name is roo, how would you exclude macOS02 or macOS04?  With that in mind, consider using a top and tail of the separator also:

# Windows
$admin_users = $(foreach($line in ((Gwmi win32_groupuser | Where-Object groupcomponent -like '*"Administrators"').PartComponent)){$line.Split('"')[-2]}) -Join ","

Write-Host ("," + $admin_users + ",")

# macOS
/usr/bin/dscl . -read /Groups/admin GroupMembership | awk -F ": " '{ print ","$2","}'  | tr " " ","

This alters the returned values:

  • macOS01: ,root,groot,roo,
  • macOS02: ,root,groot,roopak,
  • macOS03 ,root,roo,
  • macOS04 ,root,roopak,

There is now the option to search for ,roo, and only the correct devices will be found.

  • Thanks 1
Link to comment
Share on other sites

Great post, Sean! I appreciate you taking into consideration ease of searchability/filtering.

These 1-2 lines of code will go a long way towards allowing my team to audit who has local admin permissions. (FileWave is allowing us to keep that list of people very, very short.) 😀

Link to comment
Share on other sites

@Sean I'm noticing that the "Gwmi win32_groupuser" command appears to be reporting on all AD Groups (if the desktop client is bound to Active Directory). I only need to know who's a member of the local Administrators group.

As such, I modified my script as follows:

# Get the list of administrators using the net localgroup command and extract usernames
# The "context" attribute is used to retrieve 100 lines after the line of dashes, to (fingers crossed) capture all potential usernames.
$administrators = net localgroup administrators | Select-String -Pattern '^-+\s*(.+)' -Context 1, 100 | ForEach-Object { $_.Context.PostContext }

# Remove empty lines and lines containing non-username information
$administrators = $administrators -match '\S' -replace '^\s*(?:The command completed successfully\.|Alias name|Comment|Members|-+)'

# Remove known users from the array
$administrators = $administrators | Where-Object { $_ -notin @('Administrator', 'Domain Admin') }

# Join the usernames array into a single string separated by commas
$usernamesString = $administrators -join ','

# Output the usernames string
Write-Host ",$usernamesString"


Link to comment
Share on other sites

I simplified the Windows script:

 $administrators = net localgroup administrators |
    Select-String -Pattern '^-+\s*(.+)' -Context 1, 100 |
    ForEach-Object { $_.Context.PostContext } |
    Where-Object { $_ -notmatch '^-+$|^The command completed successfully\.|^Alias name|^Comment|^Members|^Administrator|^Domain\s*Admins\s*|^Enterprise\s*Admins\s*' } |
    ForEach-Object { $_ -replace '^SHIRTFACTORY\\' }

$usernamesString = "," + ($administrators -join ',')

Some (but not all) of my Windows desktop clients reference "Domain Admins" within the Custom Field. When I run the script as Administrator within PowerShell ISE (x68) I do not get any references to "Domain Admins". Why?

Link to comment
Share on other sites

  • Moderators

FileWave does not run as Administrator, it runs 32bit as System User.  If commands produce different output between running either as a different user or bit depth, then that would be a question for Microsoft.  To run in the same context as FileWave, please view our KB:


I don't have a bound device handy, but can this not just be handled in one line as per the example I provided prior?

For example, on this device I have two local Admins, but I can use the -NotLike or -NotMatch to remove other entries:


Could this not be used more simply to remove those that are Domain Admins? 

For the local account I see this for one user:


Is there not an object that demonstrates it is a Domain Admin that can be used to exclude those Domain Admins?


Link to comment
Share on other sites

@Sean, I think you'll find that when you run Gwmi win32_groupuser (as the SYSTEM user) on a system that is AD bound, the resulting output will show members of the Administrators AD Group, as well as the local Administrators group. I only care about who is a member of the local Administrators group.

That's why I'm defaulting to the net localgroup administrators command.

Link to comment
Share on other sites

  • 2 weeks later...
  • Moderators

Did you get anywhere with this?  I'd be surprised if you can't identify and target, all Admins, local Admins or AD Admins as desired with GWMI, since I can do this with my Azure Admins.  As suggested, is there not a component that can be used to identify AD Admins?

If I look at the PartComponent for a couple of users, the domain is either the local device name or AzureAD.


As such, I can use that domain information to choose how to target admins, local or Azure.

This will match Azure Admins:


Whilst this will match the non-Azure Admins:


I could also target local admins with a Match rather than NotMatch with the local device name domain:


It therefore comes down to whether AD has a similar output, where Microsoft has one of the components reporting in such a way that identification between AD Admins and Local Admins can be observed with this command.

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in

Sign In Now
  • Create New...