SharePoint to Dataverse (for Power Apps that need delegation)

Copy SharePoint lists into Dataverse tables so a Power App can delegate filters and sorts, while people keep editing in SharePoint.

Book a call

Talk through your use case, see further walkthroughs or demos, and set up a trial in your own environment.

Last updated

One way: SharePoint stays the system of record, and SPSync keeps a read-only copy of your lists current in Dataverse.

Status: Shipped · product: SPSync for Relational Databases

Power Apps built on a growing SharePoint list eventually run into delegation: galleries that stop filtering correctly, results cut off at the app’s data row limit (500 rows by default, up to 10,000 at most, per Microsoft Learn’s delegation overview), screens that slow down as the list grows. Putting the same data in Dataverse gives the app a source it can delegate to, and SPSync keeps it current while SharePoint stays the place people edit.

Signing in to the environment

Dataverse has no API key or password sign-in, so SPSync signs in as an application user:

  1. Create an app registration in your tenant, or reuse one, and give it a client secret. It needs no API permissions of its own.
  2. In the Power Platform admin centre, add the app as an application user on the environment with the System Customizer role, or System Administrator.
  3. Note the environment URL, such as https://org.crm.dynamics.com.

SPSync takes the environment URL, tenant ID and client ID as settings, and reads the client secret from an environment variable. A local state file on the sync host holds the merge state between runs, so keep it on a stable path.

Tables SPSync creates

Every table and column SPSync creates is a custom one carrying a publisher prefix, sps by default, so a pm_projects table appears as sps_pm_projects. One Dataverse table is created per SPSync table: the list’s main table, one per multi-value column, and the shared users table. An alternate key on the SharePoint item ID means a repeat sync updates the right row.

Lookups arrive as an ID column and a display-value column, ready to show and join on, with no Dataverse relationship between them. A single choice is a text column. Currency is a four-decimal number, since Dataverse’s own currency type needs a transaction currency on every record. Dates and times are stored in UTC and shown in each user’s time zone. Deleted items keep their row with sp_deleted_at set, so a view can filter them out.

New SharePoint columns appear on the next run as new Dataverse columns, and removed ones stay. Changing a column’s type is not supported yet on Dataverse and a retyped column is not forked into a _v2 column, so avoid retyping a column in SharePoint for a list synced here.

How it is used

Power Apps that outgrew delegation. A gallery rebuilt against the Dataverse table filters and sorts on the server again; see Power Apps reading SQL for large lists.

Model-driven apps on SharePoint data. The app reads the Dataverse tables while the sync keeps them in step with the list.

One environment for many sites. The same list template on several sites can replicate into one environment with a prefix per site; see the same list on many sites into one table.

Limits worth planning for

Each run sends only rows whose content changed since the last successful push, in batches of up to 500 per request. A first load or a reconcile, weekly by default, sends every row again, and that counts against Dataverse’s service-protection limits. Lists of tens of thousands of rows fit comfortably; past that, a longer reconcile interval helps. Very long SharePoint column names can exceed Dataverse’s name length limits once the prefix is added. Tables are created in the environment’s default solution, and Dataverse storage is billed by Microsoft.

Dataverse security roles decide who sees the replicated tables inside the Power Platform, separately from SharePoint permissions, so plan access there as well.

Desktop, Azure or command line

All three editions run the same engine, write the same tables and use the same licence key, so the choice comes down to who looks after the sync.

Desktop app

For macOS and Windows. Sign in with Microsoft, tick the lists, choose a database and sync from your own machine on the schedule you set. Delegated sign-in needs no admin unless your tenant requires admin consent for apps.

In your Azure

SPSync in your Azure, a managed application for your own subscription, is deployed with you on a call while the Marketplace listing is prepared. Once deployed it is always on, so the sync keeps running with every laptop closed.

Command line

Every replication is a YAML file you can review and commit. Run it from a server, a container or a CI pipeline, and re-run it safely any time.

Frequently asked questions

Why replicate a SharePoint list into Dataverse?

Power Apps delegates filtering, sorting and paging to Dataverse for far more functions than it does to a large SharePoint list. An app rebuilt on the Dataverse table keeps working past the point where the list version starts showing delegation warnings.

Do SharePoint lookups become Dataverse lookup relationships?

No. A lookup arrives as two plain columns, the related item's ID and its display value, so the app can show the name and join on the ID. SPSync does not create Dataverse lookup relationships, because those would force a load order on every sync.

What happens to SharePoint choice columns?

A single choice arrives as a text column holding the chosen value. A multi-choice column becomes its own small Dataverse table with one row per value. Dataverse choice columns are not created.

How does SPSync avoid duplicate rows on repeat syncs?

Each table gets an alternate key on the SharePoint item ID, and every write is an upsert on that key, so a changed item updates its existing row.

How does SPSync sign in to Dataverse?

As an application user: an app registration in your tenant with a client secret, added to the environment by an admin with a security role such as System Customizer. Dataverse has no API key or password sign-in.

Is there a free trial for the Dataverse destination?

Dataverse is sold after a call, partly because of its own licensing and storage costs. Get in touch and we will size it against your lists.

Talk to us about Dataverse (for Power Apps that need delegation)

SPSync is licensed per installation and per target family, with Standard Support included. Tell us which targets you need and how many machines, and we will size it with you. See how licensing works.

Set it up

Sign in to SharePoint with the SharePoint connection guide, then follow the Microsoft Dataverse setup guide to create the database user and open the connection for Dataverse (for Power Apps that need delegation). If the connection test fails, the troubleshooting guide lists the errors people hit and what fixes them.

Script it

Everything you can click, you can script. The CLI, YAML config and automation below reproduce this scenario exactly. See the CLI reference.

spsync auth login --client-id 00000000-0000-0000-0000-000000000000
spsync export-config --output replication.yaml --pipeline-name dataverse --prefix dataverse --destination-kind dataverse --connection-string-env SPSYNC_DB_CONNECTION --dataset-name dataverse --client-id 00000000-0000-0000-0000-000000000000 --site-url https://contoso.sharepoint.com/sites/example --all-lists
spsync doctor replication.yaml
spsync run replication.yaml
pipeline_name: dataverse
prefix: dataverse
auth:
  mode: delegated
  client_id: 00000000-0000-0000-0000-000000000000
  tenant_id: common
destination:
  kind: dataverse
  connection_string_env: SPSYNC_DB_CONNECTION
  dataset_name: dataverse
scopes:
  - site_url: https://contoso.sharepoint.com/sites/example
    mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false

Cron (Dataverse nightly):

# every night at 02:00
0 2 * * * cd /opt/spsync && SPSYNC_DB_CONNECTION="$(cat /etc/spsync/db.env)" uv run spsync run replication.yaml >> /var/log/spsync/run.log 2>&1

GitHub Actions:

# .github/workflows/spsync-run.yml
name: SPSync
on:
  schedule:
    - cron: "0 2 * * *"
  workflow_dispatch: {}
jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v3
      - run: uv sync
      - run: uv run spsync run replication.yaml
        env:
          SPSYNC_DB_CONNECTION: ${{ secrets.SPSYNC_DB_CONNECTION }}
          SPSYNC_LICENCE_KEY: ${{ secrets.SPSYNC_LICENCE_KEY }}

Azure DevOps:

# azure-pipelines.yml
schedules:
  - cron: "0 2 * * *"
    branches: { include: [main] }
    always: true
steps:
  - script: uv sync
  - script: uv run spsync run replication.yaml
    env:
      SPSYNC_DB_CONNECTION: $(SPSYNC_DB_CONNECTION)
      SPSYNC_LICENCE_KEY: $(SPSYNC_LICENCE_KEY)

Windows (PowerShell + Task Scheduler):

# Windows Task Scheduler action (see /docs/cli/automation/)
$env:SPSYNC_DB_CONNECTION = (Get-Content C:\spsync\db.env)
uv run spsync run C:\spsync\replication.yaml
schtasks /Create /SC DAILY /ST 02:00 /TN "SPSync" /TR "powershell -File C:\spsync\run.ps1"

macOS (launchd):

# ~/Library/LaunchAgents/io.spsync.run.plist, then: launchctl load ~/Library/LaunchAgents/io.spsync.run.plist

Secrets (licence key, connection strings) come from the environment, so never write them into the YAML. See scheduling and secrets.

Book a call

Tell us about your lists and where they need to go: we will talk through your use case, show further walkthroughs or demos, and set up a trial in your own environment.