Script Repository


Copy group membership

January 31, 2018
1988

The below scripts can be used to create Custom Commands for copying group membership from one AD object to another. One of the commands can be executed on an AD object to copy its group memberships. The other one can be executed on another object to paste the copied memberships.

For more information on creating Custom Commands, see Create a Custom Command. Both the commands must be configured for the object type you want to copy group membership for.

To add scripts to Custom Commands, use the Run a program or PowerShell script action.

To pass group memberships between AD objects, the 1st Custom Command saves group memberships of the source object to the CustomAttributeBinary1 property of the user who invokes the Custom Command. The 2nd command reads the group memberships from the same property and adjusts the group memberships of the destination object. Each group an object is a member of is represented as an array of 16 bytes (Byte[]) representing the GUID of the group. The property will store an array of arrays of bytes.

CustomAttributeBinary1 is one of Adaxes virtual properties for binary data. Such properties are not stored in Active Directory, but you can use them the same as any other properties of AD objects.

Script for copying group memberships:

Edit Remove
PowerShell
# Get GUIDs of the groups the object is a member of.
$sourceGroupGuids = $Context.TargetObject.GetEx("adm-DirectMemberOfGuid") 

# Build a single-dimension array with group member GUIDs
$totalBytes = $sourceGroupGuids.Count * 16
$result = New-Object 'System.Collections.Generic.List[System.Byte]' $totalBytes
foreach ($groupGuidBytes in $sourceGroupGuids)
{
    $result.AddRange($groupGuidBytes)
}

# Save the GUIDs to the adm-CustomAttributeBinary1 virtual property user who invokes the script
$Context.Initiator.UserAdsObject.Put("adm-CustomAttributeBinary1", $result.ToArray())
$Context.Initiator.UserAdsObject.SetInfo()

Script for pasting group memberships:

This version of the script will copy new memberships to the destination object, but will not remove the user from the groups that the source object is not a member of.

Edit Remove
PowerShell
# Get an array of group GUIDs
try
{
    $sourceGroupGuids = $Context.Initiator.UserAdsObject.Get("adm-CustomAttributeBinary1")
}
catch
{
    $Context.Cancel("Failed to get group GUIDs.")
    return
}

# Calculate the number of GUIDs
$totalBytes = $sourceGroupGuids.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!")
    return
}

$groupsToAdd = New-Object "System.Collections.Generic.HashSet[System.Guid]"

for ($i = 0; $i -lt ($totalBytes / 16); $i++)
{
    $bytes = [System.Guid]::Empty.ToByteArray()
    [System.Array]::Copy($sourceGroupGuids, $i * 16, $bytes, 0, 16)
    $guid = New-Object "System.Guid" (,$bytes)
    $groupsToAdd.Add($guid)
}

# Get GUIDs of the groups the user is a member of
$memberOfGuids = $Context.TargetObject.GetEx("adm-DirectMemberOfGuid")

# Adjust the list of groups to add the user to
foreach($memberOfGuidBytes in $memberOfGuids)
{
    $guid = New-Object "System.Guid" (,$memberOfGuidBytes)
    if (-not($groupsToAdd.Contains($guid)))
    {
        continue
    }
    
    $groupsToAdd.Remove($guid) # Already a member of the group
}

# Add to groups
$failedToAdd = ""
$successfullyAdded = ""
foreach($groupGuid in $groupsToAdd)
{
    $groupGuid = $groupGuid.ToString("B")
    $groupPath = "Adaxes://<GUID=$groupGuid>"
    $groupName = $Context.GetDisplayNameFromAdsPath($groupPath)
    try
    {
        $group = $Context.BindToObjectEx($groupPath, $True)
        $group.Add($Context.TargetObject.AdsPath)
    }
    catch
    {
        $failedToAdd += "$groupName; "
        continue
    }
    
    $successfullyAdded += "$groupName; "
}

if ($successfullyAdded.Length -ne 0)
{
    $Context.LogMessage("The object has been added to the following groups: $successfullyAdded", "Information") # TODO: modify me
}
if ($failedToAdd.Length -ne 0)
{
    $Context.LogMessage("The object has not been added to the following groups: $failedToAdd", "Information") # TODO: modify me
}

This version of the script will make the group memberships of the destination object exactly the same as the memberships of the source object.

Edit Remove
PowerShell
# Get an array of group GUIDs
try
{
    $sourceGroupGuids = $Context.Initiator.UserAdsObject.Get("adm-CustomAttributeBinary1")
}
catch
{
    $Context.Cancel("Failed to get group GUIDs.")
    return
}

# Calculate the number of GUIDs
$totalBytes = $sourceGroupGuids.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
}

$groupsToAdd = New-Object "System.Collections.Generic.HashSet[System.Guid]"

for ($i = 0; $i -lt ($totalBytes / 16); $i++)
{
    $bytes = [System.Guid]::Empty.ToByteArray()
    [System.Array]::Copy($sourceGroupGuids, $i * 16, $bytes, 0, 16)
    $guid = New-Object "System.Guid" (,$bytes)
    $groupsToAdd.Add($guid)
}

# Get GUIDs of the groups the object is a member of
$memberOfGuids = $Context.TargetObject.GetEx("adm-DirectMemberOfGuid")

# Adjust the list of groups to add or remove the object from
$groupsToRemove = New-Object "System.Collections.Generic.HashSet[System.Guid]"

foreach($memberOfGuidBytes in $memberOfGuids)
{
    $guid = New-Object "System.Guid" (,$memberOfGuidBytes)
    if ($groupsToAdd.Contains($guid))
    {
        $groupsToAdd.Remove($guid) # Already a member of the group
    }
    else
    {
        $groupsToRemove.Add($guid)
    }
}

# Remove from groups that are not in the list of copied groups
$failedToRemove = ""
$removedSuccessfully = ""
foreach($groupGuid in $groupsToRemove)
{
    $groupGuid = $groupGuid.ToString("B")
    $groupPath = "Adaxes://<GUID=$groupGuid>"
    try
    {
        $group = $Context.BindToObjectEx($groupPath, $True)
        $group.Remove($Context.TargetObject.AdsPath)
    }
    catch
    {
        $group = $Context.BindToObject($groupPath)
        $failedToRemove += $group.Get("Name") + "; "
        continue
    }
    
    $removedSuccessfully += $group.Get("Name") + "; "
}

# Add to new groups
$failedToAdd = ""
$addedSuccessfully = ""
foreach($groupGuid in $groupsToAdd)
{
    $groupGuid = $groupGuid.ToString("B")
    $groupPath = "Adaxes://<GUID=$groupGuid>"
    try
    {
        $group = $Context.BindToObjectEx($groupPath, $True)
        $group.Add($Context.TargetObject.AdsPath)
    }
    catch
    {
        $group = $Context.BindToObject($groupPath)
        $failedToAdd += $group.Get("Name") + "; "
        continue
    }
    
    $addedSuccessfully += $group.Get("Name") + "; "
}

if ($addedSuccessfully.Length -ne 0)
{
    $Context.LogMessage("The object has been added to the following groups: $addedSuccessfully", "Information") # TODO: modify me
}
if ($failedToAdd.Length -ne 0)
{
    $Context.LogMessage("The object has not been added to the following groups: $failedToAdd", "Information") # TODO: modify me
}
if ($removedSuccessfully.Length -ne 0)
{
    $Context.LogMessage("The object has been removed from the following groups: $removedSuccessfully", "Information") # TODO: modify me
}
if ($failedToRemove.Length -ne 0)
{
    $Context.LogMessage("The object has not been removed from the following groups because you do not have sufficient permissions: $failedToRemove", "Information") # TODO: modify me
}


Comments ( 0 )
No results found.
Leave a comment

Related Scripts