Recently, one of my customers has asked me to write a simple solution, which would assist him with recursive retrieval of group memberships of every AD and local built-in groups in his domain and local machine.
Thus the solution should have listed every group to which his account directly belonged to as well as all parent groups chained to it. In addition, the solution should have retrieved and show the owner of each group.
Typical use casesUse case 1. Put user1 is a direct member of group1. group1 is a direct member of group 2, group2 is a direct member of group3. So the full chain reads as user1 > group1 > group2 > group3. The logic should return group1, group2, group3 for user1.
Use case 2. Put user1 is a direct member of group1. group1 is a direct member of group 2. group2 also includes group3 as a member, however, group3 does not contain user1. So the full chain reads as user1 > group1 > group2. And group 3 > group 2. In this case, the logic should return only group1 and group2 for user1.
Powershell-based solutionThe complete source code of the solution is given below.
The solution runs in Powershell version 2.0 or higher. It employs the standard cmdlets of Powershell module "ActiveDirectory" for Windows. Details on a quick installation of this module to various editions of Windows will be given in the next chapter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | param( [string]$userName = [System.Environment]::UserName # Valid formats: ID, domain\ID, ID@domain, ID@domain.com ,[bool]$useExtendedOutput = $false ) ######################################################## FUNCTIONS ######################################################### function Get-ADGroupRecursiveParentChain() { param( [string]$distinguishedName # Sample value: CN=TestGroupGlobal,CN=Users,DC=vanilla,DC=cgi,DC=com ,[array]$allGroups ) $group = Get-ADGroup -Identity $distinguishedName -Property MemberOf,ManagedBy if( !$? ) { throw } $tmp = ($allGroups |? {$_.distinguishedName -ieq $group.distinguishedName}) if( $tmp -eq $null -or $tmp.Length -eq 0 ) { $allGroups += $group } $group.MemberOf | % { $nestedGroup = Get-ADGroup -Identity $_ -Property MemberOf,ManagedBy if( !$? ) { throw } $tmp2 = ($allGroups |? {$_.distinguishedName -ieq $nestedGroup.distinguishedName}) if( $tmp2 -eq $null -or $tmp2.Length -eq 0 ) { $allGroups += $nestedGroup } $allGroups = Get-ADGroupRecursiveParentChain -distinguishedName $nestedGroup.distinguishedName -allGroups $allGroups } return $allGroups } ####################################################### //FUNCTIONS ######################################################## ####################################################### EXECUTION ########################################################## # Step 0. Perform validations to confirm the standard Windows Active Directory Powershell module has been installed. try { Import-Module ActiveDirectory Get-ADDomain | out-null } catch{ write-host "The required Windows Powershell module 'ActiveDirectory' is not installed on this machine." ` -ForegroundColor Magenta write-host "Use the following link and/or commands to install it and then execute this script again." ` -ForegroundColor Magenta write-host write-host "https://4sysops.com/archives/how-to-install-the-powershell-active-directory-module" write-host write-host "Import-Module ActiveDirectory" write-host $message = "Alternatively, you can open a remote Powershell session to your server, which has" + ` " 'ActiveDirectory' module installed. Refer to instructions in the file readme.txt in the same directory for details." write-host $message -ForegroundColor Magenta write-host return } # Step 1. Parse value of $userName to extract username and domain into separate parts $accountName = $null $domain = $null $match = [regex]::Match($userName, "([^\\]+)\\(.+)") if( $match.Success ) { $domain = $match.Groups[1].Value $accountName = $match.Groups[2].Value } else { $match = [regex]::Match($userName, "([^@]+)@(.+)") if( $match.Success ) { $accountName = $match.Groups[1].Value $domain = $match.Groups[2].Value } else { $accountName = $userName } } # Step 2. Extract first level groups where the user is a direct member of. $user = $null $explicitGroups = $null if( $domain ) { $user = Get-ADUser -Identity $accountName -Server $domain if( !$? ) { # Error, user not found. return } $explicitGroups = Get-ADPrincipalGroupMembership -Identity $accountName -Server $domain if( !$? ) { # Error, explicit groups not found. return } } else { $user = Get-ADUser -Identity $accountName if( !$? ) { # Error, user not found. return } $explicitGroups = Get-ADPrincipalGroupMembership -Identity $accountName if( !$? ) { # Error, explicit groups not found. return } } # Step 3. Recursively extract groups of deeper levels where the first level groups are either direct or nested members of. # Example 1. Put user1 is a direct member of group1. group1 is a direct member of group 2, group2 is a direct member of group3. # So the full chain reads as user1 > group1 > group2 > group3. --> The logic should return group1, group2, group3 for user1. # Example 2. Put user1 is a direct member of group1. group1 is a direct member of group 2. group2 also includes group3 as a member. # group3 does not contain user1 # So the full chain reads as user1 > group1 > group2. group 3 > group 2 --> The logic should return only group1 and group2 for user1. $allGroups = @() $explicitGroups | % { $allGroups = Get-ADGroupRecursiveParentChain -distinguishedName $_.distinguishedName -allGroups $allGroups if( !$? ) { # Error, nested groups not found. return } } # Step 4. Show the current user write-host $message = "UserID: " + $user.SamAccountName + " | Distinguished name: " + $user.distinguishedName write-host $message # Step 5. Enlist all groups where the user is either a direct member of or an indirect member resolved via nested group memberships. # Example of simple-to-read view if( $useExtendedOutput ) { $allGroups | sort -Property name ` | ft -wrap ` @{n="Group short name";e={$_.name}}, @{n="Group distinguished name";e={$_.distinguishedName + [System.Environment]::NewLine}}, @{n="Owner";e={(Get-ADObject -Identity $_ -Property nTSecurityDescriptor ` | select -ExpandProperty nTSecurityDescriptor).Owner}}, @{n="Manager";e={if($_.ManagedBy){(Get-ADObject -Identity $_.ManagedBy).name}}} # Example of more generic view #$allGroups | sort -Property name ` # | ft -wrap ` # Name, # #DistinguishedName, # @{n="Owner";e={if($_){(Get-ADObject -Identity $_ -Property nTSecurityDescriptor | select -ExpandProperty nTSecurityDescriptor).Owner}}}, # ManagedBy write-host "In total: $($allGroups.Count) groups" write-host } else { $allGroups | sort -Property name ` | ft -auto -wrap ` @{n="Group short name";e={$_.name}}, @{n="Owner";e={(Get-ADObject -Identity $_ -Property nTSecurityDescriptor ` | select -ExpandProperty nTSecurityDescriptor).Owner}}, @{n="Manager";e={if($_.ManagedBy){(Get-ADObject -Identity $_.ManagedBy).name}}} # Example of more generic view #$allGroups | sort -Property name ` # | ft -auto -wrap ` # Name, # @{n="Owner";e={if($_){(Get-ADObject -Identity $_ -Property nTSecurityDescriptor | select -ExpandProperty nTSecurityDescriptor).Owner}}}, # ManagedBy write-host "Use an extra parameter -useExtendedOutput `$true to see the extended information" write-host } |
How to run this script on various Windows versions.
1. Copy the script given above and save it to some file, for example, c:\tmp\EnlistUserGroupMembershipsRecursively.ps1. The script has been tested in Powershell 2.0 and higher. In order to run the script successfully, you need to install and enable the standard Active Directory module for Windows Powershell that allows you to work with Active Directory objects using Powershell cmdlets.
- Detailed instructions how to do it can be found here: https://4sysops.com/archives/how-to-install-the-powershell-active-directory-module - There are chapters that describe module's installation on particular operating systems like Windows 7 / 8, Windows 2008 / 2012 servers. - After you have installed Active Directory Powershell module, remember to execute the following command: Import-Module ActiveDirectory
2. If you may not install anything on your local machine, you can use the alternative option to connect remotely to a server,
which already has Active Directory module Powershell installed and also has connection to your Active Directory Domain Controller.
- First of all, you need to enable Powershell remoting by executing the following command on your server and confirming operations.
It should create correct Windows Firewall openings to grant access for remote WinRM operations.
Enable-PSRemoting
- More details can be found here: https://blogs.technet.microsoft.com/heyscriptingguy/2012/12/30/understanding-powershell-remote-management/
- Next, you need to enter your remote Powershell session on the server from your local machine. Execute the following commands.
Enter-PSSession -ComputerName
3. After completion of step 1 above (or alternative step 2), execute the script EnlistUserGroupsRecursively.ps1 with optional parameter -userName, for example:
EnlistUserGroupsRecursively.ps1 # This call uses domain name of the current user. OR EnlistUserGroupsRecursively.ps1 -userName ID # This works for the current domain and uses the default DC. OR EnlistUserGroupsRecursively.ps1 -userName domain\ID OR EnlistUserGroupsRecursively.ps1 -userName ID@domain.com
4. Active directory groups do not have a preset special entity like "Owner". There is only an Owner of a securable AD object (AD group). In addition, each AD group may also have a Manager set via "Managed By" tab of Group Properties. In practice, sometimes namely this Manager is considered as an Owner. - An example of where to set a Group Manager: Open Start > Administrative Tools > Active Directory Users and Computers > Your Domain > Users > Double click on a desired group > Properties > Managed By
A sample output of this script is shown in the screenshot below.