JeshuaEdgar / gal-sync-graph

GAL Sync for Graph. Inspired by grahamr975/EWS-Office365-Contact-Sync, just rewritten to be compatible with Microsoft's Graph API
GNU General Public License v3.0
12 stars 4 forks source link

GAL Update #11

Open purmarili opened 7 months ago

purmarili commented 7 months ago

Hi, Is it possible to create/detele/edit mail-enabled external contacts from GAL?

Mirukas commented 7 months ago

Hi @purmarili ,

I'm also trying to get my external contacts and here's what I've come up with so far.

In the "Module/Public/func_Get-GALContacts.ps1" file, I changed Get-MgUser -All to Get-MgContact -All. If you are not using GraphSDK you can probably change the Graph Request from New-GraphRequest -Endpoint "/users?$select=&$top=999" -Beta to New-GraphRequest -Endpoint "/contacts?$select=&$top=999" -Beta.

I also added the -ContactsWithoutPhoneNumber option in the example.bat file, because properties are different between Users and Contacts in Exchange.

Let me know if you can retrieve your contacts as well.

NGBIT-de commented 1 week ago

Hi Mirukas, Hi JeshuaEdgar,

first of all, thank you very much for your work. The script is very simply designed and very easy to use. However, I have exactly the same idea as Mirukas and unfortunately, even with the support of CoPilot, I can't find a solution. My function script now looks like this, but when I execute it I have the same result as before and when I call the commands individually I have authentication errors. Do you have an idea or better still a solution for this? Many thanks in advance. Alex

function Get-GALContacts { [CmdletBinding()] param ( [bool]$ContactsWithoutPhoneNumber, [bool]$ContactsWithoutEmail, [bool]$IncludeContacts ) try { Write-Verbose "Getting GAL contacts" $allContacts = if ($UseGraphSDK) { Get-MgUser -All } else { New-GraphRequest -Endpoint "/users?$select=*&$top=999" -Beta }

    if ($IncludeContacts) {
        $allContacts += if ($UseGraphSDK) {
            Get-MgContact -All
        } else {
            New-GraphRequest -Endpoint "/contacts?`$select=*&`$top=999" -Beta
        }
    }

    if (-not $ContactsWithoutPhoneNumber) {
        $allContacts = $allContacts | Where-Object { $_.businessPhones -or $_.mobilePhone }
    }
    if (-not $ContactsWithoutEmail) {
        $allContacts = $allContacts | Where-Object { $_.mail }
    }

    $returnObject = @()
    $allContacts | ForEach-Object {
        $returnObject += [pscustomobject]@{
            businessPhones = $_.businessPhones
            displayname    = $_.displayName
            givenName      = $_.givenName
            surname        = $_.surname
            jobTitle       = $_.jobTitle                
            department     = $_.department
            emailAddresses = @(@{
                    name    = $_.mail
                    address = $_.mail
                })
        }
    }
    Write-Verbose "$($returnObject.count) contacts found"
    return $returnObject
}
catch {
    throw (Format-ErrorCode $_).ErrorMessage
}

}

NGBIT-de commented 1 week ago

Evening everyone, I was able to solve the problem myself with the help of CoPilot and debugging. Attached are my modified files. If JeshuaEdgar would like to, he is welcome to include the changes. Best regards Alex Sync-Contacts.ps1 param ( [CmdletBinding()] [parameter(Mandatory)][System.IO.FileInfo]$CredentialPath, [parameter(Mandatory)][string]$Tenant, [parameter(Mandatory)][string]$ContactFolderName, [string]$AzureADGroup, [string[]]$MailboxList, [switch]$Directory, [string]$LogPath, [switch]$ContactsWithoutPhoneNumber, [switch]$ContactsWithoutEmail, [switch]$UseGraphSDK, [switch]$IncludeContacts )

Set-Variable -Name UseGraphSDK -Value $UseGraphSDK -Scope Global -Option ReadOnly

if ($LogPath) { Start-Transcript -OutputDirectory $LogPath }

Write-Host "Using $(If ($UseGraphSDK) {"Graph SDK"} Else {"raw REST requests"}) for connection" If ($UseGraphSDK) { Import-Module Microsoft.Graph.PersonalContacts }

Import-Module .\Module\GAL-Sync.psm1 -Force

Connect-GALSync -CredentialFile $CredentialPath -Tenant $Tenant

Get users based on input

if ($Directory) { $mailBoxesToSync = (Get-GALContacts -ContactsWithoutPhoneNumber $true -IncludeContacts $IncludeContacts).emailaddresses | Select-Object -ExpandProperty address } elseif ($AzureADGroup) { $mailBoxesToSync = Get-GALAADGroupMembers -Name $AzureADGroup | Select-Object -ExpandProperty mail } elseif ($MailboxList -is [array]) { $mailBoxesToSync = $MailboxList } else { Write-Error "No valid mailbox input"; Read-Host; exit 1 }

$GALContacts = Get-GALContacts -ContactsWithoutPhoneNumber $ContactsWithoutPhoneNumber -ContactsWithoutEmail $ContactsWithoutEmail -IncludeContacts $IncludeContacts

foreach ($mailBox in $mailBoxesToSync) { try { Sync-GALContacts -Mailbox $mailBox -ContactList $GALContacts -ContactFolderName $ContactFolderName } catch { Write-LogEvent -Level Error -Message $_.Exception.Message Write-LogEvent -Level Error -Message "Failed to sync mailbox: $($mailBox)" } }

if ($LogPath) { Stop-Transcript }

func_Get-GALContacts.ps1 function Get-GALContacts { [CmdletBinding()] param ( [bool]$ContactsWithoutPhoneNumber, [bool]$ContactsWithoutEmail, [bool]$IncludeContacts ) try { $allContacts = if ($UseGraphSDK) { Get-MgUser -All } else { New-GraphRequest -Endpoint "/users?$select=*&$top=999" -Beta }

    if ($IncludeContacts) {
        $contacts = if ($UseGraphSDK) {
            Get-MgContact -All
        } else {
            New-GraphRequest -Endpoint "/contacts?`$select=*&`$top=999" -Beta
        }

        if ($contacts -is [System.Collections.IEnumerable]) {
            $allContacts += $contacts
        } else {
            $allContacts += @($contacts)
        }
    }

    $allContacts = $allContacts | Where-Object {
        ($ContactsWithoutPhoneNumber -or $_.businessPhones -or $_.mobilePhone -or $_.phones) -and
        ($ContactsWithoutEmail -or $_.mail)
    }

    $returnObject = $allContacts | ForEach-Object {
        $businessPhones = @()
        $mobilePhone = $_.mobilePhone

        if ($_.phones) {
            foreach ($phone in $_.phones) {
                if ($phone.type -eq "business") {
                    $businessPhones += $phone.number
                } elseif ($phone.type -eq "mobile") {
                    $mobilePhone = $phone.number
                }
            }
        }

        if (-not $businessPhones -and $_.businessPhones) {
            $businessPhones = $_.businessPhones
        }
        if (-not $mobilePhone -and $_.mobilePhone) {
            $mobilePhone = $_.mobilePhone
        }

        $contact = [pscustomobject]@{
            displayname    = $_.displayName
            givenName      = $_.givenName
            surname        = $_.surname
            jobTitle       = $_.jobTitle                
            department     = $_.department
            emailAddresses = @(@{
                    name    = $_.mail
                    address = $_.mail
                })
        }

        if ($businessPhones) {
            $contact | Add-Member -MemberType NoteProperty -Name businessPhones -Value $businessPhones
        }
        if ($mobilePhone) {
            $contact | Add-Member -MemberType NoteProperty -Name mobilePhone -Value $mobilePhone
        }

        $contact
    }

    return $returnObject
}
catch {
    throw (Format-ErrorCode $_).ErrorMessage
}

}