Purpose
contactSync connects to Exchange Online successfully, the PowerShell session establishes, queries return without error — and yet the policy fails. The distinguishing symptom is that contactSync can see each object's name, distinguished name and ObjectGUID but not its RecipientTypeDetails or EmailAddresses, so it cannot build a valid object and the run ends in errors such as 36007 or 36025. In the logs this appears as a successful invoke returning a null or empty result string.
This article covers what to check and in what order, and is explicit about where the documented checks end.
Confirm which product you are troubleshooting
contactSync and GALsync are different products with similar terminology and near-identical dialogs. contactSync synchronizes contacts within a single environment — from a shared mailbox, a modern public folder, or the GAL — into a subfolder of individual users' mailbox contacts. GALsync synchronizes the Global Address List between separate environments, creating mail-enabled contacts in the other side's directory. The checks below are for contactSync.
Start with the account the policy actually runs as
If the policy fails on a schedule but a manual query on the same server succeeds, this is almost certainly the answer, and checking it first will save the rest of the investigation.
The contactSync Service exists only to execute scheduled policies, and every scheduled policy runs in the context of the user that the service runs as. The service checks once a minute for enabled policies, queues them and runs them sequentially as that account.
A PowerShell module installed without -Scope AllUsers goes into the current user's profile. It is therefore present when you test interactively and absent when the service runs the same policy — which produces exactly the pattern of a connection that appears to work but returns nothing usable.
# Install for every account on the server, not just the current one
Install-Module ExchangeOnlineManagement -Force -Scope AllUsers
Get-Module -ListAvailable ExchangeOnlineManagement | Select-Object Name, Version, ModuleBase
The ModuleBase path tells you which profile the module lives in. Check any certificate used by the connection on the same basis: a certificate in your own store is not in the service account's store.
Check the remaining prerequisites
-
Module and platform versions. contactSync 8.7 requires .NET Framework 4.8, Windows PowerShell 5.1, and the ExchangeOnlineManagement module version 3.10.1 for Exchange Online PowerShell V3. A version mismatch is the most likely cause of query behaviour that changed after an upgrade.
Get-Module -ListAvailable ExchangeOnlineManagement | Select-Object Name, Version $PSVersionTable.PSVersion - Authentication method. As of version 8.6, user-based authentication for Exchange Online has been removed. Only certificate-based authentication through a registered application is supported.
Verify the application's access, not just its permissions
An application can hold the right API permissions and still be prevented from reading the objects it needs.
# Connect the same way contactSync does
Connect-ExchangeOnline -AppId <app-id> `
-CertificateThumbprint <thumbprint> `
-Organization <tenant>.onmicrosoft.com
# Does a representative object return the attributes contactSync needs?
Get-Recipient -Identity <user> |
Format-List Name, RecipientTypeDetails, PrimarySmtpAddress, EmailAddresses
# Is an application access policy scoping this app to a subset of mailboxes?
Get-ApplicationAccessPolicy | Where-Object { $_.AppId -eq '<app-id>' }
The second query is the important one. If RecipientTypeDetails and EmailAddresses come back populated in your session but contactSync reports them as empty, the difference is the context the query runs in — module version, service account, or the application's effective scope — and not the tenant data.
An application access policy restricts a registered application to a defined group of mailboxes. If one applies to this application, objects outside its scope return without the attributes contactSync needs rather than returning an access error. A policy that returns no results, as above, rules this out.
Also confirm the certificate registered on the application has not expired, and that the application still holds the API permissions with admin consent granted.
Check for policies competing over the same contact folder
This is a separate fault that can coexist with the one above, and it is worth ruling out because it causes contacts to disappear rather than fail to write.
Point each policy at its own dedicated contact folder. Where several policies write into the same target folder, contacts synchronized by one policy can be deleted when another policy runs, because each policy treats the folder's contents as its own to reconcile. The symptom is contacts that appear and then vanish on a cycle matching the policy schedule.
This holds even when the policies synchronize the same source contacts to different mailboxes. If policies were created for testing alongside a production policy, check their targets before assuming the folder is only written by one.
When contacts were synchronized by an earlier version
Contacts created by an older contactSync version may not carry the source domain value that current versions use to recognise their own objects. Where that is the case, the documented remedy is to delete the synchronized contact folder and let the policy recreate it, so that the contacts are written with the values the current version expects.
Do this only for a folder contactSync owns, and confirm the policy's target folder before deleting anything — a user's own contacts must not be in the same folder.
If the symptom persists
If the module is at the required version and installed for all users, no application access policy applies, the certificate and API permissions are valid, and a manual query returns full attributes while the policy still reports empty results, the documented checks are exhausted. This is a case for NETsec support through ENow rather than further local configuration changes.
Collect both sets of files before raising it:
- Action → Export Configuration — the policy and environment configuration.
- Action → Export Status — the log files. Open the archive and confirm the newest log covers the failing run before sending it.
Include the exact error numbers from the failing run, a note of which attributes were returned and which were empty, and the output of the manual query that succeeded. The contrast between the two is the most useful evidence available, because it localises the fault to the context rather than the tenant.
References
- contactSync 8.7 Manual p.59 — the contactSync Service; scheduled policies run in the context of the service account; the one-minute check and sequential execution queue.
- contactSync 8.7 Upgrade Instructions p.4 — .NET Framework 4.8, Windows PowerShell 5.1 and ExchangeOnlineManagement 3.10.1 requirements; removal of user-based authentication in 8.6; certificate-based authentication from 8.1.
- The current contactSync manual and upgrade instructions are published by NETsec at netsec.de.
Comments
0 comments
Please sign in to leave a comment.