Welcome Forums General PowerShell Q&A GroupMembership

Viewing 13 reply threads
  • Author
    Posts
    • #171934
      Participant
      Topics: 13
      Replies: 58
      Points: 268
      Rank: Contributor

      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: 115
      Points: 433
      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: 13
        Replies: 58
        Points: 268
        Rank: Contributor

        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: 115
        Points: 433
        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: 1
      Replies: 15
      Points: 85
      Helping Hand
      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: 13
        Replies: 58
        Points: 268
        Rank: Contributor

        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: 13
        Replies: 58
        Points: 268
        Rank: Contributor

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

      • #172300
        Participant
        Topics: 0
        Replies: 115
        Points: 433
        Helping Hand
        Rank: Contributor

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

    • #172081
      Participant
      Topics: 13
      Replies: 58
      Points: 268
      Rank: Contributor

      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: 115
        Points: 433
        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: 13
      Replies: 58
      Points: 268
      Rank: Contributor

      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: 115
        Points: 433
        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: 13
      Replies: 58
      Points: 268
      Rank: Contributor

      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: 115
        Points: 433
        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: 115
      Points: 433
      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.
      <p style=”padding-left: 40px;”>”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</p>
      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: 115
      Points: 433
      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
      <p style=”padding-left: 40px;”>–> ChildGroup1</p>
      <p style=”padding-left: 80px;”>-> ChildGroup10
      -> ChildGroup11</p>
      <p style=”padding-left: 40px;”>–> ChildGroup2</p>
      <p style=”padding-left: 80px;”>-> ChildGroup1
      -> ChildGroup3</p>

    • #172757
      Participant
      Topics: 13
      Replies: 58
      Points: 268
      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

      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: 115
      Points: 433
      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: 13
        Replies: 58
        Points: 268
        Rank: Contributor

        Yes that’s correct.

    • #173050
      Participant
      Topics: 0
      Replies: 115
      Points: 433
      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: 13
        Replies: 58
        Points: 268
        Rank: Contributor

        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: 115
      Points: 433
      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.

      <script src=”https://gist.github.com/JasonRobertson/0de9df37e5dd7f3a5679bbcae9c94daf.js”></script&gt;

    • #173269
      Participant
      Topics: 0
      Replies: 115
      Points: 433
      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: 13
      Replies: 58
      Points: 268
      Rank: Contributor

      Thanks Jason. Will try it out.

Viewing 13 reply threads
  • The topic ‘GroupMembership’ is closed to new replies.