Last updated
One way: SharePoint stays the system of record, and SPSync keeps a read-only copy of your lists current in Google Cloud SQL.
Status: Shipped · product: SPSync for Relational Databases
Getting SharePoint lists into Cloud SQL tends to stall on the connection, not the data: a public IP that refuses every address until you authorise it, a proxy to run, and an SSL mode that decides which certificate files the client must present. Once the connection works, SPSync writes the same tables it would write to PostgreSQL, MySQL or SQL Server on any other host, and Looker, Power BI or your Google Cloud app reads them with its usual driver. The PostgreSQL engine gets the most detail below, with notes where MySQL or SQL Server on Cloud SQL differ.
How do I connect SPSync to Cloud SQL?
The Cloud SQL Auth Proxy. Run it on the machine that runs the sync and point the
connection string at 127.0.0.1 on the engine’s port: 5432 for PostgreSQL, 3306 for
MySQL, 1433 for SQL Server. Leave the TLS options out, since the proxy handles
encryption and uses IAM to authorise itself. The proxy’s service account needs the Cloud
SQL Client role on the project.
A private IP. From a VM or container inside the VPC, or a network connected through private services access or Private Service Connect, connect straight to the instance’s private address.
A public IP with authorised networks. Add the sync host’s address to the instance’s authorised networks and connect directly. This is the route where the instance’s SSL mode matters most.
In every case SPSync logs in with a database username and password read from an environment variable. IAM database authentication is not supported.
SSL modes and the connection string
A Cloud SQL instance has one of three SSL modes, and a direct connection has to match it:
| Instance SSL mode | PostgreSQL connection string | MySQL | SQL Server |
|---|---|---|---|
| Allow unencrypted and encrypted (the default) | sslmode=require to encrypt anyway | ssl_ca with the server CA | Encrypt=yes |
| Encrypted only | sslmode=require | ssl_ca with the server CA | Encrypt=yes |
| Trusted client certificate required | sslmode=verify-ca with sslrootcert, sslcert and sslkey | ssl_ca, ssl_cert and ssl_key | Not offered for SQL Server |
The default accepts unencrypted connections, so a sync over a public IP is only encrypted if its string asks for it. Through the Auth Proxy none of this applies: the proxy encrypts the hop to the instance whatever the mode. The certificate files live on the sync host; the connection string, with their paths, goes in the environment variable.
SPSync writes to Cloud SQL through the same PostgreSQL, MySQL and SQL Server
destinations it uses on other hosts, and its automated tests run those engines in
containers. The Cloud SQL routes and SSL modes above come from Google’s documentation, so
prove the route from the sync host with psql, mysql or sqlcmd before the first run.
A database user for each engine
The default admin users can do far more than the sync needs. Each engine has its own
way to set up a narrower one, and on each the sync needs two places to write: the schema
the tables live in, and a second one with _staging on the end, where a run holds
changed rows before merging them.
- PostgreSQL. Create the user in the console or with
gcloud sql users create, then, connected aspostgres, runcreate schema hr authorization spsync_hr;and the same forhr_staging. Set the replication’sdataset_nametohr. - MySQL. Create the user, then the databases
hrandhr_staging. The user needs to create, alter and drop tables and indexes in both, and to read and write their rows;GRANT ALL ON hr.*and the same onhr_staging.*is the short form. - SQL Server. The
sqlserveruser Cloud SQL creates can make logins. Create a login and database user for the sync, give it two schemas it owns, and grant itCREATE TABLEin the database.
Keep each user’s password in the environment variable with the rest of the connection string, never in the replication file.
Editions, replicas and maintenance
Cloud SQL comes in two editions, Enterprise and Enterprise Plus, and the difference that touches a sync is maintenance. Enterprise Plus is designed for near-zero downtime during planned maintenance, while an Enterprise instance restarts briefly in its window. Either way the sync recovers on its next run. Read replicas suit dashboards and agents, and read pools put several read nodes behind one address. Cloud SQL’s managed connection pooling is built for apps with many short connections, and the sync does not need it.
A list, as Cloud SQL sees it
Consider a training register on a site replicated with the prefix hr, with a Course
lookup, a Trainee person column, a Score number and a multi-choice Certificates
column. Replicate the Courses list too, and SPSync writes:
| Table | What it holds |
|---|---|
hr_training_register | One row per training record: sp_item_id, title, score, course_id, trainee_user_id, sp_created, sp_modified, sp_deleted_at |
hr_courses | The Courses list, joined on sp_item_id = course_id |
hr_training_register__certificates | sp_item_id, value, ordinal, a row per certificate earned |
sp_users | The people table every person column points at |
PostgreSQL gets double precision for the score and timestamptz for dates, SQL Server
gets float and datetime2, and MySQL gets DOUBLE, DATETIME(6) and a bounded
VARCHAR for IDs. Schema changes in SharePoint carry through: new columns appear on the
next run, removed ones stay in the table, and a retyped column gets a _v2 sibling.
Worked example: pass rates by course
select c.title as course, count(*) as attempts, avg(r.score) as average_score, count(*) filter (where r.score >= 70) as passedfrom hr_training_register rjoin hr_courses c on c.sp_item_id = r.course_idwhere r.sp_deleted_at is null and r.sp_created >= now() - interval '90 days'group by c.titleorder by average_score;That is PostgreSQL. On SQL Server, write the pass count as
sum(case when r.score >= 70 then 1 else 0 end) and the date test as
r.sp_created >= dateadd(day, -90, sysutcdatetime()); on MySQL, the same case
expression and now() - interval 90 day. Looker can model the same join once and let
HR filter by department or site.
When the first connection fails
Connection refusedon 127.0.0.1. The Auth Proxy is not running, or is listening on another port. Start it before the sync, and run it as a service on a server.- The proxy logs a permission error. Its service account lacks the Cloud SQL Client role, or the Cloud SQL Admin API is not enabled in the project.
- A timeout on a public IP. The sync host’s address is missing from the authorised networks, or the host’s outbound address is not the one you added.
connection requires a valid client certificate. The instance demands client certificates and the string does not supply them. Add the three certificate settings from the table above, or switch to the proxy.
After the first good run, check the result in Cloud SQL Studio or any client:
select count(*) from hr.hr_training_register where sp_deleted_at is null should match
the number of items in the list. The weekly reconcile, configurable per replication,
makes the same comparison for every list and corrects any difference.
Querying the copy from BigQuery
If your analysts work in BigQuery, they do not need a second copy of the lists there. BigQuery’s federated queries reach Cloud SQL for PostgreSQL and MySQL through a connection resource, and run SQL on the instance:
select *from external_query( 'my-project.europe-west2.hr-cloudsql', 'select c.title as course, count(*) as attempts from hr.hr_training_register r join hr.hr_courses c on c.sp_item_id = r.course_id where r.sp_deleted_at is null group by c.title');The inner query runs on Cloud SQL, so put it on a read replica if the instance is busy. For warehouse-sized history, replicate the lists into BigQuery directly as a second destination.
On Cloud SQL for PostgreSQL the pgvector extension is available too. Enable it with
create extension vector and choose the pgvector destination,
and the sync adds an embedding to each row from the columns you name, calling OpenAI,
Azure OpenAI or a local model during the run.
A Looker view over the register
Looker connects to Cloud SQL for PostgreSQL like any Postgres database. A view file for the training table needs little more than the table name and the key:
view: hr_training_register { sql_table_name: hr.hr_training_register ;;
dimension: sp_item_id { primary_key: yes type: number sql: ${TABLE}.sp_item_id ;; } dimension: score { type: number sql: ${TABLE}.score ;; } measure: average_score { type: average sql: ${score} ;; }}Join it to a view on hr_courses through course_id, filter on sp_deleted_at being
null, and HR has an explore that refreshes as the list changes.
Typical uses on Google Cloud
A Looker or Power BI report reads the tables directly. Cloud Run services use them as an application read replica. A Power Automate flow that copied list rows into a database can be switched off. Analysts join lists on the key columns.
Agents over the Cloud SQL copy
Give an AI agent a read replica and a database user that can only read the HR schema,
then run a Postgres MCP server (or a SQL Server or MySQL one, to match the engine)
against it, through its own Auth Proxy if that is how your network works. SPSync’s own
read-only spsync mcp serve is the other option. Training records are personal data, and SharePoint’s item permissions do
not come across with them, so the database user’s grants and a row-level security policy
are how you keep one manager’s agent to their own team’s records.
Cloud SQL limits worth knowing
- Identifier lengths. PostgreSQL caps names at 63 bytes, MySQL at 64 characters and SQL Server at 128. SPSync shortens long SharePoint names to fit the engine.
- No superuser on PostgreSQL. The default
postgresuser belongs tocloudsqlsuperuser, which can create roles and schemas but is not a true superuser. The sync needs neither; a user that owns its schema is enough. - Maintenance and failover. Maintenance restarts the instance in its window, and a high-availability failover keeps the same connection name and IP. Either way the next scheduled run reconnects without a change to the configuration.
Set-up order
- Decide the connection route above and test it from the sync host with the engine’s own client.
- Sign in to SharePoint with delegated access, or have an admin grant
Sites.Selectedon the sites the sync needs. - Put the connection string in an environment variable and name it in the destination settings.
- Choose the lists, libraries or whole site and run. The first run loads everything; later runs read changes, with a reconcile, weekly by default.
Google bills Cloud SQL for the instance, its storage and any public IP, 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
Which Cloud SQL engines can SPSync write to?
Cloud SQL for PostgreSQL, for MySQL and for SQL Server. The sync behaves on each as it does on that engine anywhere else.
Do I need the Cloud SQL Auth Proxy?
It is the simplest route. Run the proxy next to SPSync and connect to localhost with no TLS settings, because the proxy encrypts the connection. A direct connection also works over a private IP, or a public IP listed in authorised networks, with the TLS settings the instance's SSL mode asks for.
Can SPSync use IAM database authentication on Cloud SQL?
No. SPSync sends a database username and password. When you use the Auth Proxy, IAM authorises the proxy to reach the instance, which is a separate step from the database login.
What happens to the sync during a Cloud SQL maintenance window?
Maintenance restarts the instance briefly. A run that overlaps it fails to connect or is cut off, and the next scheduled run reads every change since the last run that completed, so nothing is skipped.
Talk to us about Google Cloud SQL (SQL Server, Postgres, MySQL)
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 PostgreSQL setup guide, MySQL and MariaDB setup guide or SQL Server setup guide to create the database user and open the connection for Google Cloud SQL (SQL Server, Postgres, MySQL). 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 google_cloud_sql --prefix google_cloud_sql --destination-kind postgres --connection-string-env SPSYNC_DB_CONNECTION --dataset-name google_cloud_sql --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: google_cloud_sql
prefix: google_cloud_sql
auth:
mode: delegated
client_id: 00000000-0000-0000-0000-000000000000
tenant_id: common
destination:
kind: postgres
connection_string_env: SPSYNC_DB_CONNECTION
dataset_name: google_cloud_sql
scopes:
- site_url: https://contoso.sharepoint.com/sites/example
mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false
Cron (PostgreSQL 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.