PowerShell to find all members of Local groups on a list of servers and saving list to CSV

Hi

I am looking for script that can find all members of Local groups pulled from input file (over 300 groups) from  one server and saving list to CSV in specific format that can be used to create these groups in AD.

Share_Name      Owner1      Owner2      Owner3      Owner4      MemberList
Facilities               novak                                                 azxxx,bnexon,xusley,

This is what i got so far but doesn't work like i want
----------------------------------------------

Function global:Get-LocalGroup{

#Requires -Version 2.0            
[CmdletBinding()]            
 Param            
   (                      
    [Parameter(Mandatory=$false,
               Position=1,                          
               ValueFromPipeline=$true,            
               ValueFromPipelineByPropertyName=$true)]            
    [String[]]$ComputerName = "localhost",
    [String]$LocalGroup = "Administrators",
    [Switch]$Export
   )#End Param

Begin            
{            
      $MemberNames = @()    
}#Begin          
Process            
{

    $ComputerName | ForEach-Object {
    $Computer = $_
          $Group = [ADSI]"WinNT://$Computer/$LocalGroup,group"
          
        # if statement to check for errors if the local group does not exist.
        if ($Group.Path)
            {
                $Members = @($Group.psbase.Invoke("Members"))
                  [Array]$MemberNames = $Members |
                    ForEach-Object {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}


                if ($Export)
                    {
                     $Hash = @{
                        Server= $Computer.ToUpper()
                        LocalGroup = $LocalGroup
                        Groups= $MemberNames -join ", "
                        }
                    }
                else    
                   {
                     $Hash = @{
                        Server     = $Computer.ToUpper()
                        LocalGroup = $LocalGroup
                        Groups     = $MemberNames
                        }
                   }
       
                New-Object PSObject -Property $Hash |
                    Select Server,LocalGroup,Groups
            }
        else
            {
                Write-Verbose -Message "Group `"$LocalGroup`" does not exist on `"$Computer`"" -Verbose
            }
    }#Foreach-Object(ComputerName)
   
}#Process
End
{

}#End


}#Get-LocalAdmin

------------------------------

However, i am not getting any results. I am missing something .

Please help
michalek19Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

YZlatCommented:
Why not use something like this?

$Groups = Get-Content C:\LocalGroupsList.txt

$MemberNames = @()

foreach ( $LocalGroup in $Groups ) {
 	$Servers = $env:computername # for testing on local computer


	foreach ( $Server in $Servers ) {
        	$Group= [ADSI]"WinNT://$Server/$LocalGroup,group"
        	$Members = @($Group.psbase.Invoke("Members"))

        	$Members | ForEach-Object {
                	$MemberNames += $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
        	} 
        
                Write-Output $MemberNames
	}

}

Open in new window


The code above will read in an input text file with a list of all the local groups you are interested in, retriev e all members from each group and print to the screen
0
michalek19Author Commented:
I need to run this for remote server.
Also, is it possible output in csv format

Share_Name      Owner1      Owner2      Owner3      Owner4      MemberList
Facilities            novak                                                              azxxx,bnexon,xusley,
0
YZlatCommented:
just one server or multiple servers?

If one server, then replace this line:

$Servers = $env:computername # for testing on local computer

with your server name

if multiple servers, then tell me where will you be retrieving server list?
0
Has Powershell sent you back into the Stone Age?

If managing Active Directory using Windows Powershell® is making you feel like you stepped back in time, you are not alone.  For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why.

michalek19Author Commented:
If one server, then replace this line:

For signle remote server, that'w what i have to change is this correct?
$Servers = $env:bos-ref  


If multiple servers, then tell me where will you be retrieving server list?
c:\servers

Can you give me example for both
0
YZlatCommented:
use

$Servers = bos-ref  

since $env:computername is just an environmental variable that retrieves current computer name. You do not need to preceed remote computer name with $env:
0
michalek19Author Commented:
I am getting errors

httPS C:\> ./groupexp.ps1
The term 'bos-ref' is not recognized as the name of a cmdlet, function, script
file, or operable program. Check the spelling of the name, or if a path was inc
luded, verify that the path is correct and try again.
At C:\groupexp.ps1:6 char:21
+      $Servers = bos-ref <<<<  # for testing on local computer
    + CategoryInfo          : ObjectNotFound: (bos-fps:String) [], CommandNotF
   oundException
    + FullyQualifiedErrorId : CommandNotFoundException

Exception calling "Invoke" with "2" argument(s): "Unknown error (0x80005000)"
At C:\groupexp.ps1:11 char:43
+             $Members = @($Group.psbase.Invoke <<<< ("Members"))
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

The term 'bos-fps' is not recognized as the name of a cmdlet, function, script
file, or operable program. Check the spelling of the name, or if a path was inc
luded, verify that the path is correct and try again.
At C:\groupexp.ps1:6 char:21
+      $Servers = bos-ref <<<<  # for testing on local computer
    + CategoryInfo          : ObjectNotFound: (bos-fps:String) [], CommandNotF
   oundException
    + FullyQualifiedErrorId : CommandNotFoundException

Exception calling "Invoke" with "2" argument(s): "Unknown error (0x80005000)"
0
YZlatCommented:
Try this for multiple servers:

$Groups = Get-Content C:\LocalGroupsList.txt
$Servers = Get-Content C:\ServerList.txt
$MemberNames = @()
$MasterArray = @()

foreach ( $Server in $Servers ) {
        Write-Host $Server
        $ServerDataArray = @()
        
        foreach ( $LocalGroup in $Groups ) {
        Write-Host $LocalGroup
            $TempArray = @()
            $TempArray = "" | Select ServerName, GroupName, MemberNames
            
            [string]$TempArray.ServerName = $Server
            [string]$TempArray.GroupName = $LocalGroup
            $Group= [ADSI]"WinNT://$Server/$LocalGroup,group"
            $Members = @($Group.psbase.Invoke("Members"))

            $Members | ForEach-Object {
                $MemberNames += $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) + ','
            } 
       
           
            [string]$TempArray.MemberNames = $MemberNames
      #Copy the contents of the TempArray into the ServerDataArray. The TempArray is renewed for the next server.
         $ServerDataArray += $TempArray
         
        }
        
    $MasterArray += $ServerDataArray
}

$MasterArray | Export-CSV C:\ListOfMembers.csv -NoType

Open in new window


put bos-ref into a ServersList.txt file and the code above will pick it up.

The above is for multiple servers. For a single server use:

$Groups = Get-Content C:\LocalGroupsList.txt
$MemberNames = @()
$MasterArray = @()

$Server=bos-ref 
        
        foreach ( $LocalGroup in $Groups ) {
        Write-Host $LocalGroup
            $TempArray = @()
            $TempArray = "" | Select ServerName, GroupName, MemberNames
            
            [string]$TempArray.ServerName = $Server
            [string]$TempArray.GroupName = $LocalGroup
            $Group= [ADSI]"WinNT://$Server/$LocalGroup,group"
            $Members = @($Group.psbase.Invoke("Members"))

            $Members | ForEach-Object {
                $MemberNames += $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) + ','
            } 
       
           
            [string]$TempArray.MemberNames = $MemberNames
      #Copy the contents of the TempArray into the masterArray. The TempArray is renewed for the next server.
         $MasterArray += $TempArray
         
        }
        
$MasterArray | Export-CSV C:\ListOfMembers.csv -NoType

Open in new window

0
michalek19Author Commented:
For a single server use: doesn' t work. Still can't recognize single server but i will accept the other script that works like charm (multiple servers script works fine)
However, The output is save and i can open it. I would like to ask you if there is a way to remove automatically using this script from output file unknow or SID values "S-1-5-21-2452487316-3072070198-4014809800-35479"
0
michalek19Author Commented:
Is there a way to compare output f "ListOfMembers.csv" with AD groups. I am wondering if there are any dupicates.
0
michalek19Author Commented:
Multiple servers script runs fine but the result is not right.
What it does is adding already found members to each row. It multiplies member for each group. It looks like is copy members from above group and adds to new group.


Please check attachment. Also, is it possible to remove SID during output file creation
Output-file.xlsx
0
YZlatCommented:
Please use both scripts from my latest post, the previous one does not work correctly. I tested it. the last one works fine
0
michalek19Author Commented:
I used last one it works but the output "members" are not correct. Please check attachment.
0
YZlatCommented:
Those users are members of the group. I tested the code I provided here and it worked fine, retrieving all the members of each group and writing all of them to the file. that was the original question and I answered it
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Chris EspineCommented:
Hey YZlat,

I have modified your coded to provide other fields of information with the local admin group. The problem that i am having is that the script will crash powershell when it reaches a specific server. I was wondering if someone can point out where i went wrong. I am aslo attempting to add to this is script Group is local to machine as a true or false statement. Anyways here is the code below.

Function Get-LocalGroup {

 [Cmdletbinding()]

 PARAM (
        [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]
  [string[]$ComputerName = $env:COMPUTERNAME,
 
  [string]$GroupName = "Administrators"

  )
    BEGIN{
  }#BEGIN BLOCK

    PROCESS{
        foreach ($Computer in $ComputerName){
            Write-Host $Computer
             $ServerDataArray = @()

             Foreach ($Groups in $GroupName){
             Write-Host $Groups
                $Details = @()
                $Details = "" | Select-Object "Domain/Workgroup Name","Machine Name",Local_Group, "Group Members <List>","Group is Machine Local",Description
           
             $Group = [ADSI]"WinNT://$Computer/$Groups,group"
             $Members = @($group.psbase.Invoke("Members"))  
             $members | ForEach-Object {
             $name += $_.GetType().InvokeMember("Class", 'GetProperty', $null, $_, $null) + ': '
             $name += $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -Join "" | Out-String
             $Description += $_.GetType().InvokeMember("Description", 'GetProperty', $null, $_, $null)
             $CurrentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().GetDirectoryEntry() | select Name
             $Domain = $CurrentDomain.Name
               
              # Find out if this is a local or domain object
              if ($path -like "*/$Computer/*"){
               $Type = "Local"
               }
              else {$Type = "Domain"
              }

              }

              [string]$Details."Domain/Workgroup Name" = $Domain
              [string]$Details."Machine Name" = $Computer
              [string]$Details.Local_Group = $GroupName
              [string]$Details."Group Members <List>" = $name
              #$Details.Class = $class
              [string]$Details."Group is Machine Local" = $type
              [string]$Details.Description = $Description
 
              # Show the Output
                    $Details
                 }
             }
         }
    }
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
IT Administration

From novice to tech pro — start learning today.