AnEngelsen Posted November 13, 2023 Share Posted November 13, 2023 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 More sharing options...
Moderators Josh Levitsky Posted November 13, 2023 Moderators Share Posted November 13, 2023 I wonder if it's because it's running in 32bit in FW. My VM is being weird. If you run that in the PowerShell IDE x86 does it give output? Link to comment Share on other sites More sharing options...
AnEngelsen Posted November 13, 2023 Author Share Posted November 13, 2023 Turns out that `Get-LocalGroupMember` is not recognized as a cmdlet, function, script file, or operable program within the 32-bit environment. Link to comment Share on other sites More sharing options...
AnEngelsen Posted November 13, 2023 Author Share Posted November 13, 2023 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 #!/bin/bash # members -- list all members of a group # # SYNOPSIS # 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. # the_group="$1" # 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 fi # Check every user exec dscl . -list /Users \ | while read each_username do 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 More sharing options...
Moderators Sean Posted November 14, 2023 Moderators Share Posted November 14, 2023 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. 1 Link to comment Share on other sites More sharing options...
AnEngelsen Posted November 15, 2023 Author Share Posted November 15, 2023 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 More sharing options...
AnEngelsen Posted November 15, 2023 Author Share Posted November 15, 2023 @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 More sharing options...
AnEngelsen Posted November 15, 2023 Author Share Posted November 15, 2023 I also recommend including 1 additional instruction, to strip the domain name from the start of any username. # Remove "MYDOMAIN\" from the start of any username $administrators = $administrators -replace '^MYDOMAIN\\' Link to comment Share on other sites More sharing options...
AnEngelsen Posted November 20, 2023 Author Share Posted November 20, 2023 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 ',') $usernamesString 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 More sharing options...
Moderators Sean Posted November 20, 2023 Moderators Share Posted November 20, 2023 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: https://kb.filewave.com/books/filewave-general-info/page/script-best-practices 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 More sharing options...
AnEngelsen Posted November 21, 2023 Author Share Posted November 21, 2023 @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 More sharing options...
Moderators Sean Posted November 21, 2023 Moderators Share Posted November 21, 2023 Sure, but is there a component from that list the highlights which users are domain admins as oppose to which are local that could be used to separate them? Link to comment Share on other sites More sharing options...
Moderators Sean Posted November 30, 2023 Moderators Share Posted November 30, 2023 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 More sharing options...
Recommended Posts
Please sign in to comment
You will be able to leave a comment after signing in
Sign In Now