Last updated
One way: SharePoint stays the system of record, and SPSync keeps a read-only copy of your lists current in Google BigQuery.
Status: Shipped · product: SPSync for Warehouses & Lakes
On Google Cloud, BigQuery is often where the rest of an organisation’s data already lives. Replicating SharePoint lists there puts them in the same SQL as everything else: Looker Studio and Looker dashboards, scheduled queries, notebooks and dbt models, with no cluster to size for the replication itself.
The service account and the dataset
BigQuery has no connection string. SPSync reads the service account’s downloaded JSON key from an environment variable, so the key never appears in the configuration file. Grant that account two roles:
- BigQuery Data Editor on the project or the target dataset, to create and alter tables and write rows;
- BigQuery Job User, to run load, query and DML jobs.
The destination’s dataset name is the whole namespace. If the dataset does not exist,
SPSync creates it in the service account’s own project, in the region you set, US
unless you choose another such as EU or europe-west2. A dataset that already exists
keeps its region. Datasets in a different project are not supported.
How rows arrive
Each list becomes a table in that dataset, for example hr_leave_requests, with child
tables such as hr_leave_requests__approvers for multi-value columns and one sp_users
table for people. Lookups arrive as {col}_id and {col}_value. Text of every length is
STRING, whole numbers are INT64, currency is NUMERIC(38,9), timestamps are UTC
TIMESTAMP and JSON-shaped values use BigQuery’s native JSON type.
After the first load, each run merges changed rows with BigQuery’s MERGE statement.
Deleted items stay with sp_deleted_at set, and the command-line tool’s hard delete
setting issues a plain DELETE instead. Optional reporting views are available here too.
A new SharePoint column appears on the next run and a removed one stays; a retyped column
is not forked into a _v2 column on BigQuery today (SPSync does that on SQL Server,
PostgreSQL, MySQL, ClickHouse, Oracle, SQLite and DuckDB).
| Title | Client lookup | Tags multi-choice | Owner person | Due |
|---|---|---|---|---|
| Riverside | Acme Ltd | Design; Build | Ada Lovelace | 2026-11-30 |
| Harbour | Borealis plc | Build | Alan Turing | 2027-02-14 |
then sync
- id
- title
- client_id → clients.id
- owner_id → sp_users.id
- due
- is_deleted soft delete
- sp_modified
- id
- title
- …
- project_id
- tag
One row per value: Riverside has two.
- id
- display_name
One users table shared by every list.
Typical uses
KPI dashboards in Looker Studio or Looker read the tables on the sync schedule, with no spreadsheet export in between. dbt models add the dataset as another source. Data preparation for models starts from typed, deduplicated rows. A set of extraction scripts run by hand becomes one replication that is reviewed in a pull request.
Limits to know
- SPSync caps generated names at 63 bytes, well inside BigQuery’s own limit.
- BigQuery does not enforce primary keys, foreign keys or uniqueness, so SPSync’s own write path keeps one row per item.
- SPSync loads with batch load jobs. A schedule of a few minutes against a brand new table can still meet BigQuery’s own consistency rules for very recent writes, which differ from a traditional database’s.
Setting it up
- Sign in with delegated access, or have an admin register an app with
Sites.Selectedon the sites you need for unattended runs. - Create the service account, grant the two roles and download its key.
- Put the key in an environment variable and set the dataset name and region.
- Select the lists or a whole site, then run.
- Connect Looker Studio, Looker or a notebook to the dataset.
BigQuery bills storage and query bytes scanned separately from SPSync.
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
How does SPSync authenticate to BigQuery?
With a Google Cloud service account. You put the whole downloaded JSON key in an environment variable, and SPSync reads it from there. BigQuery has no connection string.
Which roles does the service account need?
BigQuery Data Editor on the project or dataset, to create tables and write rows, and BigQuery Job User, to run the load and query jobs. It needs no project-level admin role.
Are multi-value columns stored as ARRAY or STRUCT fields?
No. A multi-choice column becomes a child table with one row per value, the same as on every other destination, and no nested ARRAY copy is written. Multi-lookup and multi-person values are still being verified on a live tenant.
Does each run rebuild the whole table?
No. Each run merges the changed rows into the table with BigQuery's MERGE statement, so the work done tracks the size of the change. A first load and a reconcile, weekly by default, read the whole list again.
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 Google BigQuery
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 Google BigQuery setup guide to create the database user and open the connection for Google BigQuery. 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 bigquery --prefix bigquery --destination-kind bigquery --connection-string-env SPSYNC_DB_CONNECTION --dataset-name bigquery --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: bigquery
prefix: bigquery
auth:
mode: delegated
client_id: 00000000-0000-0000-0000-000000000000
tenant_id: common
destination:
kind: bigquery
connection_string_env: SPSYNC_DB_CONNECTION
dataset_name: bigquery
scopes:
- site_url: https://contoso.sharepoint.com/sites/example
mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false
Cron (BigQuery 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.