SharePoint to Apache Iceberg tables

Apache Iceberg tables written from SharePoint lists through a REST catalog, with snapshot history that Spark, Trino, Athena and DuckDB can query.

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 Apache Iceberg tables.

Status: Shipped · product: SPSync for Warehouses & Lakes

Iceberg is an open table format that many engines can read through one catalog. If your lakehouse already runs Spark, Trino, Athena or DuckDB against Iceberg tables, SPSync adds SharePoint lists to that catalog as more tables, with no separate copy for each engine.

The catalog connection

SPSync writes through an Iceberg REST catalog. It needs:

All of these can be set in the desktop app or server Destination screen, in the command-line tool’s YAML config, or through an AI assistant using the admin MCP server, which sends you to a secure page for the token, client secret and keys.

If the bucket only needs files, with no catalog, the Parquet export writes straight to Amazon S3 or Azure Blob Storage instead. Secrets go in environment variables. SPSync also keeps a local state file between runs, which needs a stable path on the sync host.

Full snapshots, every run

SPSync reads SharePoint with delta queries and keeps its own merged copy of each list. After every run it overwrites each Iceberg table with that copy’s full current state, as one new snapshot. Reading the latest snapshot always gives a complete, consistent table, and the snapshot history doubles as a record of each run. The cost is write volume: moderate lists on a schedule of minutes are fine, while a very large list on a very short interval rewrites a lot of data.

Table contents

A supplier audit list with a Supplier lookup, an Auditor person column and a multi-choice Findings column becomes qa_supplier_audits, with supplier_id, supplier_value and auditor_user_id, a qa_supplier_audits__findings table and the shared sp_users table. Text and IDs are string, whole numbers keep separate int and long types, currency is decimal(38,9) and timestamps are timestamptz.

Columns added in SharePoint are added to the table on the next run. Changing a column’s type on Iceberg is not supported yet, and a retyped column is not forked into a _v2 column (SPSync does that on SQL Server, PostgreSQL, MySQL, ClickHouse, Oracle, SQLite and DuckDB). If it happens, the run fails early with an error naming the list, the column and both types, and nothing is written to Iceberg.

Where it fits

dbt projects across a lakehouse read the tables from whichever engine they already use. A Data Factory or flow-based extract into the lake can be retired. A data lake built across many sources lets Trino serve ad hoc questions while Spark runs batch jobs on the same tables.

Steps

  1. Sign in to SharePoint with delegated access; for unattended runs, an admin approves Sites.Selected on the sites you replicate.
  2. Gather the catalog URI, warehouse and namespace, and any token or storage keys.
  3. Choose the lists or a whole site and run; the first run creates each table.
  4. Query the namespace from Spark, Trino, Athena or DuckDB.

Snapshots and metadata files accumulate with every run, so schedule snapshot expiry and compaction in your engine of choice; SPSync does not run them.

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

Which Iceberg catalogs does SPSync support?

A REST catalog. AWS Glue catalogs are not supported. You give SPSync the catalog endpoint, the warehouse location and a namespace, plus a bearer token or OAuth2 client credentials if the catalog needs one. OAuth2 is what Polaris, Snowflake Open Catalog, Unity and Tabular catalogs use.

Does each run merge changes or rewrite the table?

Each run overwrites every table with its full current state, recorded as a new Iceberg snapshot. That keeps the tables simple and correct, and it means very large lists on a short interval write more data than a merge would.

Can I query an earlier version of a replicated table?

Yes. Every run adds a snapshot, so you can query an earlier one by snapshot ID or timestamp, for example to compare a report with how the list looked last month.

What happens to deleted SharePoint items?

They stay in the table with sp_deleted_at set, in the main table and its child tables alike, so a query can filter them out or keep them for history.

Can I change a column's type in SharePoint for a list synced here?

No, Iceberg does not support it in SPSync today. The run stops before writing anything and names the list, the column and both types. Avoid retyping columns on lists you replicate to Iceberg.

Talk to us about Apache Iceberg tables

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 Apache Iceberg setup guide to create the database user and open the connection for Apache Iceberg tables. 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 iceberg --prefix iceberg --destination-kind iceberg --connection-string-env SPSYNC_DB_CONNECTION --dataset-name iceberg --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: iceberg
prefix: iceberg
auth:
  mode: delegated
  client_id: 00000000-0000-0000-0000-000000000000
  tenant_id: common
destination:
  kind: iceberg
  connection_string_env: SPSYNC_DB_CONNECTION
  dataset_name: iceberg
scopes:
  - site_url: https://contoso.sharepoint.com/sites/example
    mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false

Cron (Iceberg 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.