A Terraform configuration for deploying a secure Databricks workspace on Google Cloud Platform (GCP) using customer-managed VPC with Private Service Connect for private connectivity.
This deployment creates a secure, private Databricks workspace with:
Important: Private Service Connect (PSC) is a gated GA feature. You must enable it for your Databricks account first. Follow this guide.
graph TB
subgraph "GCP Project - Host/Shared VPC"
subgraph "Customer VPC"
SUBNET[Node Subnet<br/>Databricks Clusters]
PSC_SUBNET[PSC Subnet<br/>Private Endpoints]
subgraph "Private Service Connect"
FE_EP[Frontend PSC Endpoint<br/>Workspace UI & REST API]
BE_EP[Backend PSC Endpoint<br/>Cluster Relay]
FE_IP[Frontend Private IP]
BE_IP[Backend Private IP]
end
subgraph "Cloud DNS"
DNS_ZONE[Private DNS Zone<br/>gcp.databricks.com]
A_REC1[A Record: workspace-id.gcp.databricks.com]
A_REC2[A Record: dp-workspace-id.gcp.databricks.com]
A_REC3[A Record: region.psc-auth.gcp.databricks.com]
A_REC4[A Record: tunnel.region.gcp.databricks.com]
end
end
end
subgraph "GCP Project - Service/Consumer"
subgraph "Databricks Managed"
GKE[GKE Cluster<br/>Control Plane Components]
GCS[GCS Bucket<br/>DBFS Storage]
end
end
subgraph "Databricks Control Plane - Private"
FE_SA[Frontend Service Attachment<br/>plproxy-psc-endpoint]
BE_SA[Backend Service Attachment<br/>ngrok-psc-endpoint]
end
subgraph "Users"
USER[Workspace Users<br/>Via VPN/Private Network]
end
SUBNET --> FE_EP
SUBNET --> BE_EP
FE_EP --> FE_IP
BE_EP --> BE_IP
FE_IP -.PSC Connection.-> FE_SA
BE_IP -.PSC Connection.-> BE_SA
FE_SA --> GKE
BE_SA --> GKE
GKE --> SUBNET
SUBNET --> GCS
DNS_ZONE --> A_REC1
DNS_ZONE --> A_REC2
DNS_ZONE --> A_REC3
DNS_ZONE --> A_REC4
A_REC1 --> FE_IP
A_REC2 --> FE_IP
A_REC3 --> FE_IP
A_REC4 --> BE_IP
USER -.DNS Lookup.-> DNS_ZONE
USER -.Private Access.-> FE_EP
style FE_SA fill:#FF3621
style BE_SA fill:#FF3621
style GCS fill:#4285F4
style GKE fill:#4285F4
style DNS_ZONE fill:#FBBC04
style FE_EP fill:#34A853
style BE_EP fill:#34A853
This configuration does NOT include:
For these features, see:
../byovpc-psc-cmek-ws/../end2end/../infra4db/https://accounts.gcp.databricks.comautomation-sa@project.iam.gserviceaccount.com)Critical: You must request PSC enablement for your account. Follow Step 1 in the PSC documentation.
This configuration requires a pre-existing VPC with appropriate subnets:
Required Subnets:
/24 CIDR (251 usable IPs)/28 CIDR (11 usable IPs)To create this infrastructure, use ../infra4db/ first.
The service account needs these IAM roles:
On Service/Consumer Project:
roles/compute.networkAdminroles/iam.serviceAccountAdminroles/resourcemanager.projectIamAdminroles/storage.adminOn Host/Shared VPC Project:
roles/compute.networkUserroles/compute.securityAdminroles/dns.admin (for private DNS zone creation)You need to know the Databricks PSC service attachment URIs for your region. These are provided by Databricks.
Format:
Frontend: projects/prod-gcp-<region>/regions/<region>/serviceAttachments/plproxy-psc-endpoint-all-ports
Backend: projects/prod-gcp-<region>/regions/<region>/serviceAttachments/ngrok-psc-endpoint
Find Service Attachments: Databricks Supported Regions - PSC
gcloud CLI) configuredSince the workspace uses private connectivity, you need:
Private Service Connect (PSC) enables private connectivity between your VPC and Databricks services without exposing traffic to the public internet.
Databricks publishes two service attachments per region:
| Service Attachment | Purpose | Traffic Type |
|---|---|---|
| plproxy-psc-endpoint | Frontend endpoint | REST API, Web UI, Notebook access |
| ngrok-psc-endpoint | Backend endpoint | Cluster-to-control-plane relay |
You create two PSC endpoints in your VPC:
| Endpoint | Connects To | Private IP Source | DNS Records |
|---|---|---|---|
| Frontend (workspace) | plproxy service attachment | PSC subnet | 3 A records (workspace URL, dp- prefix, psc-auth) |
| Backend (relay) | ngrok service attachment | PSC subnet | 1 A record (tunnel) |
Each PSC endpoint gets a private IP from your PSC subnet:
Frontend PSC Endpoint IP: 10.1.0.5 (example)
Backend PSC Endpoint IP: 10.1.0.6 (example)
Private DNS zone resolves workspace domains to private IPs:
8296020533331897.1.gcp.databricks.com → 10.1.0.5 (Frontend IP)
dp-8296020533331897.1.gcp.databricks.com → 10.1.0.5 (Frontend IP)
us-central1.psc-auth.gcp.databricks.com → 10.1.0.5 (Frontend IP)
tunnel.us-central1.gcp.databricks.com → 10.1.0.6 (Backend IP)
sequenceDiagram
participant User as User Browser
participant DNS as Cloud DNS
participant FE as Frontend PSC Endpoint
participant BE as Backend PSC Endpoint
participant DB as Databricks Control Plane
participant Cluster as Databricks Cluster
Note over User,DNS: User Access Flow
User->>DNS: Resolve workspace-id.gcp.databricks.com
DNS-->>User: Returns Frontend Private IP (10.1.0.5)
User->>FE: HTTPS to 10.1.0.5
FE->>DB: Via PSC to Frontend Service Attachment
DB-->>FE: Response
FE-->>User: Workspace UI
Note over Cluster,BE: Cluster Communication Flow
Cluster->>DNS: Resolve tunnel.region.gcp.databricks.com
DNS-->>Cluster: Returns Backend Private IP (10.1.0.6)
Cluster->>BE: Connect to 10.1.0.6:6666
BE->>DB: Via PSC to Backend Service Attachment
DB-->>BE: Control plane commands
BE-->>Cluster: Relay traffic
For each workspace, you need 4 A records:
<workspace-id>.gcp.databricks.com → Frontend IPdp-<workspace-id>.gcp.databricks.com → Frontend IP<region>.psc-auth.gcp.databricks.com → Frontend IPtunnel.<region>.gcp.databricks.com → Backend IPNote: Per-region records can be shared across multiple workspaces in the same region.
This deployment uses four Terraform providers:
provider "google" {
project = var.google_project_name
region = var.google_region
}
provider "google" {
alias = "vpc_project"
project = var.google_shared_vpc_project
region = var.google_region
}
Required for PSC endpoint creation:
provider "google-beta" {
project = var.google_shared_vpc_project
region = var.google_region
}
Account Provider:
provider "databricks" {
alias = "accounts"
host = "https://accounts.gcp.databricks.com"
google_service_account = var.google_service_account_email
}
Workspace Provider:
provider "databricks" {
alias = "workspace"
host = databricks_mws_workspaces.databricks_workspace.workspace_url
google_service_account = var.google_service_account_email
}
google_vpc_idgoogle_shared_vpc_projectnode_subnet/24 (251 IPs)google_pe_subnet/28 (11 IPs)Required egress rules from node subnet:
# Allow to PSC endpoints
Source: Node subnet
Destination: PSC subnet
Protocols: TCP 443, 6666, 8443-8451
# Allow to GCP APIs
Source: Node subnet
Destination: 0.0.0.0/0
Protocols: TCP 443
# Allow internal cluster communication
Source: Node subnet
Destination: Node subnet
Protocols: TCP/UDP (all ports)
resource "google_compute_forwarding_rule" "frontend_psc_ep"
resource "google_compute_address" "frontend_pe_ip_address"
Purpose: Private connection to Databricks REST API and web UI
Configuration:
resource "google_compute_forwarding_rule" "backend_psc_ep"
resource "google_compute_address" "backend_pe_ip_address"
Purpose: Private connection for cluster relay traffic
Configuration:
resource "databricks_mws_vpc_endpoint" "relay_vpce"
resource "databricks_mws_vpc_endpoint" "workspace_vpce"
Registers PSC endpoints with Databricks account
resource "databricks_mws_private_access_settings" "pas"
Key Configuration:
public_access_enabled = true # or false for fully private
private_access_level = "ACCOUNT" # or "ENDPOINT"
Options Explained:
| Setting | Value | Meaning |
|---|---|---|
public_access_enabled |
true |
Users can access from internet (subject to IP access lists) |
public_access_enabled |
false |
Only private network access allowed |
private_access_level |
ACCOUNT |
Any VPC endpoints in account can access |
private_access_level |
ENDPOINT |
Only specified VPC endpoints can access |
Warning:
public_access_enabledcannot be changed after creation.
resource "databricks_mws_networks" "databricks_network"
Associates:
resource "databricks_mws_workspaces" "databricks_workspace"
Creates:
resource "databricks_workspace_conf" "this"
resource "databricks_ip_access_list" "this"
Enables:
resource "google_dns_managed_zone" "databricks-private-zone"
Configuration:
gcp.databricks.comFour A records are created automatically:
resource "google_dns_record_set" "record_set_workspace_url"
Format: <workspace-id>.gcp.databricks.com
Points to: Frontend PSC endpoint IP
Purpose: Main workspace URL
resource "google_dns_record_set" "record_set_workspace_dp"
Format: dp-<workspace-id>.gcp.databricks.com
Points to: Frontend PSC endpoint IP
Purpose: Dataplane API access
resource "google_dns_record_set" "record_set_workspace_psc_auth"
Format: <region>.psc-auth.gcp.databricks.com
Points to: Frontend PSC endpoint IP
Purpose: PSC authentication
resource "google_dns_record_set" "record_set_relay"
Format: tunnel.<region>.gcp.databricks.com
Points to: Backend PSC endpoint IP
Purpose: Cluster relay traffic
sequenceDiagram
participant TF as Terraform
participant GCP as Google Cloud
participant DB_ACC as Databricks Account
participant DB_WS as Databricks Workspace
Note over TF,GCP: Phase 1: PSC Endpoints
TF->>GCP: Allocate Frontend Private IP
TF->>GCP: Allocate Backend Private IP
TF->>GCP: Create Frontend PSC Endpoint
TF->>GCP: Create Backend PSC Endpoint
GCP->>GCP: Connect to Databricks Service Attachments
GCP-->>TF: PSC Endpoints Ready (ACCEPTED status)
Note over TF,DB_ACC: Phase 2: VPC Endpoint Registration
TF->>DB_ACC: Register Frontend VPC Endpoint
TF->>DB_ACC: Register Backend VPC Endpoint
DB_ACC-->>TF: VPC Endpoint IDs
Note over TF,DB_ACC: Phase 3: Private Access Configuration
TF->>DB_ACC: Create Private Access Settings
DB_ACC-->>TF: Private Access Settings ID
Note over TF,DB_ACC: Phase 4: Network & Workspace
TF->>DB_ACC: Create Network Configuration
TF->>DB_ACC: Create Workspace
DB_ACC->>GCP: Deploy GKE Cluster
DB_ACC->>GCP: Create GCS Bucket
GCP-->>DB_ACC: Resources Ready
DB_ACC-->>TF: Workspace URL + ID
Note over TF,GCP: Phase 5: DNS Configuration
TF->>GCP: Create Private DNS Zone
TF->>GCP: Create A Records (workspace URL)
TF->>GCP: Create A Records (dp- prefix)
TF->>GCP: Create A Records (psc-auth)
TF->>GCP: Create A Records (tunnel)
GCP-->>TF: DNS Configured
Note over TF,DB_WS: Phase 6: Workspace Configuration
TF->>DB_WS: Enable IP Access Lists
TF->>DB_WS: Configure IP Allowlist
TF->>DB_WS: Create Admin User
TF->>DB_WS: Add User to Admins Group
DB_WS-->>TF: Configuration Complete
Note over DB_WS: Workspace Ready (Private Access)
graph TD
A[Start] --> B[Create PSC Private IPs]
B --> C[Create PSC Endpoints]
C --> D{PSC Status = ACCEPTED?}
D -->|Yes| E[Register VPC Endpoints with Databricks]
D -->|No| C
E --> F[Create Private Access Settings]
F --> G[Create Network Configuration]
G --> H[Create Workspace]
H --> I[Wait for Workspace Ready]
I --> J[Create Private DNS Zone]
J --> K[Create DNS A Records]
K --> L[Enable IP Access Lists]
L --> M[Create Admin User]
M --> N[Workspace Ready]
style A fill:#4285F4
style N fill:#34A853
style H fill:#FF3621
style D fill:#FBBC04
Edit providers.auto.tfvars:
# Service Account for authentication
google_service_account_email = "automation-sa@my-service-project.iam.gserviceaccount.com"
# Service/Consumer Project
google_project_name = "my-service-project"
# Host/Shared VPC Project
google_shared_vpc_project = "my-host-project"
# Region
google_region = "us-central1"
Edit workspace.auto.tfvars:
# Databricks Configuration
databricks_account_id = "12345678-1234-1234-1234-123456789abc"
databricks_account_console_url = "https://accounts.gcp.databricks.com"
databricks_workspace_name = "my-psc-workspace"
databricks_admin_user = "admin@mycompany.com"
# Network Configuration
google_vpc_id = "my-vpc-network"
node_subnet = "databricks-node-subnet"
google_pe_subnet = "databricks-psc-subnet"
# PSC Endpoint Names (must be unique)
workspace_pe = "us-c1-frontend-ep"
relay_pe = "us-c1-backend-ep"
# PSC Private IP Names
workspace_pe_ip_name = "frontend-pe-ip"
relay_pe_ip_name = "backend-pe-ip"
# PSC Service Attachments (region-specific)
# Find yours at: https://docs.gcp.databricks.com/resources/supported-regions.html#psc
workspace_service_attachment = "projects/prod-gcp-us-central1/regions/us-central1/serviceAttachments/plproxy-psc-endpoint-all-ports"
relay_service_attachment = "projects/prod-gcp-us-central1/regions/us-central1/serviceAttachments/ngrok-psc-endpoint"
# DNS Configuration
private_zone_name = "databricks"
dns_name = "gcp.databricks.com." # Trailing dot required
Before deployment:
dns.admin)gcloud config set auth/impersonate_service_account automation-sa@project.iam.gserviceaccount.com
export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
cd gcp/gh-repo/gcp/terraform-scripts/byovpc-psc-ws
terraform init
terraform plan
Expected Resources (~20-25 resources):
terraform apply
Deployment Time: ~15-20 minutes
Progress:
terraform output
Check PSC status:
Frontend psc status: ACCEPTED
Backend psc status: ACCEPTED
Important: If status is not “ACCEPTED”, PSC connection failed. Check service attachment URIs.
nslookup <workspace-id>.gcp.databricks.com
| Output | Description |
|---|---|
workspace_url |
Workspace URL (resolves to private IP) |
front_end_psc_status |
Frontend PSC connection status (should be ACCEPTED) |
backend_end_psc_status |
Backend PSC connection status (should be ACCEPTED) |
extracted_value |
Workspace ID extracted from URL |
ingress_firewall_enabled |
IP access list enabled status |
ingress_firewall_ip_allowed |
Allowed IP addresses |
Error:
Frontend psc status: PENDING
Backend psc status: PENDING
Causes:
Solution:
gcloud compute forwarding-rules describe frontend-ep \
--region=us-central1 \
--project=my-host-project
Error: Workspace URL doesn’t resolve or resolves to public IP
Solution:
gcloud dns managed-zones list --project=my-host-project
gcloud dns record-sets list \
--zone=databricks \
--project=my-host-project
nslookup <workspace-id>.gcp.databricks.com
*.gcp.databricks.com to Cloud DNS IPError: Workspace URL times out or connection refused
Causes:
Solution:
# Should show private IP
ping <workspace-id>.gcp.databricks.com
curl ifconfig.me
# Verify this IP is in workspace.auto.tfvars ip_addresses list
telnet <frontend-private-ip> 443
Error: Clusters fail to start with “Unable to connect to control plane”
Causes:
Solution:
terraform output backend_end_psc_status
nslookup tunnel.us-central1.gcp.databricks.com
# Should resolve to backend private IP
telnet <backend-private-ip> 6666
Error:
Error: Not enough IP addresses available in subnet
Solution:
You only need 2 IPs for frontend and backend endpoints. If this fails:
gcloud compute networks subnets describe psc-subnet \
--region=us-central1 \
--project=my-host-project
gcloud compute networks subnets expand-ip-range psc-subnet \
--region=us-central1 \
--prefix-length=28 \
--project=my-host-project
Error: Admin user cannot log in after workspace creation
Solution:
curl ifconfig.me
workspace.auto.tfvars:
ip_addresses = [
"YOUR.CURRENT.IP/32",
"0.0.0.0/0" # Temporary allow all
]
terraform apply -target=databricks_ip_access_list.this
# Check PSC endpoint status
gcloud compute forwarding-rules describe frontend-ep \
--region=us-central1 --project=my-host-project
# Verify DNS records
gcloud dns record-sets list --zone=databricks --project=my-host-project
# Test DNS resolution
nslookup <workspace-id>.gcp.databricks.com
# Check PSC connection from Terraform
terraform state show google_compute_forwarding_rule.frontend_psc_ep
terraform state show google_compute_forwarding_rule.backend_psc_ep
# View all outputs
terraform output -json | jq
To destroy all resources:
terraform destroy
Destruction Order:
Time: ~10-15 minutes
After deploying your PSC-enabled workspace:
../byovpc-psc-cmek-ws/ for PSC + CMEK configuration../uc/ for Unity Catalog setup../end2end/ for complete workspace with Unity CatalogThis configuration is provided as a reference implementation for deploying Databricks workspaces with Private Service Connect on GCP.