Google Contacts Two-Way Sync with Dataverse
Bidirectionally syncs Google Contacts with a Dataverse contacts table: Google changes upsert to Dataverse and Dataverse edits write back to Google, keyed on a stored ID map with a source flag to prevent loops. Keeps both contact stores authoritative for hybrid teams.
Provided as-is, without warranty of any kind. Review and test each pattern in a non-production environment before deploying it to live automations. See our Terms.
Overview
This solution keeps Google Contacts and a Dataverse contact table in sync both ways, loop-safely:
- Inbound (Google -> Dataverse): a daily poll reads all Google contacts and upserts each into a Dataverse mirror table, keyed on email. - Outbound (Dataverse -> Google): when a Dataverse-origin contact row is added, the flow creates that contact in Google and stamps the Google ID back onto the row.
A stored ID map (the mirror table, keyed on email) plus a Sync Source flag prevent the two flows from echoing each other.
Why it matters: When contacts live in two systems, one-way sync leaves one side stale. This loop-safe two-way design keeps both stores authoritative for hybrid teams.
Connector reality (important): The Google Contacts connector can READ and CREATE contacts but has no update or delete operation. Outbound sync therefore creates new Google contacts from new Dataverse rows; it does not edit existing Google contacts in place. Inbound sync is a full upsert because Dataverse supports update.
Use Case
A hybrid team edits contacts in both Google and a Dataverse-backed app and needs them consistent. New people added on either side should appear on the other, without changes bouncing back and forth indefinitely.
Flow Architecture
Flow A — Recurrence Poll Google Contacts
Recurrence (Day/1)Daily inbound poll (Google -> Dataverse).
Initialize variables
Initialize VariableEnv-var binds (Teams group/channel, entity set), configurable labels (source flag, status), guid() correlation id, counters.
Get My Google Contacts
Google Contacts — PeopleApiListContactsV4Read all contacts -> body/connections[].
Apply to each Google Contact
Foreach (concurrency 1)Process each contact in order.
Normalize fields
ComposeNormalize email/name/phone; capture metadata.source.id.
Condition Has Email
Condition (If)Skip contacts with no email (the dedup key).
List Matching Dataverse Contacts
Microsoft Dataverse — ListRecordsFind existing row by email ($filter).
Upsert Dataverse Contact
Microsoft Dataverse — UpdateRecord / CreateRecordUpsert; stamp syncsource=GoogleToDataverse.
Post Sync Summary To Teams
Microsoft Teams — PostMessageToConversationCreated/updated/skipped + correlation id.
Flow B — When A Contact Row Is Added Or Modified
Microsoft Dataverse — SubscribeWebhookTriggerOutbound row-change event (Dataverse -> Google).
Environment Variables
| Schema name | Type | Default | Description |
|---|---|---|---|
| flowlibs_GContactSyncEntitySetName | String | flowlibs_gcontactsyncmaps | Entity set name for the Dataverse trigger + ListRecords. |
| flowlibs_TeamsGroupId | String | <your-team-id> | Teams team id for notifications. |
| flowlibs_TeamsChannelId | String | <your-channel-id> | Teams channel id for notifications. |
Connectors & Connections
| Connector | API name | Actions used |
|---|---|---|
| Google Contacts | shared_googlecontacts | PeopleApiListContactsV4 PeopleApiCreateContactV3 |
| Microsoft Dataverse | shared_commondataserviceforapps | SubscribeWebhookTrigger ListRecords CreateRecord UpdateRecord |
| Microsoft Teams | shared_teams | PostMessageToConversation |
Note — All connections are referenced as solution connection references; the flow is portable between environments as long as a connection is mapped at import time.
Customization Guide
Almost every realistic variant of this flow can be implemented by changing environment variable values. A few cases require small edits inside the flow definition — those are called out explicitly below.
- Poll frequency
- Change the Recurrence interval on Flow A (default daily).
- Field ownership / mapping
- Adjust the Compose and item/ mappings to master specific fields in one system.
- Source-flag label
- Change varSourceFlagGoogle if you relabel origins; keep both flows in agreement.
- Group scoping
- The connector's PeopleApiListContactsV4 returns all contacts (no group filter); add a Filter on a group field if needed.
- Updates to existing Google contacts
- Not possible via this connector; to push edits, add an HTTP action against the Google People API (people.updateContact) with OAuth, or surface the change for manual handling.
- Deletes
- Propagate or tombstone via an added branch (out of scope for this demo).
Key Expressions
The flow is intentionally light on Power Fx / WDL gymnastics — the heaviest expressions are the branch-name concatenation and the approval outcome check. They are listed below in the order they appear in the flow.
EXPR.01Normalized email (ID-map key)
Stable cross-system key for the ID map.
EXPR.02Google contact ID
Google People metadata.source.id captured for the map.
EXPR.03Outbound loop guard (AND)
Three-part guard that prevents echo loops.
EXPR.04Stamp Google ID back
Writes the new Google ID back to stop the loop.
Customize & download
Generate a ready-to-import copy of this solution with your environment-variable values baked in — available on Base, Pro, or Team.
Upgrade to customize
Comments
Sign in to join the conversation.
Sign inNo comments yet. Be the first to share your experience with this flow.