Script Repository


Changes in group membership (including changes made by 3rd party tools)

November 23, 2021
2797

The script sends a report on changes of membership in a group, no matter whether the changes were made using Adaxes or any 3rd party tools, such as ADUC or Exchange.

To identify the members added or removed from a group, on each run the script preserves GUIDs of the current members in a binary attribute (e.g. adm-CustomAttributeBinary1) of the group. On each subsequent run, the saved GUIDs are used to compare the list of current members of the group with the members preserved in the binary attribute.

To execute the script, create a scheduled task configured for the Group object type.

Parameters:

  • $savedMembersAttribute - Specifies the LDAP name of the binary attribute used to preserve group member GUIDs.
  • $to - Specifies the recipient of the email notification.
  • $subject - Specifies the email message subject.
  • $reportHeader - Specifies the report header.
  • $reportFooter - Specifies the report footer.
  • $headerAddedMembers - Specifies a header for the section with added members.
  • $headerRemovedMembers - Specifies a header for the section with removed members.
Edit Remove
PowerShell
$savedMembersAttribute = "adm-CustomAttributeBinary1" # TODO: modify me

# E-mail message settings
$to = "recipient@domain.com" # TODO: modify me
$subject = "Changes in group membership for group '%name%'" # TODO: modify me
$reportHeader = "<h2><b>Changes in group membership for group '%name%'</b></h2><br/>" # TODO: modify me
$reportFooter = "<hr /><p><i>Please do not reply to this e-mail, it has been sent to you for notification purposes only.</i></p>" # TODO: modify me
$headerAddedMembers = "<b>Members added to the group</b><br />" # TODO: modify me
$headerRemovedMembers = "<b>Members removed from the group</b><br />" # TODO: modify me

function SaveCurrentMembers($guidsBytes, $savedMembersAttribute)
{
    if ($guidsBytes.Count -eq 0)
    {
        # All members were removed from the group
        $Context.TargetObject.Put($savedMembersAttribute, [Guid]::Empty.ToByteArray())
    }
    else
    {
        $totalBytes = $guidsBytes.Count * 16
        $result = New-Object 'System.Collections.Generic.List[System.Byte]' $totalBytes
        
        foreach ($guidBytes in $guidsBytes)
        {
            $result.AddRange($guidBytes)
        }
        
        $Context.TargetObject.Put($savedMembersAttribute, $result.ToArray())
    }
    
    # Save changes
    $Context.TargetObject.SetInfo()
}

# Get GUIDs of direct members of the group
try
{
    $currentMemberGuidsBytes = $Context.TargetObject.GetEx("adm-DirectMembersGuid")
}
catch
{
    $currentMemberGuidsBytes = @()
}

$addedMemberGuids = New-Object "System.Collections.Generic.HashSet[System.Guid]"
foreach ($guidBytes in $currentMemberGuidsBytes)
{
    $guid = [Guid]$guidBytes
    $addedMemberGuids.Add($guid)
}

# Get saved member GUIDs
try
{
    $savedMemberGuidsBytes = $Context.TargetObject.Get($savedMembersAttribute)
}
catch
{
    if ($addedMemberGuids.Count -eq 0)
    {
        return # No current or saved members
    }
    
    # Save current members GUIDs and exit
    SaveCurrentMembers $currentMemberGuidsBytes $savedMembersAttribute
    return
}

if (($savedMemberGuidsBytes.Length -eq 16) -and ([Guid]$savedMemberGuidsBytes -eq [Guid]::Empty))
{
    $savedMemberGuidsBytes = @() # All users were removed from the group previous time
}

$savedMemberGuids = New-Object "System.Collections.Generic.HashSet[System.Guid]"
if ($savedMemberGuidsBytes.Length -ne 0)
{
    # Calculate the number of GUIDs
    $totalBytes = $savedMemberGuidsBytes.Length
    
    # Make sure that the total number of  bytes is a divisible of 16
    $remainder = 0
    [System.Math]::DivRem($totalBytes, 16, [ref]$remainder)
    if ($remainder -ne 0)
    {
        $Context.Cancel("Unexpected data length! Exiting.")
        return
    }
    
    for ($i = 0; $i -lt ($totalBytes / 16); $i++)
    {
        $bytes = [System.Guid]::Empty.ToByteArray()
        [System.Array]::Copy($savedMemberGuidsBytes, $i * 16, $bytes, 0, 16)
        $guid = [Guid]$bytes
        [void]$savedMemberGuids.Add($guid)
    }
}

# Find members that were removed from the group
$removedMemberGuids = New-Object "System.Collections.Generic.HashSet[System.Guid]"
foreach ($guid in $savedMemberGuids)
{
    if ($addedMemberGuids.Remove($guid))
    {
        continue
    }
    
    $removedMemberGuids.Add($guid)
}

if (($removedMemberGuids.Count -eq 0) -and ($addedMemberGuids.Count -eq 0))
{
    return # No changes
}

# Get the default Web Interface address
$webInterfaceAddress = "%adm-WebInterfaceUrl%"
if ([System.String]::IsNullOrEmpty($webInterfaceAddress))
{
    $Context.LogMessage("Default web interface address not set for Adaxes service. For details, see http://www.adaxes.com/help/?HowDoI.ManageService.RegisterWebInterface.html", "Warning")
}

if ($addedMemberGuids.Count -ne 0)
{
    # Add new members to the report
    foreach ($newMemberGuid in $addedMemberGuids)
    {
        # Bind to the member
        $path = "Adaxes://<GUID=$newMemberGuid>"
        
        # Get member name
        $memberName = [Softerra.Adaxes.Utils.ObjectNameHelper]::GetObjectName($path, 'IncludeParentPath')
        $memberName = [System.Web.HttpUtility]::HtmlEncode($memberName)
        
        # Add to the report
        $addedMembersReport += "<li><a href='$webInterfaceAddress`ViewObject.aspx?guid=$newMemberGuid'>$memberName</a></li>"
    }
    $addedMembersReport += "</ol><br/>"
    
    # Add to the report
    $reportHeader += $headerAddedMembers
    $reportHeader += $addedMembersReport
}

if ($removedMemberGuids.Count -ne 0)
{
    # Iterate through removed members
    foreach ($removedMemberGuid in $removedMemberGuids)
    {
        # Bind to the member
        $path = "Adaxes://<GUID=$removedMemberGuid>"
        
        # Get member name
        $memberName = [Softerra.Adaxes.Utils.ObjectNameHelper]::GetObjectName($path, 'IncludeParentPath')
        $memberName = [System.Web.HttpUtility]::HtmlEncode($memberName)
        
        # Add to report
        $removedMembersReport += "<li><a href='$webInterfaceAddress`ViewObject.aspx?guid=$removedMemberGuid'>$memberName</a></li>"
    }
    $removedMembersReport += "</ol>"
    
    # Add to the report
    $reportHeader += $headerRemovedMembers
    $reportHeader += $removedMembersReport
}

# Send mail
$report = $reportHeader + $reportFooter
$Context.SendMail($to, $subject, $NULL, $report)
    
# Save current member GUIDs to custom attribute
SaveCurrentMembers $currentMemberGuidsBytes $savedMembersAttribute

Comments ( 0 )
No results found.
Leave a comment

Related Scripts