Yesterday I posted a quick article on getting the age of the local administrator account password. It seemed appropropriate to follow up on a quick and dirty way to list all members of the local administrator group. Normally, I would turn to WMI (and have written about this in the past). But WMI is relatively slow for this task and even using the new CIM cmdlets in PowerShell 3.0 don't improve performance. Instead I'm going to return to an old school technique using the NET command.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
PS C:\> net localgroup administrators Alias name administrators Comment Administrators have complete and unrestricted access to the computer/domain Members ------------------------------------------------------------------------------- Administrator GLOBOMANTICS\Domain Admins localadmin The command completed successfully.
It is very easy to see members. To query a remote computer all I need to do is wrap this in Invoke-Command and use PowerShell remoting.
PS C:\> invoke-command {net localgroup administrators} -comp chi-fp01 Alias name administrators Comment Administrators have complete and unrestricted access to the computer/domain Members ------------------------------------------------------------------------------- Administrator GLOBOMANTICS\Chicago IT GLOBOMANTICS\Domain Admins The command completed successfully.
Yes, there is some overhead for remoting but overall performance is pretty decent. And if you already have an established PSSession, even better. For quick and dirty one-liner it doesn't get much better. Well, maybe it can.
I have no problem using legacy tools when they still get the job done and this certainly qualifies. To make it more PowerShell friendly though, let's clean up the output by filtering out blanks, that last line and skipping the "header" lines.
PS C:\> invoke-command { >> net localgroup administrators | >> where {$_ -AND $_ -notmatch "command completed successfully"} | >> select -skip 4 >> } -computer chi-fp01 >> Administrator GLOBOMANTICS\Chicago IT GLOBOMANTICS\Domain Admins
Boom. Now I only get the member names. Let's go one more level and write an object to the pipeline and be better at handling output from multiple computers. I came up with a scriptblock like this:
$members = net localgroup administrators | where {$_ -AND $_ -notmatch "command completed successfully"} | select -skip 4 New-Object PSObject -Property @{ Computername = $env:COMPUTERNAME Group = "Administrators" Members=$members }
This will create a simple object with a properties for the computername, group name and members. Here's how I can use it with Invoke-Command.
invoke-command { $members = net localgroup administrators | where {$_ -AND $_ -notmatch "command completed successfully"} | select -skip 4 New-Object PSObject -Property @{ Computername = $env:COMPUTERNAME Group = "Administrators" Members=$members } } -computer chi-fp01,chi-win8-01,chi-ex01 -HideComputerName | Select * -ExcludeProperty RunspaceID
Now I have objects that I can export to XML, convert to HTML or send to a file. But since I've come this far, I might as well take a few more minutes and turn this into a reusable tool.
Function Get-NetLocalGroup { [cmdletbinding()] Param( [Parameter(Position=0)] [ValidateNotNullorEmpty()] [object[]]$Computername=$env:computername, [ValidateNotNullorEmpty()] [string]$Group = "Administrators", [switch]$Asjob ) Write-Verbose "Getting members of local group $Group" #define the scriptblock $sb = { Param([string]$Name = "Administrators") $members = net localgroup $Name | where {$_ -AND $_ -notmatch "command completed successfully"} | select -skip 4 New-Object PSObject -Property @{ Computername = $env:COMPUTERNAME Group = $Name Members=$members } } #end scriptblock #define a parameter hash table for splatting $paramhash = @{ Scriptblock = $sb HideComputername=$True ArgumentList=$Group } if ($Computername[0] -is [management.automation.runspaces.pssession]) { $paramhash.Add("Session",$Computername) } else { $paramhash.Add("Computername",$Computername) } if ($asjob) { Write-Verbose "Running as job" $paramhash.Add("AsJob",$True) } #run the command Invoke-Command @paramhash | Select * -ExcludeProperty RunspaceID } #end Get-NetLocalGroup
This function lets me specify a group of computers or PSSessions as well as the local group name. Today I may need to know who belongs to the local administrator's group but tomorrow it might be Remote Desktop Users.
PS C:\> Get-NetLocalGroup -Group "remote desktop users" -Computername $sessions Group : remote desktop users Computername : CHI-FP01 Members : Group : remote desktop users Computername : CHI-WIN8-01 Members : Computername : CHI-EX01 Members : Group : remote desktop users Group : remote desktop users Computername : CHI-DC01 Members : jfrost
Sometimes even old school tools can still be a part of your admin toolkit.
Even if I’d be tempted to resort to other means to get this information, I think the approach (legacy cmdline app -> objects) is an extremely valuable technique. And by “hiding” it in a script you leave the opportunity to switch the implementation out. Very nice!
I like it. I should get to work.
Again a very good post!
I Like to go with the WMI / CIM approach under use of the well known SID of the Administrators Group (S-1-5-32-544) because we have International Computer setups.
net localgroup depends on the Name of the group.
The German Administrators group name is “Administratoren” and the Spanish is Adminitradores (or so) 😉
By use of the well known SID you do not have problems with different Names and it works worldwide!
See: http://support.microsoft.com/kb/243330/en-us
Greets:
Peter Kriegel
http://www.admin-source.de