The script creates an LDAP filter that allows finding all subordinates of the user on which the script is executed and saves it to a certain attribute of the user account. The filter will include direct reports of the user, subordinates of the user's direct reports, and users managed by the user via membership in AD groups.
Note: Employee-manager relationships are established in Active Directory via the Manager property of a user's account.
Parameter:
-
$propertyForFilter - Specifies the LDAP display name of the property that will be used to store the LDAP filter.
To create the LDAP filter for any user on demand, create a custom command that can be executed on User objects and execute it on the users you need. To keep up with changes in management inside your Active Directory, create a scheduled task to update the filters of managers on a regular basis.
Import-Module Adaxes
$propertyForFilter = "adm-CustomAttributeText1" # TODO: modify me
function GetAllSubordinates($directReportDN, $subordinateDNs)
{
if($subordinateDNs.Contains($directReportDN))
{
return
}
[void]$subordinateDNs.Add($directReportDN)
# Bind to the direct report
$user = $Context.BindToObjectByDN($directReportDN)
# Get subordinates
try
{
$directReportDNs = $user.GetEx("directReports")
}
catch
{
return
}
foreach ($directReportDN in $directReportDNs)
{
GetAllSubordinates $directReportDN $subordinateDNs
}
}
function SearchObjects($filter, $properties)
{
$searcher = $Context.BindToObject("Adaxes://rootDSE")
$searcher.SearchFilter = $filter
$searcher.SearchScope = "ADS_SCOPE_SUBTREE"
$searcher.PageSize = 500
$searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
$searcher.SetPropertiesToLoad($properties)
$searcher.VirtualRoot = $True
try
{
$searchResultIterator = $searcher.ExecuteSearch()
$searchResults = $searchResultIterator.FetchAll()
return ,$searchResults
}
finally
{
# Release resources
if ($searchResultIterator){ $searchResultIterator.Dispose() }
}
}
# Get DNs of the groups that the user is a direct or indirect member of
$domainName = $Context.GetObjectDomain("%distinguishedName%")
$groups = Get-AdmPrincipalGroupMembership -Identity "%distinguishedName%" -Server $domainName `
-Recursive -AdaxesService localhost
$managerDNs = New-Object "System.Collections.Generic.HashSet[System.String]"
foreach ($group in $groups)
{
$managerDNs.Add($group.distinguishedName)
}
# Add the user DN to the hash set
[void]$managerDNs.Add("%distinguishedName%")
# Add all subordinate DNs to the filter
$subordinateDNs = New-Object "System.Collections.Generic.HashSet[System.String]"
foreach ($managerDN in $managerDNs)
{
$manager = $Context.BindToObjectByDN($managerDN)
# Get subordinates
try
{
$directReportDNs = $manager.GetEx("directReports")
}
catch
{
continue # No subordinates
}
foreach ($directReportDN in $directReportDNs)
{
GetAllSubordinates $directReportDN $subordinateDNs
}
}
# Build filter
$subordinateFilter = New-Object "System.Text.StringBuilder"
foreach ($dn in $subordinateDNs)
{
[void]$subordinateFilter.Append([Softerra.Adaxes.Ldap.FilterBuilder]::Create("distinguishedName", $dn))
}
$filter = New-Object "System.Text.StringBuilder"
if ($subordinateFilter.Length -ne 0)
{
$searchResults = SearchObjects "(|$($subordinateFilter.ToString()))" @("ObjectGuid")
$filter.Append("(&(objectCategory=person)(objectClass=user)(|")
foreach ($searchResult in $searchResults)
{
[void]$filter.Append([Softerra.Adaxes.Ldap.FilterBuilder]::Create("ObjectGuid", [Guid]$searchResult.Properties["ObjectGuid"].Value))
}
[void]$filter.Append("))")
}
else
{
[void]$filter.Append("(objectGuid=\00)")
}
# Save filter to the property specified
$Context.TargetObject.Put($propertyForFilter, $filter.ToString())
$Context.TargetObject.SetInfo()
The script can be used to build a business unit that will present each user with a list of all their subordinates. For information on how to create such a business unit, see Example 3 under Query Results in Group AD Objects Based on Logged In User.
For the LDAP filter, specify a value reference for the property that you used in $propertyForFilter. For example, if you specified adm-CustomAttributeText1, use the following value reference: %adm-CustomAttributeText1%.
I wonder if it would be possible to change the filter to use ObjectSID instead of DN ?
If so, updating the filter would not be nessacary when users change name, change OU or OU change name.
Cheers
Actually, the filter uses GUIDs of the subordinates and there is no need to update the filter when users change name, OU etc. However, the filter has to be updated when the manager's subordinates list was changed.