Public/Get-GroupWritebackReconciliationOperations.ps1

function Get-GroupWritebackReconciliationOperations {
    [CmdletBinding()]

    Param(
        [Parameter(Mandatory = $false)]
        [Switch] $DoNotWarnIfMissingOnPremDN
    )

    Process {
        $ADGroups = Get-ADGroup -Filter $Script:ADGroupFilter -Properties member, adminDescription

        if (!$ADGroups) {
            Write-Error "No on-premises AD groups matching the filter."
            return @()
        }
        
        foreach ($ADGroup in $ADGroups) {
            Write-Verbose "Processing group '$($ADGroup.Name)' with objectguid '$($ADGroup.ObjectGUID)'."
            
            if ($ADGroup.adminDescription -notmatch "^takenover_[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") {
                Write-Warning "Group '$($ADGroup.Name)' does not have a valid adminDescription. Skipping group."
                continue
            }

            $EntraIDGroupObjectId = $ADGroup.adminDescription -replace "^takenover_"

            Write-Verbose " - Fetching Entra ID group members for group '$EntraIDGroupObjectId'."
            $EntraIDMembers = @{}
            $uri = "https://graph.microsoft.com/v1.0/groups/$EntraIDGroupObjectId/members?`$select=id,onPremisesDistinguishedName&`$top=999"
            
            try {
                do {
                    $Response = Invoke-RestMethod -Uri $Uri -Method Get -Headers (Get-EntraIDAccessTokenHeader -Profile $Script:AccessTokenProfile)
                    if ($Response.value) {
                        foreach ($Member in $Response.value) {
                            if ($Member.onPremisesDistinguishedName) {
                                $EntraIDMembers[$Member.onPremisesDistinguishedName] = $Member
                            }
                            else {
                                if (!$DoNotWarnIfMissingOnPremDN.IsPresent) {
                                    Write-Warning "Member with ID '$($Member.id)' in group '$EntraIDGroupObjectId' does not have an onPremisesDistinguishedName. Skipping member."
                                }
                            }                        
                        }
                    }
                    $Uri = $Response.'@odata.nextLink'
                } while ($Uri)

                Write-Verbose " - Found $($EntraIDMembers.Count) members in Entra ID group '$EntraIDGroupObjectId'."
            }
            catch {
                Write-Error "Failed to fetch members for Entra ID group '$EntraIDGroupObjectId'. Error details: $($_.Exception.Message)"
                continue
            }

            # Compare members
            $ADGroupMemberMap = @{}
            if($ADGroup.member) {
                $ADGroupMemberMap = $ADGroup.member | Group-Object -AsHashTable -AsString
            }

            # Find members in Entra that are not in AD
            if($EntraIDMembers) {
                $EntraIDMembers.Keys | 
                Where-Object { -not $ADGroupMemberMap.ContainsKey($_) } |
                ForEach-Object {
                    New-GroupWritebackReconciliationOperation -Action "Add member" -Group $ADGroup.DistinguishedName -Member $_
                }
            } else {
                Write-Warning "No members found in Entra ID group '$EntraIDGroupObjectId'."
            }

            # Find members in AD that are not in Entra (and should be removed)
            if ($ADGroupMemberMap) {
                $ADGroupMemberMap.Keys |
                Where-Object { -not $EntraIDMembers.ContainsKey($_) } |
                ForEach-Object {
                    New-GroupWritebackReconciliationOperation -Action "Remove member" -Group $ADGroup.DistinguishedName -Member $_
                }
            }
        }
    }
}