SharePoint to Azure Blob Storage (Parquet)

SharePoint lists written as Parquet into an Azure Blob Storage container after every run, a current snapshot plus a file per run, for Synapse serverless SQL, Spark and DuckDB.

Book a call

Book a call to talk through your use case, see further walkthroughs or demos, and set up a trial in your own environment.

Last updated

Status: Shipped · product: SPSync for Warehouses & Lakes

Raw data for the reporting platform lives in a Blob container, and every source arrives there on a schedule except one: the SharePoint list the facilities team uses to log site inspections. It reaches the container as a CSV someone saves from Excel when they remember. SPSync takes that job over. It reads the list through Microsoft Graph, keeps a merged copy on the sync host, and uploads every table to your container as Parquet after each run. The copy goes one way, and SharePoint stays the system of record.

How do I copy a SharePoint list to Azure Blob Storage?

Pick one of two shipped destinations. Both write the same files in the same layout:

If you want tables with a transaction log rather than files, the Delta Lake destination merges each run into Delta tables in a Blob container. The Parquet page covers the export to a local folder and to Amazon S3.

Container URL and sign-in

Give SPSync the container as abfss://<container>@<account>.dfs.core.windows.net/<prefix>. SPSync takes the account name from that address and uploads through the account’s Blob endpoint, so a standard Blob account should work as well as one with hierarchical namespace switched on (Data Lake Storage hierarchical namespace), though the flat-namespace case has not been tried on a live account. SPSync’s automated tests run this upload against the Azurite emulator; it has not been run against a live Azure storage account, so try one table first.

Two ways to sign in:

spsync doctor checks that it can reach the container before the first run.

Files in the container

Each table gets a folder under the prefix, and every run uploads two blobs into it:

Run IDs are random, so order the history by each blob’s last-modified time.

The site inspections list, with a Site lookup, an Inspector person column and a multi-choice Hazards found column, becomes site_inspections/, site_inspections__hazards_found/ and the shared sp_users/. The lookup arrives as site_id and site_value, the person as inspector_user_id, and each hazard as its own row with the parent item’s ID. When someone deletes an inspection in SharePoint, its row remains in later snapshots and sp_deleted_at records when it went.

A column added in SharePoint shows up in later files. On the DuckDB export, a retyped column also gets a _v2 column; the Azure Data Lake Storage Gen2 destination does not fork a retyped column today.

Which tools read Parquet from a Blob container?

Synapse serverless SQL reads a file in place: OPENROWSET with the URL of a table’s current.parquet returns the latest snapshot, with no load step (query Parquet files with serverless SQL pool). Spark and Databricks read the same path, and DuckDB’s azure extension reads it from a laptop. Power BI connects with the Azure Blob Storage connector, which lists the container’s files, and Power Query opens the Parquet file from there.

When an engine reads a whole table folder rather than one file, it gets the current snapshot and every history file together. Point it at current.parquet for the latest state, and at the history files only when you want to compare runs.

Steps

  1. Sign in with delegated access, or have an admin grant Sites.Selected on the sites you replicate.
  2. Create a storage account and container, or pick a prefix in an existing one.
  3. Issue a SAS token, or give the sync host’s identity a data role on the container.
  4. Set up the destination: the bucket URL on a DuckDB destination in replication.yaml, or an Azure Data Lake Storage Gen2 destination in the desktop app.
  5. Choose the lists, run spsync doctor from the command line, then start the first run. It loads every item and writes the first snapshot.
  6. Point Synapse, Spark, DuckDB or Power BI at the table folders.

Each run adds a history file per table. A lifecycle management rule on the container can delete blobs a set number of days after they were last modified (lifecycle policy structure). Every run rewrites current.parquet, so a rule like that removes old history files and leaves the current snapshot in place while runs continue. Storage capacity and transactions are billed by Microsoft.

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

Is Azure Blob Storage its own destination in SPSync?

Not as a separate type. Two shipped destinations write Parquet to a Blob container: the DuckDB destination's Parquet export, set in the command-line tool's YAML config, and the Azure Data Lake Storage Gen2 destination, which the desktop app also offers. The Delta Lake destination writes Delta tables to Blob as well.

Does the storage account need hierarchical namespace?

SPSync writes through the account's Blob endpoint, so a standard account should work. Hierarchical namespace is the configuration the code is written for, but neither kind of account has been tested against live Azure: the automated tests run against the Azurite emulator. Try one table first, whichever kind you use.

How does SPSync sign in to the container?

With a SAS token held in an environment variable, or with no token, in which case the Azure default credential chain signs in as the sync host's identity, such as a managed identity or an az login session.

Can Synapse serverless SQL read the files without loading them?

Yes. OPENROWSET reads a Parquet file from its URL, so a query over a table's current.parquet returns the latest snapshot. Serverless SQL bills for the data each query processes.

What happens to items deleted in SharePoint?

The row stays and its sp_deleted_at column is set, so a report can show deleted items or filter them out. To remove the row instead, set deletes: hard on that replication in the command-line tool's YAML config. Runs from the desktop app and from SPSync in your Azure always keep the row.

Talk to us about Azure Blob Storage (Parquet)

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 DuckDB and the Parquet export setup guide to create the database user and open the connection for Azure Blob Storage (Parquet). 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.

# This destination is available now. `spsync export-config` does not take it as a
# --destination-kind value yet, so write replication.yaml by hand (see the YAML tab)
# and run it with `spsync run replication.yaml` like any other destination.
pipeline_name: azure_blob_storage
prefix: azure_blob_storage
auth:
  mode: delegated
  client_id: 00000000-0000-0000-0000-000000000000
  tenant_id: common
destination:
  kind: duckdb
  connection_string_env: SPSYNC_DB_CONNECTION
  dataset_name: azure_blob_storage
  parquet_export_bucket_url: abfss://<container>@<account>.dfs.core.windows.net/spsync
  # Leave this out to sign in with the Azure default credential chain instead.
  parquet_export_sas_token_env: AZURE_STORAGE_SAS_TOKEN
scopes:
  - site_url: https://contoso.sharepoint.com/sites/example
    mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false

Cron (this destination 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

Book a call to talk through your use case, see further walkthroughs or demos, and set up a trial in your own environment.