Skip to content
abhinavbhatia
Sheet 02 / 05 — Blogs← Archive

Databricks on Google Cloud, Part 10: Migration to Google Cloud

How to move Databricks workloads from AWS, Azure or an on-premises Hadoop estate to Databricks on Google Cloud: what is different, what moves easily and what does not, a five-step approach with the real tools, three routes for the data, a translation cheat sheet, two reference architectures, the problems I see most often, and a recap of the whole series.

TL;DR Pocket Books · Part 10

Read this chapter as 5 illustrated mini-books

Flip pages horizontally like a hardcover book, or swipe up like a reel to move through all 5 topics (15 animated pages).

0 of 5 readAll Series Shorts →
Part 1 · TL;DR 1 of 5
← → Turn Page · ↑ ↓ Next TL;DR ReelSwipe ← → page · Swipe ↑ ↓ next TL;DR
100%
1.0×
Full post ↗
PART 1 · FOUNDATIONSPLATE 01.1

i
TL;DR 01PAGE 1 OF 4
MINI-BOOK 01 · STEP 1

Nine parts ago we started with an empty Google Cloud organisation. Since then we have subscribed to Databricks, created projects and a workspace, wired up identity, networking, storage and governance, used the platform, connected it to BigQuery and Gemini, and learnt to pay for it and run it. All of that assumed Acme was starting fresh on Google Cloud.

Many teams are not. They already run Databricks, usually on AWS or Azure, sometimes beside an older on-premises Hadoop cluster, and they want to move it. The Databricks platform is the same on every cloud, so most of their skills and code carry over. The parts that do not carry over are exactly the parts this series has covered: projects, service accounts, networks, buckets and billing.

This last part is about the move itself. I compare the three clouds, sort what moves easily from what does not, walk through a five-step approach with the real tools, show three routes for the data, give a translation cheat sheet for code and configuration, draw two reference architectures, and list the problems I see most often. It ends with a recap of the whole series. All status claims are as of October 2026, and the ones that changed since are updated and dated (last checked 8 November 2026).

If you prefer to watch first, here is the heart of this part as a short animated explainer.

Video: how to move Databricks from AWS to Google Cloud, in three minutes. The narration is a synthetic voice; the script and animation are mine.

What are the key ideas?

Six terms carry this post.

  • Migration. Moving a running workload, with its data, code, schedules and permissions, from one platform to another, so that the new platform takes over and the old one can be switched off.
  • Source and target. The source is where the workload runs today (here, Databricks on AWS); the target is where it will run (Databricks on Google Cloud).
  • Wave. A group of workloads that move together. Migrating in waves means moving one self-contained group at a time, learning from it, and then moving the next, instead of moving everything in one weekend.
  • Hydrate. Filling a new, empty Unity Catalog metastore with the catalogs, schemas, tables, volumes and grants that existed in the old one. The data is copied; the metadata is recreated.
  • Parallel run. A period during which the same pipelines run on both source and target, and their outputs are compared, before anyone relies on the target.
  • Cut-over. The moment consumers (dashboards, applications, people) switch from the source to the target, which then becomes the system of record: the copy everyone treats as correct.

To make this concrete, suppose that before Part 1, Acme’s supply team had built its lakehouse on Databricks on AWS, in the eu-west-2 (London) region, with a workspace called acme-supply-aws-prod and its data in s3://acme-supply-lake. This post moves that workload to the workspace you already know, acme-prod-euw2-supply in europe-west2, with its data in gs://acme-lake-supply.

ONE WORKLOAD, TWO CLOUDS, FIVE STEPS Databricks on AWS acme-supply-aws-prod eu-west-2 (London) s3://acme-supply-lake Unity Catalog + some Hive metastore tables Databricks on GCP acme-prod-euw2-supply europe-west2 (London) gs://acme-lake-supply Unity Catalog, metastore in euw2 1 Assess 2 Design 3 Build 4 Migrate 5 Validate cut over only after a parallel run travels: data, code, jobs, models, grants The Databricks platform is the same on both sides. What changes is underneath it: storage paths (s3:// to gs://), identities, the network, regions and who bills you.

the migration at a glance: Acme's supply workspace on Databricks on AWS moves to Databricks on Google Cloud in five steps, assess, design, build, migrate, and validate and cut over; data, code, jobs, models and permissions travel, while storage paths, identities, network and billing change underneath

How is Databricks on Google Cloud different from AWS and Azure?

The Databricks platform is the same on all three clouds; what differs is everything around it: the account, the metastore, the storage and identity plumbing, and the tier rules.

Think of moving to another country with the same model of car. You drive it exactly as before, but it needs new number plates, a new registration, new insurance and, in some countries, a different inspection to be allowed on certain roads. The driving is the Databricks workspace, notebooks and Spark; the plates and registration are the new Databricks account and metastore; the insurance is the service accounts and buckets; and the inspection is the tier a feature needs.

The platform is the same on all three clouds, and so is the split into a control plane and compute planes: compare the Databricks on AWS architecture page with Microsoft’s What is Azure Databricks?, and Part 1 explains both planes on Google Cloud. What changes is the cloud underneath and the commercial wrapper.

On Azure, the product is called Azure Databricks, and Databricks’ pricing page notes that its charges “are billed directly by Microsoft”. On Google Cloud, the product launched in February 2021, when Google Cloud and Databricks announced “a new partnership to deliver Databricks at global scale on Google Cloud”, and you subscribe through Google Cloud Marketplace (Part 1 walks through it). As of October 2026, Databricks lists 15 Google Cloud regions, and its regional table shows serverless compute for notebooks, jobs, pipelines and SQL warehouses in all 15.

ONE PLATFORM, THREE CLOUDS AWS Azure Google Cloud Product name Databricks on AWS Azure Databricks Databricks on Google Cloud Who bills it Databricks: pay as you go or a contract Microsoft, on the Azure bill Google Cloud Marketplace, on the Google Cloud bill Object storage Amazon S3 Azure Data Lake Storage Cloud Storage Classic compute Amazon EC2 instances Azure virtual machines Compute Engine VMs Architecture control plane + compute planes control plane + compute planes control plane + compute planes Documentation docs.databricks.com/aws learn.microsoft.com/ azure/databricks docs.databricks.com/gcp Skills carry across clouds. The cloud underneath changes networking, identity and billing, which is what this series covers.

Databricks on the three clouds: the same platform and the same control plane and compute plane design; what changes is who bills you, the object storage and the virtual machines underneath

The cloud vocabulary itself (account, subscription, project, VPC, IAM role, service account) is mapped in Prerequisite 2, and where a workspace lands on each cloud is drawn in Part 1. For a migration, four further differences matter more than any name. How does this apply to Acme? Each one below is a new set of plates or papers for acme-supply-aws-prod on its way to acme-prod-euw2-supply.

  1. A new account and a new metastore. Your Google Cloud workspaces live in the Databricks account that the Marketplace subscription creates, not in the account that holds your AWS or Azure workspaces. And a Unity Catalog metastore (the top-level container for catalogs, schemas, tables and grants) is regional: Databricks says “you must have one metastore for each region in which your organization operates”. So the target always starts with an empty metastore in europe-west2, which you hydrate.
  2. A new workspace starts without legacy features. Databricks’ legacy features page says that “starting October 8, 2026, Databricks provisions all new workspaces in all accounts without access to legacy features”, and the account setting that controlled this is now deprecated, “enabled and locked” (checked 8 November 2026). Those features are DBFS root and mounts (DBFS, the Databricks File System, is a file-system view over object storage; a mount maps a bucket to a path such as /mnt/raw), the Hive metastore (the per-workspace table catalog that predates Unity Catalog), no-isolation shared clusters, and Databricks Runtime versions before 13.3 LTS (LTS means long-term support). Every Google Cloud workspace you build for the migration is new, so there is no switch to turn them back on, and a source workspace that depends on them is the biggest single source of rework I see. Part 5 explains DBFS and why it is legacy.
  3. Different storage and identity plumbing. Paths change from s3:// or abfss:// to gs://, and Unity Catalog reaches Cloud Storage through a Google service account that Databricks creates (Part 2), not an AWS IAM role or an Azure managed identity.
  4. Different tier boundaries. On Google Cloud, a customer-managed VPC needs the Premium tier and Private Service Connect needs the Enterprise tier (Part 3 and Part 9 explain both). Check that the tier you buy matches the controls your security team expects from the source.

Can I just move my workspace?

This is the first question I am asked, and the answer is no. You do not move a workspace; you build a new one on Google Cloud and move its contents.

Moving house abroad works the same way. Nobody ships the house. You take the boxes, the books and the furniture to a new home. Some things travel well, because they are self-contained: a box of books arrives as it left. Some do not, because they were built into the old house: a fitted kitchen has nowhere to go, and a bookmark that says “page 212 of the copy on the hall shelf” means nothing once that copy stays behind. The house is the workspace, the boxes are Delta tables and code, the fitted kitchen is the Hive metastore and DBFS mounts, and the bookmark is a streaming checkpoint.

I know of no documented way to move a workspace from one cloud to another; a workspace’s cloud, project and region are fixed when it is created (Part 1). A migration builds a new workspace on Google Cloud and moves its contents. Some contents travel easily, and some need real work. Here is how a workspace’s contents sort into boxes and fitted kitchens:

WHAT TRAVELS, AND HOW Asset How it moves Effort Watch out for Delta table data Storage Transfer Service, OpenSharing or CLONE easy egress charges; pause writers Notebooks, files, repos Git folders, or CLI export-dir/import-dir easy s3:// paths and /mnt mounts in code Unity Catalog objects recreate in the new metastore (Terraform, SQL); names can stay the same medium one metastore per region and cloud Grants replay from information_schema privilege views medium groups must exist in the new account Jobs and pipelines Declarative Automation Bundles, or the Terraform resource exporter medium node types, gcp_attributes, policies MLflow experiments, models mlflow-export-import medium re-apply grants; recreate endpoints Secret scopes recreate scopes and re-enter values manual no Key Vault-backed scopes on GCP Hive metastore tables upgrade to Unity Catalog first (UCX, SYNC, CLONE), then move hard new workspaces have no Hive metastore Streaming checkpoints do not move; streams restart on the new source with a new checkpoint hard plan a backfill or a start offset Delta data moving with Storage Transfer Service also needs its tables re-registered or cloned.

what moves easily and what does not: Delta data, code and notebooks move easily; Unity Catalog objects, grants, jobs and MLflow assets take some work; secret scopes are re-created by hand; Hive metastore tables and streaming checkpoints are the hard parts

Three things make the easy rows easy. Delta tables are open files in object storage (a Parquet data file per chunk and a _delta_log folder of commits, as Prerequisite 1 shows), so they can be copied like any other files. The Delta protocol stores each data file’s path as “a relative path to a data file from the root of the table” (or an absolute path), so a copied table usually still finds its files at the new location. And notebooks and code live in Git, or can be exported with the Databricks CLI.

The hard rows are hard because they depend on something that does not exist on the target. Tables in the Hive metastore have no Hive metastore to land in. A streaming checkpoint (the folder where Spark Structured Streaming records which input it has already processed) records the source it read; the Apache Spark streaming guide says that changing a query’s input sources between restarts “is not allowed”. A stream that read s3:// yesterday needs a new checkpoint when it reads gs:// today.

How does this apply to Acme? Acme’s boxes are the 46 tables in its Unity Catalog catalog and the notebooks in Git: they travel as they are. Its fitted kitchen is the nine tables still in the Hive metastore and the three DBFS mounts that step 1 below turns up, which are fixed on AWS before anything moves. And any stream it runs, such as Auto Loader ingestion, needs a new bookmark: a fresh checkpoint on the gs:// side.

How do you run a migration, step by step?

Plan once, then move in small waves, each proven before the next. The common mistake is the big-bang weekend: everything copied at once, and every problem found at once, by the business. Moving house room by room is the everyday version: you set up the new place once, then move the kitchen, live with it for a few days, fix what is missing, and only then move the next room. For Acme, I would make the first wave one low-risk pipeline, as the checklist at the end recommends.

I use five steps. The first three happen once; the last two repeat for every wave.

ASSESS, DESIGN, BUILD, MIGRATE, VALIDATE 1 ASSESS Inventory workspaces, jobs, tables, users, paths, secrets TOOLS UCX assessment system tables resource exporter 2 DESIGN Projects, network, identity, catalogs, pricing tier TOOLS Parts 1 to 6 of this series 3 BUILD Projects, VPC, workspace, metastore, credentials TOOLS Terraform: google and databricks providers 4 MIGRATE Data, code, jobs, models, grants, in waves TOOLS Storage Transfer OpenSharing, CLONE mlflow-export-import 5 VALIDATE Counts, checksums, parallel run, then switch TOOLS SQL comparisons query history job run timeline Steps 4 and 5 repeat for each wave: start with one low-risk pipeline, then the rest.

a five-step migration approach: assess the source estate, design the Google Cloud landing zone and Unity Catalog layout, build it with Terraform, migrate data, code, jobs, models and grants in waves, then validate and cut over after a parallel run; the tools for each step sit underneath

Step 1: Assess what you have

You cannot plan a move without an inventory: every workspace, job, cluster policy, table, path, secret scope, model and group, and who depends on each. Three tools do most of the work.

  • UCX, the Unity Catalog migration assistant from Databricks Labs. Its assessment workflow “retrieves, or crawls, details of workspace assets and securable objects in the Hive metastore” and stores the findings in an inventory database with a dashboard. Note the support position on its README: Databricks Labs projects “are not formally supported by Databricks with Service Level Agreements (SLAs)”. You install it with databricks labs install ucx and start the assessment with databricks labs ucx ensure-assessment-run (I have not run these on Acme’s estate).
  • System tables, the read-only tables in the system catalog where Databricks records usage, lineage and job history (system tables reference). They answer the two questions UCX does not: which jobs cost the most, and which ones read storage paths directly instead of tables.
  • The Terraform resource exporter, part of the Databricks Terraform provider, which “generates *.tf files for Databricks resources and import.sh to run import state” (experimental resource exporter). It is labelled experimental, and its guide asks you to review every generated file.

These two queries, run on the source workspace, find the work that the storage-path change will create. The column names come from the lineage and billing system table references.

-- Which notebooks, jobs and pipelines read s3:// paths directly? (last 90 days)
SELECT entity_type, entity_id, source_path, count(*) AS reads
FROM system.access.table_lineage
WHERE source_path LIKE 's3://%'
  AND event_date >= current_date() - INTERVAL 90 DAYS
GROUP BY ALL
ORDER BY reads DESC;

-- Which jobs used the most DBUs in the last 30 days?
SELECT usage_metadata.job_id, sum(usage_quantity) AS dbus
FROM system.billing.usage
WHERE usage_metadata.job_id IS NOT NULL
  AND usage_date >= current_date() - INTERVAL 30 DAYS
GROUP BY ALL
ORDER BY dbus DESC
LIMIT 20;

The first list is your code-change backlog. The second tells you which jobs deserve a careful benchmark on the target, because a DBU (Databricks Unit, the unit Databricks bills compute in) spent on a big job matters more than one spent on a small one. Here is what Acme’s assessment found:

ACME'S ESTATE ON AWS, FROM THE ASSESSMENT Item Count Example Lands as Workspaces 2 acme-supply-aws-prod, -dev 2 workspaces Unity Catalog catalogs 1 acme_supply (3 schemas) acme_supply Unity Catalog tables 46 supply_gold.supply_service same names Hive metastore tables 9 hive_metastore.legacy.orders_raw upgrade first Jobs 31 nightly_supply_service Lakeflow Jobs DBFS mounts 3 /mnt/raw on s3://acme-supply-raw external location Secret scopes 2 erp (the ERP API key) Databricks-backed Registered models 4 acme_supply.ml.otif_forecast same name Acme and these counts are fictional. The two highlighted rows block the move until they are fixed.

Acme's source estate on AWS before the move, as the assessment found it: two workspaces, one Unity Catalog catalog with 46 tables, nine tables still in the Hive metastore, 31 jobs, three DBFS mounts, two secret scopes and four registered models, with where each one lands on Google Cloud

The two highlighted rows are the blockers. Nine tables still sit in the Hive metastore, and three DBFS mounts are still in use. Neither exists in Acme’s new Google Cloud workspaces, so both are fixed on AWS, before anything moves.

Step 2: Design the target

Design is where the rest of this series pays off, because every design decision has a part of its own. Settle these before you build:

  • Projects and region: which projects hold the workspace, the network and the data, and in which region (Part 1).
  • Identity: how people sign in and how groups are provisioned from Microsoft Entra ID through SCIM (System for Cross-domain Identity Management, the standard for syncing users and groups) (Part 2).
  • Network: subnet sizes, Cloud NAT, Private Service Connect and VPC Service Controls (Part 3, and Part 4 for serverless).
  • Storage and catalogs: buckets, external locations and managed storage (Part 5), and the catalog and grant design (Part 6).
  • Tier: Premium or Enterprise, decided by the controls you need (Part 9).

My view: keep the catalog, schema and table names the same as on the source. Code that reads acme_supply.supply_gold.supply_service by name then moves without a single edit, and the storage-path change is confined to the places that still use paths.

Step 3: Build the landing zone

Build the target with Terraform, using two providers side by side: the Google provider for projects, the VPC, Cloud NAT and buckets, and the Databricks provider for the workspace, metastore, storage credential and external locations. Part 1 and Part 2 show the resources and how Terraform authenticates, and Part 2’s step-by-step list maps each step of a workspace deployment to its Terraform resource. The exporter output from step 1 helps here: it is a starting point for jobs, cluster policies and permissions, once the AWS-specific values are translated.

Step 4: Migrate in waves

Each wave moves data, metadata, code, jobs, models, secrets and grants, in roughly that order. The next sections take each one in turn.

Step 5: Validate and cut over

The last step proves that the target produces the same answers as the source, then moves consumers. It has its own section below.

How does the data move?

Copy whole buckets when you are moving a lot at once; share or read table by table when you want governance kept in Unity Catalog on both sides.

Think of a library that is moving across town. It can hire a removal van and move every shelf in one go, sorting the catalogue out at the other end. It can lend books one at a time through an inter-library loan, each one checked out and logged. Or the new library’s readers can be given a visitor’s card for the old building, and copy what they need. The van is Storage Transfer Service, the inter-library loan is OpenSharing, and the visitor’s card is a read-only S3 external location.

There are three documented routes for table data from AWS, and each suits a different case.

THREE WAYS TO MOVE THE DATA AWS s3://acme-supply- lake Unity Catalog metastore (eu-west-2) Google Cloud gs://acme-lake- supply Unity Catalog metastore (europe-west2) A Storage Transfer Service copies objects S3 to gs://; then register or DEEP CLONE B OpenSharing, D2D share in AWS, catalog in GCP; then CREATE TABLE AS SELECT C Read-only S3 location GCP metastore reads s3://; then CREATE TABLE AS SELECT Every route reads data out of AWS, so AWS data transfer charges apply to all three. The OpenSharing and S3 routes copy table by table; Storage Transfer Service copies whole buckets.

three documented routes for moving table data from AWS to Google Cloud: Storage Transfer Service copies the files from S3 to Cloud Storage; OpenSharing shares tables from the AWS metastore to the Google Cloud metastore, where CREATE TABLE AS SELECT copies them; a read-only S3 external location lets the Google Cloud workspace read the source files and copy them; all three end as managed tables in gs://acme-lake-supply

Route A: Storage Transfer Service. Storage Transfer Service is Google Cloud’s managed service for bulk copies, and it supports “Amazon S3, Azure Blob Storage, or Cloud Storage to Cloud Storage”, as well as on-premises file systems and HDFS (the Hadoop Distributed File System). It copies objects, so it moves whole buckets or prefixes at once, Delta tables included, and you register or clone the tables afterwards. Agentless transfers from S3 have no Storage Transfer Service charge, according to its pricing page; an optional Google-managed private network for S3 costs US$0.04 per GiB within the EU, on top of the usual network charges.

Route B: OpenSharing, Databricks to Databricks. OpenSharing, formerly Delta Sharing, is the Databricks sharing platform, and its Databricks-to-Databricks protocol shares tables with any Databricks user “regardless of account or cloud host”, as long as they use a Unity Catalog workspace. Acme creates a share in the AWS metastore and a recipient for the Google Cloud metastore; the Google Cloud workspace then sees the share as a read-only catalog, and CREATE TABLE ... AS SELECT (CTAS) writes a managed copy. This route moves table by table, keeps governance in Unity Catalog on both sides, and needs no bucket-level access. Part 8 covers OpenSharing in depth.

Route C: a read-only S3 external location. Databricks on Google Cloud “supports cross-platform read-only access to S3 buckets”. You create a storage credential from an AWS IAM role and an external location on the S3 bucket in the Google Cloud metastore, following Connect to an AWS S3 external location (read-only). The Google Cloud workspace can then read the source files and CTAS them into managed tables. It suits AWS sources only; there is no equivalent for Azure storage in that list.

All three routes read data out of AWS, and AWS charges for data transfer out. AWS’s own post Free data transfer out to internet when moving out of AWS says you “can contact AWS Support to ask for free DTO rates” for data being migrated, so ask before the first large copy.

A word on consistency. Storage Transfer Service copies objects one at a time, and a Delta commit is only valid if the data files it names are present. My view: copy while writers are paused, or finish with a final incremental transfer after the last write, so that no commit arrives without its files.

Which route? My order of questions, simplest first:

1. Many tables, whole buckets or prefixes, writers can pause?   -> A  Storage Transfer Service
2. Source is Databricks with Unity Catalog, you want table by
   table moves with no bucket access?                           -> B  OpenSharing, then CTAS
3. Source is S3 and you want to read the files in place
   before copying?                                              -> C  read-only S3 location
Azure source? Only A and B apply; C covers S3 only.

How does this apply to Acme? The next section follows Acme through Route A, the removal van: the files from s3://acme-supply-lake land in gs://acme-lake-supply first, and the catalogue is rebuilt behind them.

How do you hydrate the new metastore?

Copied files are not yet tables: the metastore has to be told where they are, who may read them and which ones it should own. The trap is to see the files arrive in the bucket and call the data migrated.

Back at the library, the removal van delivers boxes of books, but no reader can find one until the books are shelved, entered in the catalogue and given lending rules. The boxes are the copied Delta files, the catalogue entries are the external and managed tables in Unity Catalog, and the lending rules are the grants.

How does this apply to Acme? For Route A, the files arrive first and the metadata follows. These are the six steps, with Acme’s names:

SIX STEPS TO A FILLED METASTORE 1 Copy files Storage Transfer Service: s3://acme-supply-lake to gs://acme-lake-supply/stage 2 Map names and paths catalog acme_supply keeps its schema and table names; a list of old and new paths 3 Credential, locations storage credential acme_lake_cred; external location on the gs:// bucket 4 External tables CREATE TABLE ... LOCATION 'gs://...' for each copied external table directory 5 Managed tables DEEP CLONE each staged table into the catalog's managed storage 6 Validate row counts, checksums, grants and lineage; then drop the staging tables CLONE does not copy tags, and a new table starts with no grants. Replay both in step 6.

hydrating the Google Cloud Unity Catalog metastore in six steps: copy the files, map old paths and names to new ones, create the storage credential and external locations, register external tables on the gs:// paths, deep clone managed tables into managed storage, and validate counts, checksums, grants and lineage

Steps 3 to 5 in SQL look like this. I have not run these against Acme’s buckets; the syntax follows the external locations and CREATE TABLE CLONE references.

-- 3. Credential and external location (the credential is created in Catalog Explorer;
--    Databricks generates its Google service account, which you grant on the bucket)
CREATE EXTERNAL LOCATION acme_lake_supply
  URL 'gs://acme-lake-supply/'
  WITH (STORAGE CREDENTIAL acme_lake_cred);

-- 4. Register a copied table directory as a staging external table
CREATE TABLE acme_supply.stage.shipments
  LOCATION 'gs://acme-lake-supply/stage/silver/shipments';

-- 5. Deep clone it into the catalog's managed storage
CREATE TABLE acme_supply.supply_silver.shipments
  DEEP CLONE acme_supply.stage.shipments;

A deep clone copies a table’s data and metadata into a new, independent table. The clone documentation lists what it does not copy: “the table description, user-defined commit metadata, Delta Lake table history, and Unity Catalog properties, such as tags”. Streaming tables and materialized views cannot be cloned at all; their pipelines are redeployed and rebuild them on the target. Re-running CREATE OR REPLACE TABLE ... DEEP CLONE syncs a clone incrementally, which helps during the parallel run.

Before any of this, the Hive metastore tables must already be in Unity Catalog on the source. The upgrade guide offers the upgrade wizard and the SYNC command for external tables, and says that “you must use CLONE or CREATE TABLE AS SELECT (CTAS) if you want to copy existing Hive tables to Unity Catalog as managed tables”. UCX automates the same steps at scale.

What changes in the code and configuration?

Code that names tables moves untouched; code that names storage paths has to be rewritten. Picture a letter addressed “to Priya Shah, care of the Finance team”: it reaches her wherever the team moves. A letter addressed to “desk 4, third floor, 10 Old Street” goes to an empty desk the day after the office moves. Table names and Unity Catalog volumes are the first kind of address; s3://, abfss:// and /mnt/ paths are the second.

Most code changes are translations: the same idea under a different name. This cheat sheet covers the ones that touch Databricks on all three clouds. For security features, the Google Cloud versions are in Part 6 (keys), Part 3 (private connectivity) and Part 4 (perimeters).

THE SAME THING, THREE NAMES Area AWS Azure Google Cloud Storage path s3://bucket/path abfss://container@account .dfs.core.windows.net/path gs://bucket/path Bucket layout flat bucket ADLS Gen2, hierarchical namespace flat bucket; HNS is not supported for external locations UC storage credential IAM role managed identity (Access Connector) Google service account that Databricks creates Secret scopes Databricks-backed Azure Key Vault-backed or Databricks-backed Databricks-backed Auto Loader file events SNS + SQS Event Grid + Queue Storage Pub/Sub Streaming bus Kinesis, Amazon MSK Event Hubs Pub/Sub, Managed Service for Apache Kafka Orchestration outside Step Functions, Amazon MWAA Azure Data Factory Lakeflow Jobs or Cloud Composer Private connectivity AWS PrivateLink Azure Private Link Private Service Connect (Enterprise tier) Cluster block aws_attributes azure_attributes gcp_attributes Spot with fallback SPOT_WITH_FALLBACK SPOT_WITH_FALLBACK_AZURE PREEMPTIBLE_WITH_FALLBACK_GCP Memory-optimised node r5.2xlarge Standard_E8s_v3 n2-highmem-8 Node types: match vCPUs and memory, then benchmark (Part 1). r5.2xlarge and n2-highmem-8 are both 8 vCPU, 64 GB.

a translation cheat sheet for moving a Databricks workspace from AWS or Azure to Google Cloud: storage paths, bucket layout, Unity Catalog storage credentials, secret scopes, Auto Loader file events, streaming, orchestration, private connectivity, cluster attribute blocks, spot settings and an example node type

A few rows deserve a sentence each.

  • Paths. abfss is the URI scheme of the Azure Blob File System driver, in the form abfss://<container>@<account>.dfs.core.windows.net/<path> (Azure Databricks storage docs). Both it and s3:// become gs://. Better still, replace paths with table names or Unity Catalog volumes (governed folders of files, addressed as /Volumes/<catalog>/<schema>/<volume>/).
  • Bucket layout. ADLS Gen2 has a hierarchical namespace (HNS: real directories, with atomic renames). The Google Cloud equivalent exists, but the external locations page says “Google Cloud Storage hierarchical namespace (HNS) is not supported with external locations”. Map ADLS Gen2 to flat buckets (Part 5).
  • Secrets. On Azure, a secret scope can be backed by Azure Key Vault. The Google Cloud secrets page offers two places to keep secrets: workspace secret scopes, which Databricks stores for you, and secrets in Unity Catalog, “available in every workspace attached to the metastore”. So Key Vault-backed scopes are recreated as workspace secret scopes with the same names, or as Unity Catalog secrets, and the values re-entered. Part 6 covers the other options.
  • File events. In file notification mode, Auto Loader (the Databricks feature that incrementally ingests new files) subscribes to events from the bucket instead of listing it. The file notification page shows the services it uses: SNS and SQS on S3, Event Grid and Queue Storage on Azure, and Pub/Sub on Cloud Storage.
  • Orchestration. Lakeflow Jobs (formerly Databricks Workflows) runs Databricks tasks (Lakeflow Jobs); Cloud Composer, Google’s managed Apache Airflow (overview), replaces Step Functions, Amazon MWAA (Managed Workflows for Apache Airflow) or Azure Data Factory when the pipeline spans other systems.
  • Clusters. Each cloud has its own attributes block in a cluster definition. The Terraform cluster reference lists availability values of SPOT_WITH_FALLBACK on AWS, SPOT_WITH_FALLBACK_AZURE on Azure and PREEMPTIBLE_WITH_FALLBACK_GCP on Google Cloud. instance_profile_arn has no Google Cloud equivalent, because data access goes through Unity Catalog. Part 1 maps node types by shape and explains the gcp_attributes block.

How does this apply to Acme? Here is one of Acme’s ingestion notebooks, before and after. The first version uses the desk address, the second the name. The second version runs on any cloud, because it names tables and volumes instead of buckets.

# Before, on AWS: a DBFS mount and a path
orders = spark.read.format("delta").load("/mnt/raw/erp_orders")
erp_key = dbutils.secrets.get("erp", "api-key")

# After, on Google Cloud: a table name, a volume and the same secret scope name
orders = spark.read.table("acme_supply.supply_bronze.erp_orders")
erp_key = dbutils.secrets.get("erp", "api-key")

landing = "/Volumes/acme_supply/supply_bronze/landing/erp/"
(spark.readStream.format("cloudFiles")
    .option("cloudFiles.format", "json")
    .option("cloudFiles.useNotifications", "true")   # Pub/Sub on Cloud Storage
    .load(landing))

Workspace code moves with Git. Databricks Git folders connect a workspace folder to a repository, so the target workspace simply clones the same repository. Code that is not in Git can be moved with the Databricks CLI’s workspace export-dir and workspace import-dir commands (CLI commands). Jobs and pipelines are best redeployed from Declarative Automation Bundles (formerly Databricks Asset Bundles), which describe “Databricks resources such as jobs and pipelines as source files”; a bundle needs only a new target block and new node types. If there are no bundles yet, the Terraform exporter’s output from step 1 is the fallback.

How do models, grants and secrets move?

Models are exported and imported; grants and secrets do not travel at all: they are replayed or re-entered on the target. When you move house, the furniture goes in the van, but the keys do not: you change the locks and hand out new keys, and you write a new list of who may let themselves in once you know who your neighbours are. The furniture is the MLflow models and experiments, the new locks and keys are re-entered (ideally rotated) secrets, and the list is the grants, replayed once the groups exist.

How does this apply to Acme? Acme’s four registered models, two secret scopes and its grants each take one of those paths.

Models and experiments. MLflow Export Import is an open-source tool that copies “MLflow objects (runs, experiments or registered models) from one MLflow tracking server (Databricks workspace) to another”. It exports to an intermediate directory, then imports, and supports both the workspace model registry and the Unity Catalog model registry.

MLFLOW ASSETS: EXPORT, THEN IMPORT AWS workspace experiments and runs registered models artifacts in S3 Intermediate directory a local disk or a Cloud Storage bucket Google Cloud workspace experiments and runs registered models artifacts in Cloud Storage export import Afterwards: re-apply grants on models, and recreate serving endpoints that point at them. The tool is an open-source project on GitHub. Test it on a copy of the workspace first.

moving MLflow assets with mlflow-export-import: the tool exports experiments, runs and registered models with their artifacts from the AWS workspace to an intermediate directory, then imports them into the Google Cloud workspace, where artifacts land in Cloud Storage

Its bulk tools are export-all and import-all. A sketch, not run here:

# On the AWS side
export MLFLOW_TRACKING_URI=databricks
export DATABRICKS_HOST=https://<aws-workspace-url>
export-all --output-dir /tmp/mlflow-export

# On the Google Cloud side
export DATABRICKS_HOST=https://<gcp-workspace-url>
import-all --input-dir /tmp/mlflow-export

A registered model in Unity Catalog has a three-part name such as acme_supply.ml.otif_forecast, so if the target catalog and schema have the same names, the model can keep its name. Grants on it do not travel, and model serving endpoints, which belong to the source workspace, are recreated on the target.

Grants. A grant lives in the source metastore, so it is replayed on the target. The information schema views such as table_privileges list who holds which privilege on which table, which makes them a good input for a script that issues GRANT statements on the target. The groups must exist first: provision them into the new account through SCIM from Microsoft Entra ID (SCIM provisioning), and the UCX assessment needs matching account groups too.

Secrets. Recreate each scope with the same name, and re-enter the values from the system that issued them. This is also a good moment to rotate them.

How do you validate and cut over?

Validation is where migrations earn trust. My view: never cut over on the strength of a successful copy; cut over on the strength of the same answers.

A shop that installs a new till does the same thing. For a week, every sale goes through both the old till and the new one, and at closing time someone compares three numbers: how many sales, the total takings, and a fingerprint of the receipts. If all three match every night, including the busy Saturday, the old till goes. The two tills are the source and target, the week is the parallel run, and the three numbers are a row count, a sum and a sum of row hashes.

ONE WAVE, FROM COPY TO SWITCH-OFF Initial copy full data Sync new data only Parallel run compare daily Switch consumers move Freeze source read-only Decommission after sign-off Source on AWS is the system of record until the switch. Google Cloud is the system of record after it. The parallel run is long enough to cover every daily, weekly and month-end job at least once.

cutting over one wave: an initial copy, incremental syncs, a parallel run in which both platforms produce the same tables and are compared every day, a switch of consumers to Google Cloud, a freeze of the source and finally its decommissioning

The parallel run is the heart of it. During it, the source stays the system of record, the target runs the same jobs on the same inputs, and a comparison job checks the outputs every day. Run it long enough to cover every schedule at least once, including weekly and month-end jobs. Then switch consumers, make the source read-only, and decommission it after sign-off.

How does this apply to Acme? Acme compares its gold table, acme_supply.supply_gold.supply_service, which has one row per brand, region and week. A spot check of three rows, and a whole-table check per week:

SPOT CHECK: THREE ROWS OF ACME_SUPPLY.SUPPLY_GOLD.SUPPLY_SERVICE brand region week_start units_ordered units_delivered otif_pct AWS vs GCP Acme Pure Western Europe 2026-05-04 17,475 15,585 89.18 match Acme Pure Western Europe 2026-05-11 16,679 14,799 88.73 match Acme Pure Western Europe 2026-05-18 17,448 15,160 86.89 match WHOLE TABLE, PER WEEK week_start rows AWS rows GCP sum delivered AWS sum delivered GCP result 2026-05-04 48 48 702,114 702,114 match 2026-05-11 48 48 688,390 688,390 match 2026-05-18 48 47 695,872 681,105 differs One row per brand, region and week, owned by supply, in Databricks. Figures are fictional. The missing row: a late file landed in S3 after the last sync. Re-sync, then compare again.

validating supply_service after the copy: a spot check of three rows (brand Acme Pure, region Western Europe, three weeks) that match on both platforms, and a whole-table check per week that finds one missing row on Google Cloud for the week of 18 May 2026; supply_service has one row per brand, region and week and is owned by supply

The whole-table check is two aggregate queries, one on each side, compared by a small job. On the target it looks like this; the source runs the same query against its own copy.

SELECT week_start,
       count(*)             AS row_count,
       sum(units_delivered) AS sum_delivered,
       sum(hash(brand, region, units_ordered, units_delivered, otif_pct)) AS row_hash
FROM acme_supply.supply_gold.supply_service
WHERE week_start >= '2026-05-04'
GROUP BY week_start
ORDER BY week_start;

Counts catch missing rows, sums catch changed numbers, and a sum of row hashes catches a change in any column. In Acme’s case the week of 18 May was one row short, because a late file landed in S3 after the last sync. A re-sync fixed it, which is exactly the kind of gap a parallel run exists to find.

Performance belongs in validation too. Compare run times and DBUs for the top jobs from step 1 using system.lakeflow job tables on both sides, and expect to retune node types and cluster sizes for a few of them.

What do reference architectures look like?

The first is Acme after the move from AWS. Every name is one you have met in Parts 1 to 9; the AWS side is frozen, then switched off.

ACME AFTER THE MOVE Before: AWS eu-west-2 workspace s3:// buckets SNS + SQS instance profile frozen, then decommissioned GOOGLE CLOUD, EUROPE-WEST2 acme-net-host Shared VPC Cloud NAT PSC, if Enterprise (Part 3) acme-dbx-prod acme-prod-euw2-supply classic compute VMs on a Shared VPC subnet acme-lake gs://acme-lake-supply bronze, silver, gold Delta tables (Part 5) Unity Catalog metastore, europe-west2 catalog acme_supply; grants replayed (Part 6) Pub/Sub Auto Loader file events acme-analytics: BigQuery reads gold tables (Part 8) Gemini Enterprise asks questions of both sides Same names as Parts 1 to 9. Classic compute runs in acme-dbx-prod; the data lives in acme-lake. Code that reads tables by name moves unchanged; code that reads s3:// paths does not.

reference architecture for Acme after the move from AWS: in Google Cloud, the Shared VPC host project holds the network, Cloud NAT and Private Service Connect endpoints; acme-dbx-prod holds the workspace's classic compute; acme-lake holds the Delta tables in gs://acme-lake-supply; Unity Catalog in europe-west2 governs them; Pub/Sub carries file events; BigQuery and Gemini Enterprise read the gold tables

The gold tables end where the rest of the series put them: BigQuery reads them for marketing analysis, and Gemini Enterprise is where Acme’s people ask questions across both sides (Part 8).

The second starts from an on-premises Hadoop estate instead of another cloud: files in HDFS, an Oracle database that the business runs on, and a Hive metastore with the table definitions.

FROM AN ON-PREMISES HADOOP ESTATE ON-PREMISES HDFS files CSV, Parquet, logs Oracle database orders, changes Hive metastore table definitions MOVE Storage Transfer agents: HDFS to GCS Datastream CDC to Cloud Storage Hive metastore federation in UC DATABRICKS ON GCP Auto Loader files into bronze Lakeflow pipelines silver and gold Unity Catalog acme_supply SERVE Databricks SQL dashboards, Genie BigQuery reads gold (Part 8) Gemini Enterprise questions Underneath: Cloud Monitoring, Cloud Logging, IAM, and the network from Part 3 Federation is a bridge: queries work during the move, then tables are copied to Delta.

reference architecture for an on-premises Hadoop estate: HDFS files move to Cloud Storage with Storage Transfer Service agents, an Oracle database streams changes to Cloud Storage through Datastream, and Hive metastore federation exposes the old Hive tables in Unity Catalog during the move; Databricks on Google Cloud ingests with Auto Loader, builds bronze, silver and gold Delta tables with Lakeflow pipelines, and serves them to Databricks SQL, BigQuery and Gemini Enterprise; Cloud Monitoring and Cloud Logging run underneath

Each column uses a documented service.

  • Files: Storage Transfer Service supports transfers “from HDFS to Cloud Storage” through agents installed next to the cluster (overview).
  • Database changes: Datastream is Google Cloud’s change data capture (CDC) service, which reads a database’s change log and streams every insert, update and delete. It supports Oracle among its sources and can “write change event streams into Cloud Storage”, where Auto Loader picks it up.
  • Table definitions during the move: Unity Catalog catalog federation can federate to “external Hive metastores”, which Databricks lists “as a step in the migration path to Unity Catalog”. Users query the old tables through Unity Catalog while they are copied to Delta. Hive metastore federation has the details.
  • Pipelines: Auto Loader lands files in bronze tables, and Lakeflow pipelines (formerly Delta Live Tables) build silver and gold (Part 7).

For moving existing Spark and SQL code into Databricks in general, the Databricks migration overview is the starting point.

What can go wrong?

These are the problems I see most often, with the usual cause and the fix.

SYMPTOM, CAUSE, FIX Symptom Usual cause Fix dbutils.fs.mount fails new workspace (since 8 Oct 2026): no DBFS mounts external locations and volumes (no switch to turn mounts back on) Runtime 12.2 not offered runtimes before 13.3 LTS are a legacy feature, off by default upgrade the runtime on AWS first, then move hive_metastore is empty no Hive metastore in new accounts upgrade to Unity Catalog on AWS first (UCX, SYNC, CLONE) External location fails bucket has hierarchical namespace enabled use a flat bucket (Part 5) Key Vault scope rejected Key Vault-backed scopes exist only on Azure Databricks-backed scope with the same name Stream starts from zero checkpoint belongs to the old source path new checkpoint; plan the start offset or a backfill Copied table is broken files copied while a writer was committing pause writers; run a final incremental transfer Users see no tables groups or grants not created in the new account SCIM groups (Part 2), then replay grants Cluster fails on quota regional CPU quota for the machine family raise quota before the wave (Parts 1 and 9)

troubleshooting a migration to Databricks on Google Cloud: the symptom, the usual cause and the fix, for mounts, old runtimes, the Hive metastore, hierarchical namespace buckets, Key Vault scopes, streaming checkpoints, half-copied tables, missing grants and quotas

Behind the table, a handful of field lessons:

  • Legacy features first. Mounts, the Hive metastore and old runtimes are switched off in every new workspace. Fix them on the source, where you can test calmly, rather than discovering them during a wave.
  • Quotas before waves. Compute Engine CPU quota is per region and often per machine family (Part 9 lists the quotas and sizes them for a peak). A wave that doubles the cluster count needs the quota raised a week before, not on the day.
  • The network cannot be resized in place. A workspace’s VPC is fixed, and changing its IP range means a new subnet and a new network configuration (Part 1). Size subnets for the peak after all waves, not for the first one.
  • Regional features. Check the regional table for every feature the source uses. It also notes that “some regions do not support GPU instance types”.
  • Organisation policies. Domain restricted sharing, resource locations and the ban on service account keys all apply to the new projects (Part 1); the key ban matters for BigQuery federation, which still needs a key (Part 2).
  • DNS for Private Service Connect. If the target uses Private Service Connect, the workspace URL must resolve to the private endpoint (Part 3; Prerequisite 3 explains DNS itself).
  • Helpers outside the support contract. UCX’s README says Databricks Labs projects are “not formally supported by Databricks with Service Level Agreements (SLAs)”, and MLflow Export Import is an open-source project on GitHub. Pin the versions you test with, and test on a copy first.

What would I do now?

  1. Run the UCX assessment and the two system table queries on the source, and turn the results into a list of blockers (Hive metastore tables, mounts, old runtimes) and a list of code changes (storage paths).
  2. Fix the blockers on the source: upgrade to Unity Catalog, replace mounts with external locations and volumes, and move every job to Databricks Runtime 13.3 LTS or later.
  3. Build the Google Cloud landing zone with Terraform from Parts 1 to 6 (Part 2 maps each step to its resource), keeping catalog, schema and table names identical to the source.
  4. Pick one low-risk pipeline as the first wave, move it with the data route that suits it, and run it in parallel with the source until the comparison job is clean.
  5. Write down what the first wave taught you, raise quotas for the next one, and only then plan the rest.

What did we learn?

  • Databricks is the same platform on AWS, Azure and Google Cloud; a migration changes what sits underneath it: the account, the metastore, storage paths, identities, the network and billing.
  • A workspace cannot be moved between clouds, so a migration builds a new one and moves its contents: Delta data and code move easily, while Hive metastore tables, mounts and streaming checkpoints need real work.
  • Every Databricks workspace created since 8 October 2026 has no DBFS mounts, Hive metastore or runtimes before 13.3 LTS, so those dependencies are best removed on the source first.
  • Table data has three documented routes from AWS (Storage Transfer Service, OpenSharing and a read-only S3 external location), and every route pays AWS data transfer charges unless AWS waives them.
  • A five-step approach (assess, design, build, migrate, validate) run in waves, with a parallel run and a row-level comparison before cut-over, turns a risky move into a series of small, checked ones.

What comes next?

This is the last part, so here is the whole series, one line per post:

If you read one thing twice, make it Part 1: almost every question in a migration comes back to which project, which region and which network. Thank you for reading the series.

Sources

Opinions are my own. Every fact in this post comes from the public documentation linked above. Acme Consumer Brands and its data are fictional.