Use PowerShell ADSI to Migrate AD Group Members

Other Posts in this Series:

UPDATE: I’ve updated this script because LDAP does not return all Active Directory group members if there are more than 1500 members in the group.

This post provides a function which enables us to use PowerShell ADSI to migrate AD group members. We can also specify whether to copy or move the group members.

function  Get-ADGroupMembers {
    param ([string]$adgroup)
      
    $searcher=[adsisearcher]""
    $searcher.Filter="(&(objectClass=group)(cn=$adgroup))"
    $searcher.PageSize=200
    
    #find group   
    $result=$searcher.FindOne()

    #if group found
    if ($result) {
                 
        #if range exists because group is large
        $moreThan1500Members = [bool]($result.Properties.PropertyNames | where { $_ -like "member;range=*" })

        #if group has more than 1500 members
        if($moreThan1500Members) {     
   
            #we need to recurse through ranges
            $iteratedAllRanges=$false
            $rangeBottom =0
            $rangeTop= 0
    
            while (!($iteratedAllRanges)) {

               $rangeTop=$rangeBottom + 1499

               #set new range
               $memberRange="member;range=$rangeBottom-$rangeTop"
               $searcher.PropertiesToLoad.Clear()
               [void]$searcher.PropertiesToLoad.Add("$memberRange")
               
               try {

                    #if range invalid, throw exception
                    $result = $searcher.FindOne()
                    $rangedProperty = $result.Properties.PropertyNames -like "member;range=*"
                    $members +=$result.Properties.item($rangedProperty)

                    if ($members.count -eq 0) { $iteratedAllRanges=$true }

               } catch {
                    $iteratedAllRanges=$true
               }

               #increment bottom of range for next iteration
               $rangeBottom+=1500

            }
        } else {
            #group member count is less than 1500
            $members += $result.properties.item("member")
        }

        $searcher.Dispose()
        return $members

    }

    return $false   
}


function Migrate-ADGroup
{
    Param
    (
       [string]$sourceDN, 
       [string]$targetDN,
       [bool]$move
    )
   
   if (!([adsi]::Exists("LDAP://$sourceDN"))) {
        write-host "$sourceDN does not exist"
        return     
   }
 
   if (!([adsi]::Exists("LDAP://$targetDN"))) {
        write-host "$targetDN does not exist"
        return
   }

    $sourceDNADSI = [ADSI]"LDAP://$sourceDN"
    $targetDNADSI = [ADSI]"LDAP://$targetDN"

    try {
        $count = 0;

        Get-ADGroupMembers ($sourceDNADSI.Name) | ForEach-Object {
    
            $count++
            $groupObject = [adsisearcher]"(distinguishedname=$($_))"   

            if ($move) {
                write-host "Moving $($groupObject.FindOne().Properties.name)"
                try { $targetDNADSI.Add("LDAP://$_") } catch {}
                try { $sourceDNADSI.Remove("LDAP://$_") } catch {}
            } else {
                write-host "Copying $($groupObject.FindOne().Properties.name)"
                try { $targetDNADSI.Add("LDAP://$_") } catch {}
            }
        }
        write-host "Processesed $count objects"

    } catch {
        write-host $_.Exception.Message
    }
}

$sourcegroup = "CN=application1,OU=Apps,DC=alkanesolutions,DC=co,DC=uk"
$targetgroup = "CN=application2,OU=Apps,DC=alkanesolutions,DC=co,DC=uk"

#source group to migrate from, target group to migrate to, false (copy members) or true (move members)
Migrate-ADGroup $sourcegroup $targetgroup $false