Exchange Contacts Mirroring & Global Distribution Automation

Summary

This automation synchronizes contacts between licensed user mailboxes and a shared mailbox folder named All Contacts using Microsoft Graph app-only authentication.

It provides:


Functional Overview

Step 1: User -> Shared mailbox

Step 2: Shared mailbox -> User

Matching and deduplication

Contacts are matched in this order:

  1. SyncId in personalNotes
  2. Primary email (emailAddresses[0].address)
  3. displayName (fallback)

Metadata Stamping (personalNotes)

The script maintains sync tags in personalNotes:

Notes behavior:


Update Behavior

UpdateExisting is enabled by default.

An existing target contact is only updated if:

If no change or target is newer/equal, it is skipped.


User Scope / Test Mode

User list resolution:

  1. Per-tenant test users (if configured).
  2. Otherwise all licensed users from Graph:
    • /users?$filter=assignedLicenses/any(x:x/skuId ne null)

Shared mailbox account is excluded from user sync loops.


Configuration

Preferred: full JSON config

Use AUTOMATION_SYNCCONTACTS_CONFIG_JSON:

{
  "MicrosoftTenants": [
    {
      "TenantId": "tenant-id-or-domain",
      "ClientId": "app-client-id",
      "ClientSecret": "app-client-secret",
      "GlobalAddressBookUserId": "shared.contacts@contoso.com",
      "TestUserList": ["user1@contoso.com"]
    }
  ],
  "DryRun": "true"
}

Multi-tenant via separate env var

Use AUTOMATION_SYNCCONTACTS_MICROSOFT_TENANTS_JSON with tenant array.

Backward-compatible single-tenant env vars

Optional tenant test-user map env var

Works even with full config JSON:

Example:

{
  "contoso.onmicrosoft.com": ["user1@contoso.com", "user2@contoso.com"],
  "fabrikam.onmicrosoft.com": ["user3@fabrikam.com"]
}

Lookup keys include tenant identifiers like TenantId, PrimaryDomain, TenantDomain.


Prerequisites


Required Microsoft Graph Permissions

Application permissions:


Dry Run

Set DryRun / AUTOMATION_SYNCCONTACTS_DRY_RUN to true to simulate actions without writes. The script logs what would be created/updated and prints creation summaries.


CI/CD

Designed for automation pipelines (for example GitLab CI/CD). Store secrets as protected CI/CD variables.


Troubleshooting


Security Notes


Script Source

Automation-ContactsMirroring.ps1


Revision #4
Created 2026-01-09 21:50:07 UTC by Luca Noah Caprez
Updated 2026-02-24 16:22:56 UTC by Luca Noah Caprez