Get-ADGroup member into expanded output

I’m using a command similar to:

Get-ADGroup -Server $dc -Identity $group -Properties member | Select-Object -ExpandProperty member -Property Name | Select-Object *

and I get output similar to:

Name             Length
----             ------
GroupName            54
GroupName            58
GroupName            60

where the lengths are correct to the length of the member value but I can’t seem to get the actual member value. I can get the member values without other data but I need to combine this information.

For info, there is a member called Chars which is a method. So if I use:

... | Select-Object -ExpandProperty member -Property Name | Foreach-Object { $_.Chars(0) }

I get the first character of the member value. I also have the length of the member value but would need some way to pass this range to the Chars() method or some way to return the whole value from Chars().

Any ideas?

This doesn’t seem like the right way to do this but it does work:

... | Select-Object Name,SID,@{Name="member"; Expression={$member = ""; for ($i = 0; $i -lt $_.Length; $i++) {$member += $_.Chars($i)}; $member}}

Hopefully someone can tell me the obvious improvement to this! There must be a way to access the expanded property values!!!

Is there a particular reason why you’re not using

to get the members of a given AD group?

Because I can get all the AD group properties I need (including direct members) in only one pass of the AD using Get-ADGroup. As I’m storing this in a SQL database, I can deal with hierarchical group membership within the database.

Might help to know which group properties you are wanting.

Name,SID,objectGUID,member,distinguishedName are the main ones.

But since my only issue is with expanding the “member” property, this doesn’t really change my question.

If I only return the expanded “member” property, the data is there. If I add other properties in the Select-Object, I only get access to “Chars” and “Length” for this information.

You are quite confused about what is actually happening here. Take some simpler steps to see what is going on. Run just this

Get-ADGroup -Server $dc -Identity $group -Properties member | Select-Object -ExpandProperty member

You will only see a list of Distinguished Names for the members of the group. There is no other data about those users contained here. You will have to query AD for the users details. I’ll touch on this more later on. Now run

Get-ADGroup -Server $dc -Identity $group -Properties member | Select-Object -ExpandProperty member |  Get-Member

It will tell you the object is a string. It’s simply a string representation of the distinguished name. Now run this

Get-ADGroup -Server $dc -Identity $group -Properties member | Select-Object -ExpandProperty member | Select *

This will show you a length. Which is all powershell can do because you passed a string as an object. The object was destroyed when you used -ExpandProperty, because that property was a string.

Now combine it with -Property Name

Get-ADGroup -Server $dc -Identity $group -Properties member | Select-Object -ExpandProperty member -Property Name

You will still only see the DistinguishedName. The name is output to the pipeline but not to the screen. Confirm this by running

$ouptut = Get-ADGroup -Server $dc -Identity $group -Properties member | Select-Object -Property Name -ExpandProperty member

$output.name

You should see the group names. However, Get-Member will still only show this as a string.
You can add properties to a string but it will always just show it’s string representation. So running

$output

Will show the Distinguished names of the members. Now wrapping it all up, when you do Select *, you are collecting the name property and the string which powershell says “a string, return the length”

You can confirm this by checking the distinguished names against the length, it will be the exact number.

$output | Foreach-Object {
    Write-Host "The group name is $($_.Name)"
    Write-Host "The distinguished name is $_"
    Write-Host "The distinguished name is $($_.Length) characters long"
}

Now to better handle your goal, I would recommend

First, make a hashtable of all the users in AD, so you don’t have to keep querying AD for potentially the same users again and again. The key for the hashtable will be the distinguished name, making lookups very simple later.

$aduserlist = Get-Aduser -Filter * -Properties Desired, Properties, Here
$usertable = $aduserlist | Group-Object -Property DistinguishedName -AsHashTable

Now while you’re processing the groups, you can pull all the member details from the table

Get-ADGroup -Server $dc -Identity $group -Properties members | ForEach-Object {
    Write-Host "The AD Group is $($_.Name)"
    Write-Host "There are $($_.Members.Count) members in this group"

    foreach($member in $_.members){
        $currentmember = $usertable[$member]
        Write-Host "Member: $($currentmember.Name)  Login: $($currentmember.samaccountname) -ForegroundColor Cyan
    }
}

Hopefully this illustrates the issue you’re seeing and why, as well as give you some direction for acheiving your desired output. (which you never really said.)

One last note, this does not account for other groups or devices, which may be members of the groups as well.

2 Likes

Try this
Get-ADGroup $group-Properties member |select -Property name, @{n='member'; e="member"}

You can put result to variable, and do what you need.

This just outputs the “member” as a PSCustomObject of members. The requirement is to expand this into the result set.

But if you remove the final part of that query:

Get-ADGroup -Server $dc -Identity $group -Properties member | Select-Object -ExpandProperty member 

I get a list of members for the AD group, one per line - which is what I want. The problem is that I want this combined with the Name, etc. of the group. Adding:

-Property Name

to this statement doesn’t add anything to the output. If you store the output from the first command (without “-Property Name”) into a variable, it contains the data for the group’s membership.

For example:

$members = Get-ADGroup -Identity $group -Properties name,member | Select-Object -ExpandProperty member
$members
CN=.....
CN=.....
CN=.....

$members = Get-ADGroup -Identity $group -Properties name,member | Select-Object -ExpandProperty member -Property Name
$members
CN=.....
CN=.....
CN=.....

$members = Get-ADGroup -Identity $group -Properties name,member | Select-Object -ExpandProperty member -Property Name | Select-Object *
$members
Name          Length
----          ------
Group1            57
Group1            58
Group1            60

So the expansion of the [string]member value works in the first Select-Object but it won’t include the non-expanded properties (even though they’re available via $members.Name, etc.). However, passing it to the second Select-Object loses the functionality to display the string and just shows the length.

The overall requirement is to pass this data to Out-DataTable and store in a variable. But passing the $members variable to Out-Data has the same result as passing it to a second Select-Object:

$members = Get-ADGroup -Identity $group -Properties name,member | Select-Object -ExpandProperty member -Property Name
$members
CN=.....
CN=.....
CN=.....
$members.Name
Group1
Group1
Group1

$dt = $members | Out-DataTable
$dt
Length
------
    57
    58
    60
$dt.Columns.Count
1
$dt.Columns[0].Name
Length
$dt.Columns[0].DataType
IsPublic IsSerial Name  BaseType
-------- -------- ----  --------
True     True     Int32 System.ValueType

So I can save the single Select-Object output into a variable, and it’ll contain everything I need. However there’s no simple way to pass that information to Out-DataTable (or any other pipeline command).

You also say:

Why doesn’t it say “a string, return the content”? After all, it doesn’t say “an integer, return the bit count”.

A summary of what I’d like to do is:

$dt = Get-ADGroup -Identity $group -Properties name,SID,distinguishedName,member | Select-Object -Property name,SID,distinguishedName -ExpandedProperty member | Out-DataTable
$dt
name   SID         distinguishedName member
----   ---         ----------------- ------
Group1 S-1-4-5-... CN=Group1,OU=...  CN=Member1,OU=...
Group1 S-1-4-5-... CN=Group1,OU=...  CN=Member2,OU=... 
Group1 S-1-4-5-... CN=Group1,OU=...  CN=Member3,OU=...

I can achieve this with multiple loops through the data but I don’t understand why the single Select-Object command doesn’t appear to work as expected.

You really aren’t understanding that the members property is a simple string. Best of luck to you.

Clearly I’ve missed something in your assistance but thanks for trying to help me. I’ll stick with the:

... | Select-Object Name,SID,@{Name="member"; Expression={$member = ""; for ($i = 0; $i -lt $_.Length; $i++) {$member += $_.Chars($i)}; $member}}

method as it delivers what I need, even though I don’t understand why it’s necessary.

Did you even try any of the tests I suggested? I think It would’ve been very helpful to you.

They were methods I’d tried previously. They produce the output I’m looking for but since the Select-Object command has an -ExpandProperty option, I was trying to understand why I needed to run multiple loops to achieve what (on the surface) should be achievable with a single command.