Feature specs
Your source of truth is a Dataverse table. But since not everyone in your org has the necessary license or access rights, you would like to also show the content of that table in a SharePoint list.
- Develop a synchronization flow that ensures the SharePoint list always reflects the current state of the Dataverse table.
- This synchronization should run once per day (to limit the number of flow runs), for example nightly.
- The SharePoint list is read-only for everyone (except your flow).
The usual approaches (not recommended!)
- You delete the whole SharePoint list content every night and completely refill it with the Dataverse content. This is extremely simple and extremely wasteful in terms of flow calls and SharePoint resources. You also lose the version history, which could be useful to your users.
- Each night, you iterate over every row of your SharePoint list and compare it to the Dataverse table. Very inefficient and very slow, but at least the version history is maintained.
A much better approach (very fast, efficient and robust!)
Your SharePoint list
- "Key" (Text): The GUID of the Dataverse record, which is the source of truth for the corresponding SharePoint row. Active "Enforce unique values" on this column. If you are familiar with databases, this change will effectively allow this column to become an alternate key.
- "Last sync" (Date/Time): Timestamp indicating when this row was last updated by the flow.
- Make sure this list is read-only (except for your flow)
Flow structure overview
- Load all needed data once: Load both the full Dataverse table and the SharePoint list once—right at the beginning of the flow.
- Find records to sync: Find records to sync: Use Select, Filter array, and some additional trickery to identify Dataverse records whose corresponding SharePoint row is outdated or does not yet exist.
- Create or Update: Iterate over the content of the filter array. If a SharePoint row does not exist yet, then create a new one. Otherwise, update the row.
Part 1 - Load all Data
Part 2 - Find records to sync
Part 2.1 - Select
- Key: item()?['Key']
- Value: item()?['LastSync']
Part 2.2 - Create a flat JSON object
- Turn the Output from Select into a string
- replace all },{ sequences with a single comma, effectively dissolving the boundaries between the JSON objects in the array.
- turn the whole string back into a JSON object, using json()
Part 2.3 - Filter Array
- The first
coalesce()is used in case modifiedon is empty. It will then use "createdon" as fallback. - the second coalesce is there in case our flattened json object does not have a key, we are looking for (in other words, the SharePoint list row does not exist yet). If this happens, we should use an arbitrarily early date, which is guaranteed to be lower than any of the dates in our Dataverse table's "modifiedon" column. January 1, 2000 is a safe choice for this purpose.
Part 2.4 Almost done!
Part 3 Iterating over the filtered array
The fastest method would be to essentially do the exact same thing again, but this time create a flat JSON object where the key is still the Dataverse record GUID, but the value is the ID of the Sharepoint list item.
- Key: item()?['Key']
- Value: item()?['ID']
- Use a compose action to get the SharePoint list ID from a given Dataverse GUID.
- If this ID does not exist, it means the row does not exist and therefore we have to create it
- If it does exist, we can Update the list item
Limitations & scale considerations
This approach is intentionally optimized for small to medium datasets, where performance, robustness, and simplicity matter more than horizontal scalability. Because the flow loads the entire Dataverse table and the SharePoint list into memory at the beginning of each run, it relies on Power Automate’s ability to comfortably handle in‑memory JSON objects.In practice, this works extremely well for hundreds to low thousands of records. Once the SharePoint list grows beyond a few thousand active items, memory pressure and payload size limits in Power Automate can start to become a bottleneck. At that point, the bottleneck is not SharePoint or Dataverse themselves, but the fact that Power Automate evaluates all Select, Filter array, and expression logic in memory rather than streaming records.
Another important consideration is data shape stability. This pattern assumes:A stable one‑to‑one relationship between Dataverse records and SharePoint rows
A guaranteed unique key (the Dataverse GUID)
A clear definition of “last updated” that is fully controlled by Dataverse
If users are allowed to edit the SharePoint list directly or if multiple systems write to the same list, the assumptions behind the timestamp comparison no longer hold.
Finally, this approach trades absolute real‑time accuracy for efficiency. By design, synchronization happens on a scheduled basis (e.g., nightly). If near‑real‑time synchronization or very large datasets (tens of thousands of rows) are required, Dataverse change tracking, event‑driven flows, or per‑record lookups should be considered instead.
In short: this pattern shines when SharePoint is a read‑only projection, the dataset is bounded, and the goal is to achieve maximum speed with minimal flow actions - not when SharePoint is expected to behave like a transactional replica.
Nuances
- Consider filtering both your Dataverse records on load as well as your SharePoint list items. Use an identical filter in both cases! Example: {all active records} OR {those who have been changed in the last 100 days}. This will disregard inactive records that have been inactive for enough time (100 days) so that their state switch from active to inactive could be synced by your flow.
- Consider creating a filtered array of all the SharePoint items whose Key does not exist as a GUID in the Dataverse records. In other words, the record was deleted from Dataverse. Then iterate over that array and delete the rows. You can do this with a Select on the Dataverse records ("All Guids"), with the Map being only the GUID. Then Filter Array on the SharePoint items and the condition being: "All Guids" does not contain the value in the Key column item()?['Key'].
- In case the needed information to build the SharePoint list item is spread across multiple tables, do consider using FetchXML or Expand Query. I personally love working with FetchXML and use XrmToolbox to develop and test a new query before dropping it into Power Automate.
- You can further speed up your flow by activating concurrency on the "Apply to each" loop that iterates over the filtered arrays (create/update or deletion).
