PowerShell Problem Solver: Use PowerShell to Find Local Groups and Members

Identifying members of local groups is an ongoing task for IT pros. Although I’ve written about local groups before on the Petri IT Knowledgebase, another discussion thread came up, so I thought I’d revisit this topic again as I think it makes for a good learning opportunity.

The goal is to have PowerShell write something to the pipeline that indicates the computer name, the name of a local group, and its members. There is a possibility that the output might be directed to a CSV file, so keep that in mind. I’m going to test with the local computer. Eventually we will be able to scale out, although even that part of the task has some options.

An easy way to list local groups is with WMI. You can use either Get-WMIObject or Get-CimInstance.

Using get-wmiobject in Windows PowerShell. (Image Credit: Jeff Hicks)
Using get-wmiobject in Windows PowerShell. (Image Credit: Jeff Hicks)
  The code in the forum was then turning around and using a ForEach loop to get each group using ADSI.
​
You can then use some arcane COM code to extract the member names.
031315 1905 PowerShellP2
You could use those values in your output. We'll come back to the ADSI code.
My first thought was to simply use WMI for everything. Each Win32_Group object has a collection of associated classes, including Win32_UserAccount, which would reflect the users.

You could construct a convoluted Associators Of query. But I prefer to use the included GetRelated method. You can specify a class name as a parameter to limit the search. With that, I can do everything in a one line command.
​
You'll notice the use of the –Join operator. Without it, the value of Members would be an array, which would look like {Administrator,Jeff,svcTest}. It would make sense on the screen, but it doesn't make sense to export to a CSV file. You would end up with something like System.Object[]. So instead, I'm using –join to join the names into a single string, separated by a semi-colon. Since the output might end up in a CSV file, I didn't want to complicate it by having a nested CSV string. But you can use any delimiter you want, even a space. A potential problem is that these types of "what's related" WMI queries are painfully slow. But they work.
031315 1905 PowerShellP3
You can achieve similar results with the CIM cmdlets. In this case, there is a dedicated cmdlet.
​
But this is also slow, especially if you are querying many remote computers. One way to mitigate is to run the WMI or CIM command remotely using Invoke-Command.
​
Now the WMI query is running simultaneously on all of the remote computers. You could even run Invoke-Command with its AsJob parameter and check the results later.

Turning back to the original post, it made sense that if the original solution was going to use ADSI, why not use it for the entire task? It would make more sense to use ADSI to make a connection to retrieve all of the groups and then enumerate each group's members. Here's the code I came up with.
​
Notice that again I am joining the members into a single string.
Joining members into a string with Windows PowerShell. (Image Credit: Jeff Hicks)
Joining members into a string with Windows PowerShell. (Image Credit: Jeff Hicks)
This is pretty quick. It doesn't take much to create a CSV audit trail from here.
​
Even here, we might want to take advantage of remoting to improve performance. In fact, you might need to use remoting anyway for firewall purposes. Using ADSI will require RPC and DCOM connectivity which may be blocked. By wrapping this in a remoting command, however, everything happens over a single port.
​
Because Invoke-Command already gives me the computername, I edited the scriptblock to remove the code that did the same thing. Later I can work with the data in Excel.
We can now work with our data in Microsoft Excel. (Image Credit: Jeff Hicks)
We can now work with our data in Microsoft Excel. (Image Credit: Jeff Hicks) 
Or re-import it back to PowerShell and slice it up there.
031315 1905 PowerShellP6
Or maybe I want to find where Jack Frost has stuck his nose in.
Searching for the user Jack Frost in Windows PowerShell. (Image Credit: Jeff Hicks)
Searching for the user Jack Frost in Windows PowerShell. (Image Credit: Jeff Hicks)
The point is that once you have this data there is much you can do with it. And getting the data can be done in a variety of ways. It is up to you to balance simplicity, ease of use, and performance. If you get stuck figuring it out, feel free to use the Petri IT Knowledgebase PowerShell forum.