GroupMembership

Welcome Forums General PowerShell Q&A GroupMembership

This topic contains 23 replies, has 3 voices, and was last updated by

 
Participant
3 weeks, 3 days ago.

  • Author
    Posts
  • #171934

    Participant
    Topics: 10
    Replies: 48
    Points: 200
    Rank: Participant

    I have some groups. For example:

    Group1. Group2, Group3 are members of Group1 and Group4 is a member of Group2.

    and few users are also members of Group1 and Group2.

    I want to generate a report of all nested groups inside group1, I don't want the users.

    For example:

    Group1 Group2 Group4
    Group3

    How can I achieve this, so far I have this script:

    $allgroups=Get-ADGroup -Filter { Name -like "Group*"} -server "lab.local
    $res=foreach ( $g in $allgroups)
    {
    $members = Get-ADGroupMember -Identity $g -Server lab.local
    }
    foreach ($member in $members)
    
    {
    
    if ($member.objectClass -eq 'group')
    
    {
    
    [pscustomobject]@{
    Group = $g
    Member = $member.Name}
    }
    }
    
    $res
    
  • #172039

    Participant
    Topics: 0
    Replies: 100
    Points: 363
    Helping Hand
    Rank: Contributor

    I've been working on a similar process for Exchange Distribution Groups. I created another advanced function for Nested AD Groups and made it available. Below is a link to the GitHub repository if you like to try it out. I will add if you don't have the permissions to view a specific group it will return an error stating object not found on the server.

    Get-NestedADGroupMember -Identity 'Domain Admins' -ListGroups

    https://github.com/JasonRobertson/PowerShell/blob/master/Get-NestedADGroupMember/Get-NestedADGroupMember.ps1

    • #172078

      Participant
      Topics: 10
      Replies: 48
      Points: 200
      Rank: Participant

      Okay. I tried this however it is listing the groups in a list, I want to know the hierarchy of the group membership. Probably export to a csv file. Like if

      Group2, Group3 are members of Group1 and Group4 is a member of Group2 and few users are also members of Group1 and Group2. I don't want to the users in the output file.

      Group1 Group2 Group4
      Group3
      Group5 Group6 Group8
      Group7
    • #172084

      Participant
      Topics: 0
      Replies: 100
      Points: 363
      Helping Hand
      Rank: Contributor

      If you use the Get-NestedDistributionGroupList you can use those outputs to create a custom object and export to a csv file. Below is an example code

      $Results = @()
      Foreach ($ADGroup in (Get-ADGroup -Filter *)){
      $Object= [PSCustomObject]@{
      GroupName=$adgroup.Name
      NestedGroups=Get-NestedADGroupMember-Identity $ADGroup-ListGroups |Select-Object-ExpandProperty Name
      }
      $Results+=$Object
      }
      $Results
      GroupName      NestedGroups
      ---------      ------------
      Administrators {Enterprise Admins, Domain Admins}
      
      $Results | Export-CSV -Path $env:Path\ADGroup_Nested.csv -NoTypeInformation
  • #172042

    Participant
    Topics: 0
    Replies: 4
    Points: 21
    Rank: Member
    $members = Get-ADGroupMember -Identity $g -Server lab.local | where-object {$_.objectClass -eq "Group"}

    If you do this, you won't need the second foreach loop...

    • #172102

      Participant
      Topics: 10
      Replies: 48
      Points: 200
      Rank: Participant

      Thanks for the reply. It would be helpful if you explain the logic in your script? Also I want the output as below:

      Group1 Group2 Group4
      Group3
      Group5 Group6 Group8
      Group7
    • #172216

      Participant
      Topics: 10
      Replies: 48
      Points: 200
      Rank: Participant

      Any suggestion on the logic behind the script or any other way to obtain the information?

    • #172300

      Participant
      Topics: 0
      Replies: 100
      Points: 363
      Helping Hand
      Rank: Contributor

      Are you asking p42p0wd3r or in general? The replies are under p42p0wd3r response.

  • #172081

    Participant
    Topics: 10
    Replies: 48
    Points: 200
    Rank: Participant

    Also I want to be able to query the group in another domain in the same forest also and I want the script to be able to find the groups in other domains as well.

    • #172090

      Participant
      Topics: 0
      Replies: 100
      Points: 363
      Helping Hand
      Rank: Contributor

      Great news! That has already been taken care of in the script as well. 😀

      Here is the logic from the script

      $DomainList= (Get-ADForest).domains |Get-ADDomain|Select-Object DistinguishedName, DNSRoot |Sort-Object
      foreach ($Domain in $DomainList){
         If ($Group[0].DistinguishedName.EndsWith($Domain.DistinguishedName)) {
            $GetADGroup=@{
               Identity=$Group[0].DistinguishedName
               Server=$Domain.DNSRoot}
         }
      }
  • #172087

    Participant
    Topics: 10
    Replies: 48
    Points: 200
    Rank: Participant

    I got the below error when I tried the Get-NestedADGroupMember
    Cannot compare "CN=Group1,DC=lab,DC=local" because it is not IComparable.
    At line:116 char:13
    + If ($Group -gt 0){
    + ~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NotIcomparable

    • #172093

      Participant
      Topics: 0
      Replies: 100
      Points: 363
      Helping Hand
      Rank: Contributor

      I receive a similar error, when I spoke with my Domain Admin, he advised me this is because I don't have permissions to view the details of that security group. Remember if this gets you 99% of the way there, it's better than being 0%. 🙂

  • #172348

    Participant
    Topics: 10
    Replies: 48
    Points: 200
    Rank: Participant

    Are you asking p42p0wd3r or in general? The replies are under p42p0wd3r response.

    Hi Jason,

    Sorry for the confusion. I am asking you. Can you explain the logic of your script. What your script is doing steps by step?

    Also I would like the output as below:

    Group1 Group2 Group4
    Group3
    Group5 Group6 Group8
    Group7

    Can we do something like this, if yes how?

    Check the groupmembership of group1 and put only its members which are groups in a variable says members.

    Then again loop through groups in the members variable and check if it contains any groups as members.

    They problems happens that type of members variable changes to string and it is no longer of the type ADPrincipal.

    If this sounds confusing then please explain the logic of your script step by step, that will be really helpful.

    • #172390

      Participant
      Topics: 0
      Replies: 100
      Points: 363
      Helping Hand
      Rank: Contributor

      Hey Tech,

      Sorry for the delay in the reply, busy day today at work. 🙂 I'm happy to explain the script further be prepared for a bit of a long post.

  • #172396

    Participant
    Topics: 0
    Replies: 100
    Points: 363
    Helping Hand
    Rank: Contributor

    If you read the verbose commands in the script, they will explain a bit of what is being done.

        Begin
        {
            Write-Verbose "ENTER - BEGIN BLOCK"
            Write-Verbose "Create User, Group and GroupList variables"
            [System.Collections.ArrayList]$User = @()
            [System.Collections.ArrayList]$Group = @()
            [System.Collections.ArrayList]$GroupList = @()
            Write-Verbose "Collect AD Domains"
            $DomainList = (Get-ADForest).domains | Get-ADDomain | Select-Object DistinguishedName, DNSRoot | Sort-Object
            Write-Verbose "EXIT - BEGIN BLOCK"
        }

    I use an [ArrayList] instead of a [Array] for the User, Group and GroupList, because you cannot remove an object from an array because it is a type with a fixed size. An array can increase in size but never decrease.  An [ArrayList] on the other hand does have the remove method in its type.

    The $DomainList variable is used to obtain the DisinguishedName and DNSRoot properties for each of the domains in the organization. The properties are used later to determine what domain the group is located in. This is important for when the group i.e. Administrators has Enterprise Admins nested from the parent domain, refer to lines 129 – 135.

    The process block is quite extensive and handles all of the logic, I'm going to break this down into two pieces.

    The first part I will discuss is the foreach loop created on line 100. I use the Get-ADGroupMember command to identify the members of the parent ad group based on what was provided for the Identity parameter. With each of the members, the switch statement identifies if the ObjectClass of the member is Group or not.

    Lines 101 – 122.

        Process
        {
            Write-Verbose "ENTER - PROCESS BLOCK"
            Write-Verbose "ENTER - Foreach - $Identity"
            Foreach ($Member in (Get-ADGroupMember -Identity $Identity)){
                switch ($Member.ObjectClass) {
                    Group {
                        Write-Verbose "Nested AD Group Identified: $($Member.Name)"
                        If ($Member.DistinguishedName -notin $Group.DistinguishedName) {
                            $Group.Add($Member) | Out-Null
                            IF ($ListGroups) {
                                $GroupList.Add($Member) | Out-Null
                            }
                        }
                        Else {
                            Write-Verbose "$($Member.Name) is already identified, skipping to mitigate duplicate entry"
                        }
                    }
                    default{
                        If ($Member.DistinguishedName -notin $User.DistinguishedName) {
                            $User.Add($Member) | Out-Null
                        }
                        Else {
                            Write-Verbose "$($Member.Name) is already identified, skipping to mitigate duplicate entry"
                        }
                    }
                }
            }
            Write-Verbose "EXIT - Foreach - $Identity"

    To identify the Objectclass we can do this with an IF Statement or a Switch as seen below.

    If ($member.ObjectClass -eq 'Group'){
       If (Member.DistinguishedName -NotIn$Group.DistinguishedName){
          $Group.Add($member) | Out-Null
       }
    }
    Else{
       If ($Member.DistinguishedName -notin $User.DistinguishedName){
          $User.Add($Member) | Out-Null
       }
    }

    The If statement is a viable option; however, the switch statement allows the same and increases the legibility of the code.

    As each member is iterated through and the object is added either to the [ArrayList]$Group or [ArrayList]$User. If the -ListGroups parameter is used, groups are added to the [ArrayList]$GroupList.

    The second piece of the Process block deals with the desired recursing of the nested groups to find additional nested groups. This is where the "MAGIC" happens. By using the Do-While loop, while [ArrayList] $Group -gt 0, resolves the issue of how many levels of nested groups can be drilled down. The answer is pretty much until the computer/server runs out of RAM.

    As previously stated in a prior post, we identify the domain via a ForEach loop with $DomainList. The If statement checks the first group object distinguished name and uses the EndsWith() methods to check wether $Domain.DistinguishedName is at the end of $Group[0].DistinguishedName. If this is true, the $Domain.DNSRoot property is assigned to the Server parameter. DNSRoot is used because the -Server parameter for Get-ADGroupMember does not support DistinguishedName.

    Lines 129 – 136

    foreach ($Domain in $DomainList){
       If ($Group[0].DistinguishedName.EndsWith($Domain.DistinguishedName)) {
          $GetADGroup = @{
             Identity = $Group[0].DistinguishedName
             Server   = $Domain.DNSRoot
          }
       }
    }

    The $GetADGroup variable is a hashtable and I use a method in PowerShell known as Splatting.

    "Splatting is a method of passing a collection of parameter values to a command as a unit. PowerShell associates each value in the collection with a command parameter. Splatted parameter values are stored in named splatting variables, which look like standard variables, but begin with an At symbol (@) instead of a dollar sign ($)" – Microsoft About Splatting

    I digress, back to the script. 🙂 During the Do-While loop, the goal is only to look at one group at a time, this is accomplished by only indexing the first entry in [ArrayList] $Group. This is accomplished with [0] to the right of $Group. By using the distinguished name, I remove almost all ambiguity from the query, but this doesn't work if the group is in multiple domains i.e. Domain Admins. This issue is resolved by comparing the end of the distinguished name matches the distinguished name of the AD domain.

    At this point, the logic is a rinse and repeat as previously done in lines 100 – 123 and continues to repeat while the $Group -gt 0. Once the group has been processed, it is removed from the [Arraylist]$Group with the remove method. The default behavior is to provide all of the users, but if you use -ListGroups instead this will bypass all Object Classes, not equal to Group.

    Woohoo! Finally done with that explanation. That is a lot of information to digest.

  • #172423

    Participant
    Topics: 0
    Replies: 100
    Points: 363
    Helping Hand
    Rank: Contributor

    Going back to the example of the export, why doesn't this work for you? It will give you the Parent Group and all of the nested groups inside it. If you are looking for a Hierarchial Tree structure like below that will take some development.

    Parent Group

    –> ChildGroup1

    -> ChildGroup10
    -> ChildGroup11

    –> ChildGroup2

    -> ChildGroup1
    -> ChildGroup3

  • #172757

    Participant
    Topics: 10
    Replies: 48
    Points: 200
    Rank: Participant

    Going back to the example of the export, why doesn't this work for you? It will give you the Parent Group and all of the nested groups inside it. If you are looking for a Hierarchial Tree structure like below that will take some development.

    Parent Group

    –> ChildGroup1

    -> ChildGroup10

    -> ChildGroup11

    –> ChildGroup2

    -> ChildGroup1

    -> ChildGroup3

    Hello Jason, Thanks for your explanation and reply. However, I want to extract the group membership three levels down. I don't want the indentation. Just want the nesting to appear in different columns in a csv file. How can I achieve this?

  • #173029

    Participant
    Topics: 0
    Replies: 100
    Points: 363
    Helping Hand
    Rank: Contributor

    Is this what you are trying to do?

    ParentGroup NestedDGMembers1 NestedDGMembers2 NestedDGMembers3
    ParentDG1 DG1

    DG2

    DG4 DG5

    DG6

    ParnetDG2 DG2 DG3 DG4

    DG1

     

    • #173035

      Participant
      Topics: 10
      Replies: 48
      Points: 200
      Rank: Participant

      Yes that's correct.

  • #173050

    Participant
    Topics: 0
    Replies: 100
    Points: 363
    Helping Hand
    Rank: Contributor

    Techsavy,

    That would take a bit of manipulation. Any reason you are trying to only drill down 3 nested layers?

    • #173095

      Participant
      Topics: 10
      Replies: 48
      Points: 200
      Rank: Participant

      Because we don't expect more nesting than 2 layers for the groups we are searching for. So at max searching for 3 layers should be sufficient.

  • #173266

    Participant
    Topics: 0
    Replies: 100
    Points: 363
    Helping Hand
    Rank: Contributor

    Okay, below is the new code to try out. I had to create a whole new function to carry this out for you.

  • #173269

    Participant
    Topics: 0
    Replies: 100
    Points: 363
    Helping Hand
    Rank: Contributor

    Here is an example of it in action

    C:\Users\Jason.robertson> Get-NestedGroup -Identity sales | Export-Csv C:\Temp\Example.csv
    C:\Users\Jason.robertson> Import-Csv C:\Temp\Example.csv | FL
    
    ParentDG  : Sales
    NestedDG1 : team-sales-development-reps
    NestedDG2 : No Nested Group
  • #173368

    Participant
    Topics: 10
    Replies: 48
    Points: 200
    Rank: Participant

    Thanks Jason. Will try it out.

You must be logged in to reply to this topic.