0 votes

We have a need to add users who have direct reports to a security group. We're currently using, If the Direct Reports' property is not empty, but this also grabs users who are managers of shared mailboxes.
How can we update our scheduled task to exclude users who are only managers of shared mailboxes? I imagine it has to do with the recipientTypeDetails, but I can't figure out how to implement it.

ago by (20 points)

1 Answer

0 votes
ago by (304k points)

Hello,

There is no possibility to achieve such a thing using built-in functionality. It can only be done using a PowerShell script. Unfortunately, we do not have anything like that in our repository. However, the following SDK article should be helpful: https://adaxes.com/sdk/ServerSideScripting.

0

Yeah, I was afraid Suppoer was going to say that there was nothing prepared for this kind of request, but I did anticipate that answer.

So... I wrote a script:

function Convert-AdsLargeIntegerToInt64($adsLargeInt) {
    return ($adsLargeInt.HighPart * [math]::Pow(2, 32)) + $adsLargeInt.LowPart
}

function IsValidReport($report, $excludedMask) {
    try {
        $rawValue = $report.Get("msExchRecipientTypeDetails")
        $recipientType = Convert-AdsLargeIntegerToInt64 $rawValue
        return (($recipientType -band (-bnot $excludedMask)) -ne 0)
    } catch {
        # If the property is missing, assume it's a valid user
        return $true
    }
}

$dryRun = $true
$excludedMask = 4 + 16 + 32 + 2147483648 + 8589934592 + 17179869184 + 34359738368
$hasValidReports = $false

# === Step 1: Get direct reports ===
$directReports = @()
try {
    $directReports = $Context.TargetObject.GetEx("directReports")
} catch {
    if ($dryRun) {
        $Context.LogMessage("%userPrincipalName% has no direct reports (property not set)", "Information")
    }
}

if ($directReports.Count -eq 0) {
    if ($dryRun) {
        $Context.LogMessage("%userPrincipalName% has no direct reports", "Information")
    }
} else {
    foreach ($reportDN in $directReports) {
        try {
            $report = $Context.BindToObjectByDN($reportDN)
            $reportName = $report.Get("name")

            if ($dryRun) {
                try {
                    $rawValue = $report.Get("msExchRecipientTypeDetails")
                    $recipientType = Convert-AdsLargeIntegerToInt64 $rawValue
                    $Context.LogMessage("Report: $reportName | recipientType: $recipientType", "Information")
                } catch {
                    $Context.LogMessage("Report: $reportName | recipientType: <not set>", "Information")
                }
            }

            if (IsValidReport $report $excludedMask) {
                if ($dryRun) {
                    $Context.LogMessage("Report: $reportName is a valid report (not excluded)", "Information")
                }
                $hasValidReports = $true
                break
            } elseif ($dryRun) {
                $Context.LogMessage("Report: $reportName is excluded", "Information")
            }
        } catch {
            $Context.LogMessage("Failed to bind to report: $reportDN", "Warning")
        }
    }
}

# === Step 2: Group membership management ===
$groupDN = "CN=YourGroupName,OU=Groups,DC=yourdomain,DC=com"
$group = $Context.BindToObjectByDN($groupDN)
$userDN = $Context.TargetObject.Get("distinguishedName")
$userPath = "LDAP://$userDN"
$isMember = $group.IsMember($userPath)

if ($hasValidReports -and -not $isMember) {
    if ($dryRun) {
        $Context.LogMessage("[Dry Run] Would add %userPrincipalName% to the group.", "Information")
    } else {
        $group.Add("Adaxes://$userDN")
        $Context.LogMessage("Added %userPrincipalName% to the group.", "Information")
    }
} elseif (-not $hasValidReports -and $isMember) {
    if ($dryRun) {
        $Context.LogMessage("[Dry Run] Would remove %userPrincipalName% from the group.", "Information")
    } else {
        $group.Remove("Adaxes://$userDN")
        $Context.LogMessage("Removed %userPrincipalName% from the group.", "Information")
    }
}

I would love any feedback on it, and feel free to include it in the Script Repository!

0

Hello,

The script is very overcomplicated and is not using Adaxes functionality at its best. The example is that to get the ADS Path of the parget object you can just use $Context.TargetObject.AdsPath instead of binding and so on. However, you can keep it as is in case the script does what you need.

0

I really appreciate the feedback! Yes, it had some complexity, due to the $dryRun option I had in it. I wanted to log exactly what was happening as I was developing and testing it without making any actual changes.

I followed your recommendations with binding, including them only where necessary, and I removed the $dryRun option.

# === Static Configuration ===

# Group to manage (static path)
$groupPath = "Adaxes://CN=YourGroupName,OU=Groups,DC=yourdomain,DC=com"

# Bitmask of excluded recipient types (shared, room, equipment, remote variants)
$excludedMask = 4 + 16 + 32 + 2147483648 + 8589934592 + 17179869184 + 34359738368

# === Initialization ===

$hasValidReports = $false

# === Step 1: Get direct reports ===

$directReports = @()
try {
    $directReports = $Context.TargetObject.GetEx("directReports")
} catch {
    # No directReports property — treat as no valid reports
}

# === Step 2: Evaluate direct reports ===

foreach ($reportDN in $directReports)
{
    try {
        $report = $Context.BindToObjectByDN($reportDN)

        try {
            $recipientType = [int64]$report.Get("msExchRecipientTypeDetails")
        } catch {
            # If the property is missing, assume it's a valid user
            $hasValidReports = $true
            break
        }

        if (($recipientType -band (-bnot $excludedMask)) -ne 0)
        {
            $hasValidReports = $true
            break
        }
    } catch {
        $Context.LogMessage("Failed to bind to report: $reportDN", "Warning")
    }
}

# === Step 3: Group membership management ===

$group = $Context.BindToObject($groupPath)
$userPath = $Context.TargetObject.AdsPath
$isMember = $group.IsMember($userPath)

if ($hasValidReports -and -not $isMember) {
    $group.Add($userPath)
    $Context.LogMessage("Added %userPrincipalName% to the group.", "Information")
}
elseif (-not $hasValidReports -and $isMember) {
    $group.Remove($userPath)
    $Context.LogMessage("Removed %userPrincipalName% from the group.", "Information")
}

I hope this cleaner code is easier to understand and that it will be helpful to others.

Related questions

0 votes
1 answer

Ideally looking to make this a rule based group, but report or business unit should work also. In our domain, service accounts become direct reports of the user who requested/ ... if a specific object is a/not a direct report. Is this possible? Thank you

asked Nov 14, 2023 by ThompsonAlex (40 points)
0 votes
0 answers

Good Afternoon, I'm looking for some clarification on what security settings I would need to apply to the Self-Service Users to allow them to update both their own ... accounts they have full access to. Please let me know if this requires more clarification.

asked Jul 22, 2021 by jtop (700 points)
0 votes
1 answer

Hi, is there a way through the Adaxes SDK to check all users against a specific group and determine wether or not they are part of it (must include direct and indirect ... Forrest root domain which holds the group, members of the group can be in every domain

asked Jun 30, 2014 by ijacob (960 points)
0 votes
1 answer

Hi, Is there a way I can create a rule based group or scheduled task in which the Direct reports of the direct reports are added to a group? So for example: CEO VP's ... in the list that no longer reports to a manager who reports to the CEO. Thanks in advance

asked Dec 22, 2022 by gareth.aylward (180 points)
0 votes
1 answer

I'm trying to automate adding users who are enrolled in MFA to an AD group. The scripts I found elsewhere here that do not work so I believe they may have been written against a prior Adaxes version or referencing a report that does not meet our needs.

asked May 31, 2024 by neal (50 points)
3,717 questions
3,397 answers
8,592 comments
549,963 users