In this article, we will discuss how to get a consolidated list of local administrators group members from remote computers.
We use powersell command-let invoke-command to execute the command in the remote systems. Invoke-Command is the most used command-let to execute scripts remotely.
To use Invoke-Command, powershell remoting has to be enabled in the remote system. Group policy can be used to deploy this feature in the organization.
One liner to fetch local administrator group details
Invoke-Command -ScriptBlock { Get-LocalGroupMember administrators } -ComputerName pc1, pc2, pc3 | select PSComputerName, Name, SID, PrincipalSource
Result:
PSComputerName Name SID PrincipalSource
-------------- ---- --- ---------------
pc1 pc1\Administrator S-1-5-21-1644265705-1531034170-3899888674-500 Local
pc1 pc1\user S-1-5-21-1644265705-1531034170-3899888674-1002 Local
pc2 pc2\Administrator S-1-5-21-1644265705-1531034170-3899899674-500 Local
pc3 pc3\Administrator S-1-5-21-1644265705-1531034170-3899875674-500 Local
This one liner will connect to the respective computers mentioned in the ComputerName parameter and lists out the member details. The PrincipalSource shows whether the object belongs to local or domain.
Note: As the script will connect to the remote computers, we need to run it with a user account which has access to these machines.
Get-LocalGroupMember command-let is part of Microsoft.PowerShell.LocalAccounts and was made available from the version Powershell 5.1.
Further details of al commands are documented at https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.localaccounts/?view=powershell-5.1
Script to fetch the local administrator group details
We will now wrap the above one liner to a function with the necessary checks to ensure that the computers are online by checking with a ping request.
function Fetch-LocalAdminMembers
{
[CmdletBinding()]
[Alias()]
Param
(
[Parameter(Mandatory=$true)]
[String[]]$Computers,
[Parameter()]
[pscredential]$Credential
)
begin
{
#Checking whether the systems are online
#OnlineComputers variable will have all the computers which responded to ping
$OnlineComputers = @()
$Computers | ForEach-Object {
if(Test-Connection $_ -Count 1 -ErrorAction SilentlyContinue) {
$OnlineComputers += $_
}
}
}
process
{
#If credentials are not passed, the script runs in current user context
if($Credential -eq $null)
{
Invoke-Command -ScriptBlock {Get-LocalGroupMember administrators; Start-Sleep -Seconds 2} -ComputerName $OnlineComputers | select PSComputerName, Name, SID, PrincipalSource
}
else
{
Invoke-Command -ScriptBlock {Get-LocalGroupMember administrators; Start-Sleep -Seconds 2} -ComputerName $OnlineComputers -Credential $Credential | select PSComputerName, Name, SID, PrincipalSource
}
}
end
{
$OnlineComputers = $Credential = $null
}
}
Executing the function
Fetch-LocalAdminMembers -Computers pc1, pc2
Result:
PSComputerName Name SID PrincipalSource
-------------- ---- --- ---------------
pc1 pc1\Administrator S-1-5-21-1644265705-1531034170-3899888674-500 Local
pc1 pc1\user S-1-5-21-1644265705-1531034170-3899888674-1002 Local
pc2 pc2\Administrator S-1-5-21-1644265705-1531034170-3894649674-500 Local
pc2 pc2\user2 S-1-5-21-1644265705-1531034170-3894649674
-1002 Local
We can refine the script further for faster execution, If you can identify it, I highly recommend you to comment it with the code snippet.
This will be helpful for the readers and will make this more interactive.
Hope you liked the article and thank you for reading
$Result = @()
foreach($server in (gc .\test-servers.txt)){
$computer = [ADSI](”WinNT://” + $server + “,computer”)
$Group = $computer.psbase.children.find(”Administrators”)
function getAdmins
{$members = $Group.psbase.invoke(”Members”) | %{$_.GetType().InvokeMember(”Adspath”, ‘GetProperty’, $null, $_, $null)}
$members}
$Result += $server
$Result += ( getAdmins )
$Result += “————————————————————”
}
$Result
Excellent script Jeff. Now can you make it enumerate every group membership in Local Administrators?