SOLVED

Get nested groups in a group with members in an excel file

Copper Contributor

Hi everyone, I have the following quest powershell script:

 

 

Function Get-NestedGroupMember($group) { 
Get-QADGroupMember $group | foreach{ 
if($_.type -eq "group"){Get-NestedGroupMember($_)} 
else {$_}} 
} 
Get-NestedGroupMember("domain admins") | Select Name | Export-CSV .\test.csv -NoType

 

 

What It does is to bring all nested groups within "domain admins" group which is fine, but when I open the CSV file I can see the members of all nested groups, however what I need is to have another column stating which group the user is a member of.

 

Could you please help?

Many thanks!

4 Replies

@tincho1984 

 

Hi.

 

Firstly, you can pull the transitive membership a little more easily. Here's an example using the default Microsoft commandlets as I don't run the Quest module.

 

Get-ADObject -Filter { objectClass -eq "group" } -SearchBase ((Get-ADObject -Filter { (objectClass -eq "group") -and (cn -eq "Domain Admins") }).distinguishedName) -SearchScope Base -Properties msds-memberTransitive | Select-Object name, msds-memberTransitive;

 

This requires a maximum of only two calls to Active Directory, rather than a call for every nested group.

 

To produce the 1:1 listing between group name and member name that you asked for, you expand upon this statement above by using a loop construct, as shown below. Technically, you actually need two loops: one to handle the groups returned from Get-ADObject and a second to handle the members within msds-memberTransitive.

 

The basic idea is that you iterate through each member of the group, adding in the group name next to the member's distinguishedName.

 

$OutputFile = ".\someCsvFile.Csv";
$GroupName = "Domain Admins";
Get-ADObject -Filter { objectClass -eq "group" } -SearchBase ((Get-ADObject -Filter { (objectClass -eq "group") -and (cn -eq $GroupName) }).distinguishedName) -SearchScope Base -Properties msds-memberTransitive |
    ForEach-Object {
        $Group = $_;

        $Group.'msds-memberTransitive' |
            ForEach-Object {
                [PSCustomObject] @{
                    GroupName = $Group.name;
                    Member = $_;
                }
            }
    } | Export-Csv -NoTypeInformation -Path $OutputFile;

 

Which produces the output you were after.

 

Cheers,

Lain

@LainRobertson 

 

Hi, thank you so much for your effort and sorry for the late reply. When I ran the script I get the output file but not exactly what I need. Let me explain you this way, I think it will be easier:

 

For example, I have let's say a group called "Domain Users" and inside there are 5 more groups, let's call it Group1, Group2, Group3, Group4 and Group5. Each of these groups have several users, so the output file I need is something like this:

 

Domain Users:

 

Column A      Column B

-GROUP-         -USER-

Group1             Martin

Group1             Richard

Group2             Kevin

Group3             John

Group4             Brian

Group4             Gary

Group4             Sarah

Group5             Samantha

 

Of course, if the group "Domain Users" has also users, list them as well. Is this possible to achieve? Since the output file from the script you wrote list all the users inside the groups within but you can't see if they belong to Group1, or Group2 etc etc.

 

Thanks!

best response confirmed by tincho1984 (Copper Contributor)
Solution

@tincho1984 

 

Yeah, okay. So, you're after more of a detailed mapping rather than simply enumerating the members.

 

That being the case, you were on the right track, as to pull that additional metadata, you need to perform a separate call per member to the additional AD object of that member so you can in turn fetch things like objectClass and whatever else you want.

 

I've dropped another example below - which I'm a bit ashamed of as it's a rush job that uses Microsoft's ActiveDirectory module, but it probably does what you want and you already seem to be leveraging additional modules, so here goes.

 

PS: You could name the script whatever you like - I've provided a name simply so the examples further down align with the name.

Get-ADGroupMemberMappings.ps1

 

[cmdletbinding()]
Param(
    [parameter(Mandatory=$true)][string] $Name
)

# Let's get a domain controller reference to work with consistently throughout the script.
$Server = (Get-ADRootDSE).dNSHostName;

function Get-ADGroupMemberMappings([Microsoft.ActiveDirectory.Management.ADObject] $InputObject)
{
    if ($InputObject -and $InputObject.member)
    {
        foreach ($MemberPath in $InputObject.member)
        {
            $Member = Get-ADObject -Server "$($Script:Server):3268" -Identity $MemberPath -Properties member -ErrorAction:Stop;

            [PSCustomObject] @{
                ObjectID = $InputObject.ObjectGUID;
                Group = $InputObject.Name;
                MemberType = $Member.objectClass;
                Member = $Member.distinguishedName;
            }

            if ($Member.objectClass -ceq "group")
            {
                Get-ADGroupMemberMappings -InputObject $Member;
            }
        }
    }
}

Get-ADGroupMemberMappings -InputObject (Get-ADObject -Server $Server -Filter { (objectClass -eq "group") -and (cn -eq $Name) } -Properties member);

 

 

Example usage:

LainRobertson_0-1656388938367.png

 

You can assign that to a variable, pipe it to a CSV, etc. etc.

 

Anyhow, see if that's more in line with what you were after.

 

Edited:

Just to quickly mention that some of the built-in groups like Domain Computers and Domain Users - for which the "member" attribute are dynamically constructed - do not get enumerated when searching. Some extra legwork needs to be done to capture their members but I skipped doing so as it wasn't going to be time well spent.

 

You can tackle that unique requirement if you'd like but there's little value in doing so since you already know what the membership for those will be.

 

Cheers,

Lain

@LainRobertson 

 

That worked perfectly, exactly what I needed, thank you so so so much. Have a great day 🙂

 

Cheers

1 best response

Accepted Solutions
best response confirmed by tincho1984 (Copper Contributor)
Solution

@tincho1984 

 

Yeah, okay. So, you're after more of a detailed mapping rather than simply enumerating the members.

 

That being the case, you were on the right track, as to pull that additional metadata, you need to perform a separate call per member to the additional AD object of that member so you can in turn fetch things like objectClass and whatever else you want.

 

I've dropped another example below - which I'm a bit ashamed of as it's a rush job that uses Microsoft's ActiveDirectory module, but it probably does what you want and you already seem to be leveraging additional modules, so here goes.

 

PS: You could name the script whatever you like - I've provided a name simply so the examples further down align with the name.

Get-ADGroupMemberMappings.ps1

 

[cmdletbinding()]
Param(
    [parameter(Mandatory=$true)][string] $Name
)

# Let's get a domain controller reference to work with consistently throughout the script.
$Server = (Get-ADRootDSE).dNSHostName;

function Get-ADGroupMemberMappings([Microsoft.ActiveDirectory.Management.ADObject] $InputObject)
{
    if ($InputObject -and $InputObject.member)
    {
        foreach ($MemberPath in $InputObject.member)
        {
            $Member = Get-ADObject -Server "$($Script:Server):3268" -Identity $MemberPath -Properties member -ErrorAction:Stop;

            [PSCustomObject] @{
                ObjectID = $InputObject.ObjectGUID;
                Group = $InputObject.Name;
                MemberType = $Member.objectClass;
                Member = $Member.distinguishedName;
            }

            if ($Member.objectClass -ceq "group")
            {
                Get-ADGroupMemberMappings -InputObject $Member;
            }
        }
    }
}

Get-ADGroupMemberMappings -InputObject (Get-ADObject -Server $Server -Filter { (objectClass -eq "group") -and (cn -eq $Name) } -Properties member);

 

 

Example usage:

LainRobertson_0-1656388938367.png

 

You can assign that to a variable, pipe it to a CSV, etc. etc.

 

Anyhow, see if that's more in line with what you were after.

 

Edited:

Just to quickly mention that some of the built-in groups like Domain Computers and Domain Users - for which the "member" attribute are dynamically constructed - do not get enumerated when searching. Some extra legwork needs to be done to capture their members but I skipped doing so as it wasn't going to be time well spent.

 

You can tackle that unique requirement if you'd like but there's little value in doing so since you already know what the membership for those will be.

 

Cheers,

Lain

View solution in original post