From 2bfd9bdd92980a5a53efa7eab0c73447eb54102f Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 13:52:06 +0200 Subject: [PATCH 01/14] docs: add design spec for GCP modules refactor Captures the brainstormed design for refactoring GCP-related Terraform examples and modules: a single composer module under modules/gcp/ that conditionally instantiates submodules (network, private-connectivity, account, dns), with each example becoming a thin caller that varies only scenario inputs. Co-authored-by: Isaac --- .../2026-05-14-gcp-modules-refactor-design.md | 355 ++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-14-gcp-modules-refactor-design.md diff --git a/docs/superpowers/specs/2026-05-14-gcp-modules-refactor-design.md b/docs/superpowers/specs/2026-05-14-gcp-modules-refactor-design.md new file mode 100644 index 0000000..bf70ad3 --- /dev/null +++ b/docs/superpowers/specs/2026-05-14-gcp-modules-refactor-design.md @@ -0,0 +1,355 @@ +# GCP Modules Refactor — Design Spec + +**Date:** 2026-05-14 +**Author:** Michele Daddetta +**Status:** Approved (pending implementation plan) + +## Problem + +Today the repo ships six GCP modules and six GCP example directories. Each example wraps its own dedicated module: + +- `examples/gcp-basic` → `modules/gcp-workspace-basic` +- `examples/gcp-byovpc` → `modules/gcp-workspace-byovpc` +- `examples/gcp-with-psc-exfiltration-protection` → `modules/gcp-with-psc-exfiltration-protection` +- `examples/gcp-sa-provisioning` → `modules/gcp-sa-provisioning` +- `examples/gcp-test-modules` (orphan, contains only state files) +- `examples/gcp-sa-provisionning` (typo dir, contains only a Makefile) + +The three workspace modules duplicate `databricks_mws_workspaces`, `databricks_mws_networks`, `google_compute_network` + subnet + router + NAT, and `random_string.suffix`. A change to any shared piece (e.g. a new GCP region added to the regional PSC service-attachment map, a workspace argument added by the Databricks provider) needs to land in 2–3 places. + +The user's northstar: an example provides only the **basic information about the desired scenario** — does a VPC already exist, what's its name, is the workspace using frontend PrivateLink, is private access enforced — and the module figures out the rest. + +There is no "existing VPC" example today; we add one as part of this refactor. + +## Goals + +1. Eliminate cross-module duplication for GCP workspace deployment. +2. Single top-level composer that takes scenario inputs and conditionally instantiates submodules. +3. Submodules are organized by concern (network, private connectivity, Databricks account resources) and consumed only by the composer. +4. Each example becomes a thin caller — main.tf is ~20 lines and varies only the inputs that matter for that scenario. +5. Variable names describe what they do, not what they protect against. No marketing language. +6. New scenario "existing VPC" is supported on day one. +7. Old modules and examples remain functional during migration; we ship the new modules alongside and migrate one example per PR. + +## Non-Goals + +- No Unity Catalog redesign. UC remains a separate module that the example wires up directly. The user has separate work in flight for this area. +- No service-account-provisioning redesign. SA-provisioning is a one-time bootstrap with a different lifecycle than the workspace; it remains a separate module called by its own example. +- No state migration tooling for existing applies of old examples. Users re-apply on clean state. +- No new CI test harness (terratest, GitHub Actions matrix). We rely on the existing `pre-commit` config plus per-PR manual sandbox apply. +- No changes to AWS or Azure modules. + +## Architecture + +### Module layout + +``` +modules/gcp/ +├── databricks-workspace/ # top-level composer; the one examples call +├── network/ # all google_compute_network/subnet/router/nat/peering +│ # for both hub & spoke; shared-VPC host/service binding +├── private-connectivity/ # GCP-side: PSC subnet + addresses + forwarding rules +│ # + egress firewall rules (deny-egress, google-apis, ctl-plane, hive) +├── account/ # ALL databricks_mws_* resources: +│ # mws_networks + mws_workspaces + mws_vpc_endpoint +│ # + mws_private_access_settings +├── dns/ # private DNS zones + records (hub + spoke) +│ # split from private-connectivity because DNS needs workspace_url +│ # which is only available after account creates the workspace +├── service-account/ # relocated from modules/gcp-sa-provisioning (git mv) +└── unity-catalog/ # relocated from modules/gcp-unity-catalog (git mv) +``` + +The five-submodule split (rather than the three-submodule grouping originally discussed) is required to keep the dependency graph acyclic. `account` cannot live before `private-connectivity` because `databricks_mws_vpc_endpoint` references the PSC forwarding rules created in GCP. `dns` cannot live before `account` because DNS records embed the `workspace_dns_id` regex-extracted from `databricks_mws_workspaces.workspace_url`. Keeping all `databricks_mws_*` resources together in `account` (the user's chosen concern-based grouping) requires DNS to be its own submodule. + +### Data flow + +``` +example + └── modules/gcp/databricks-workspace (composer) + ├── modules/gcp/network (count = vpc_source != "databricks_managed" ? 1 : 0) + │ outputs: spoke_vpc_*, spoke_subnet_*, hub_vpc_* (nullable), nat_id (nullable) + │ + ├── modules/gcp/private-connectivity (count = any private_link_* flag is true ? 1 : 0) + │ consumes: network outputs + │ outputs: frontend_psc_fr_id, backend_psc_fr_id, hub_frontend_psc_fr_id (nullable), + │ frontend_psc_ip_spoke, backend_psc_ip_spoke, frontend_psc_ip_hub (nullable), + │ psc_subnet_self_link + │ + ├── modules/gcp/account (always) + │ consumes: network outputs + private-connectivity outputs + │ outputs: workspace_id, workspace_url, network_id (nullable), + │ frontend_endpoint_id, backend_endpoint_id, transit_endpoint_id (nullable) + │ + └── modules/gcp/dns (count = restricted_egress ? 1 : 0) + consumes: network outputs + private-connectivity PSC IPs + account.workspace_url + outputs: none + +example optionally also calls: + └── modules/gcp/unity-catalog (wired with workspace_id + workspace_url) +``` + +The composer declares `random_string.suffix` once and passes it to each submodule, eliminating the per-module duplication that exists today. + +The dependency graph is linear: `network → private-connectivity → account → dns`. No back-references between modules. `databricks_mws_vpc_endpoint` is created inside `account` (rather than `private-connectivity`) so that `account` owns every `databricks_mws_*` resource and so that the cycle "`account` needs endpoint IDs / DNS needs workspace_url" is decomposed into a linear chain. + +## Composer API + +```hcl +# === Identity ============================================================= +prefix : string required +databricks_account_id : string required +google_project : string required # workspace google project +google_region : string required +workspace_name : string default = null # default "${prefix}-ws-${suffix}" +tags : map default = {} + +# === Where does the VPC come from? ======================================= +vpc_source : string default = "databricks_managed" + # one of: "databricks_managed", "create", "existing" + +# Used when vpc_source = "create" +spoke_vpc_cidr : string default = null +subnet_cidr : string default = null +pod_cidr : string default = null # GKE secondary range +svc_cidr : string default = null # GKE secondary range + +# Used when vpc_source = "existing" +existing_vpc_name : string default = null +existing_subnet_name : string default = null + +# === Connectivity (orthogonal flags, each defaults false) ================ +private_link_frontend : bool default = false # frontend PSC endpoint + frontend mws_vpc_endpoint +private_link_backend : bool default = false # SCC PSC endpoint + backend mws_vpc_endpoint +private_access_only : bool default = false # mws_private_access_settings; public_access_enabled = false +restricted_egress : bool default = false # hub VPC + deny-egress firewall + private DNS + +# === Required when restricted_egress = true ============================== +hub_vpc_google_project : string default = null +spoke_vpc_google_project : string default = null # falls back to google_project +is_spoke_vpc_shared : bool default = false +hub_vpc_cidr : string default = null +psc_subnet_cidr : string default = null +hive_metastore_ip : string default = null # else looked up via internal regional map +``` + +### Composer outputs + +```hcl +workspace_id = module.account.workspace_id +workspace_url = module.account.workspace_url +network_id = module.account.network_id # null when vpc_source = "databricks_managed" +vpc_id = try(module.network[0].spoke_vpc_id, null) +spoke_vpc_id = try(module.network[0].spoke_vpc_id, null) +hub_vpc_id = try(module.network[0].hub_vpc_id, null) +suffix = random_string.suffix.result # useful for downstream modules (UC, etc.) +``` + +### Cross-variable validation (preconditions in composer's `main.tf`) + +| Rule | Reason | +|------|--------| +| `restricted_egress = true` ⇒ `vpc_source = "create"` | Hub-spoke + egress firewall + private DNS require the module to own both VPCs | +| `restricted_egress = true` ⇒ `private_link_frontend OR private_link_backend = true` | Egress-restricted workspace without PSC is unreachable | +| `restricted_egress = true` ⇒ `hub_vpc_google_project`, `hub_vpc_cidr`, `psc_subnet_cidr` set | Hub topology needs these | +| `vpc_source = "create"` ⇒ `spoke_vpc_cidr`, `subnet_cidr` set | Need CIDRs | +| `vpc_source = "existing"` ⇒ `existing_vpc_name`, `existing_subnet_name` set | Need names to look up | +| `vpc_source = "databricks_managed"` ⇒ `private_link_frontend`, `private_link_backend`, `restricted_egress` all false | Cannot attach PSC or firewalls to a VPC we don't own | + +## Submodule contracts + +### `modules/gcp/network` + +**Inputs:** `prefix`, `suffix`, `google_region`, `vpc_source`, `spoke_vpc_google_project`, `spoke_vpc_cidr`, `subnet_cidr`, `subnet_name`, `pod_cidr`, `svc_cidr`, `existing_vpc_name`, `existing_subnet_name`, `create_hub` (bool — composer passes `restricted_egress`), `hub_vpc_google_project`, `hub_vpc_cidr`, `is_spoke_vpc_shared`, workspace project. + +**Behavior:** Spoke VPC + subnet + router + NAT (when `vpc_source = "create"`) or `data` lookups (when `"existing"`). Optional hub VPC + subnet + bidirectional peering + optional shared-VPC host/service binding (when `create_hub`). + +**Outputs:** `spoke_vpc_id`, `spoke_vpc_name`, `spoke_vpc_self_link`, `spoke_subnet_id`, `spoke_subnet_name`, `spoke_subnet_self_link`, `hub_vpc_id` (nullable), `hub_vpc_name` (nullable), `hub_vpc_self_link` (nullable), `hub_subnet_name` (nullable), `nat_id` (nullable). + +### `modules/gcp/private-connectivity` + +**Inputs:** `prefix`, `suffix`, `google_region`, spoke VPC refs + project, hub VPC refs + project (nullable), `enable_frontend`, `enable_backend`, `restrict_egress`, `psc_subnet_cidr`, spoke CIDR (for firewall source ranges), hub CIDR (for hub ingress firewall), `hive_metastore_ip` (nullable; falls back to regional map keyed by `google_region`). + +**Behavior, file-organized:** +- `psc.tf`: PSC subnet (in spoke); frontend address + forwarding rule when `enable_frontend`; backend address + forwarding rule when `enable_backend`; hub-side frontend address + forwarding rule when hub exists AND `enable_frontend`. Owns the regional PSC service-attachment maps (`google_frontend_psc_targets` and `google_backend_psc_targets`). +- `firewall.tf`: when `restrict_egress`, creates spoke deny-egress (priority 1100) + allow-google-apis + allow-databricks-control-plane (targeting PSC IPs) + allow-managed-hive (using regional `hive_metastore_ip`); hub ingress from spoke CIDR. + +`databricks_mws_vpc_endpoint` resources are NOT created here — they live in `account` so that all `databricks_mws_*` resources are colocated and so the dependency graph stays linear. + +**Outputs:** `psc_subnet_self_link`, `frontend_psc_fr_id` (forwarding-rule name; nullable), `backend_psc_fr_id` (nullable), `hub_frontend_psc_fr_id` (nullable), `frontend_psc_ip_spoke`, `backend_psc_ip_spoke`, `frontend_psc_ip_hub` (nullable). + +### `modules/gcp/account` + +**Inputs:** `prefix`, `suffix`, `workspace_name`, `databricks_account_id`, `google_project`, `google_region`, `vpc_source`, spoke VPC name, spoke subnet name, spoke project, hub project (nullable), `frontend_psc_fr_id` (nullable), `backend_psc_fr_id` (nullable), `hub_frontend_psc_fr_id` (nullable), `enable_frontend`, `enable_backend`, `private_access_only`, `nat_dependency` (passes through `module.network[0].nat_id`). + +**Behavior:** +- `databricks_mws_vpc_endpoint` resources (frontend, backend, hub-transit) emitted with `count = 1` gated by the corresponding `enable_*` and forwarding-rule-id inputs. Each references the GCP forwarding rule by name and project. +- `databricks_mws_networks` emitted when `vpc_source != "databricks_managed"`. The `vpc_endpoints` block is populated only when both frontend and backend endpoints exist. +- `databricks_mws_workspaces` always emitted. Single resource with conditional attributes: + - `network_id` = `databricks_mws_networks.this.network_id` when `vpc_source != "databricks_managed"`, else null + - `private_access_settings_id` = `databricks_mws_private_access_settings.this.id` when `private_access_only`, else null + - `depends_on = [nat_dependency]` to make sure NAT is ready before workspace creation +- `databricks_mws_private_access_settings` emitted with `count = 1` when `private_access_only`; sets `public_access_enabled = false` and `private_access_level = "ACCOUNT"`. + +**Outputs:** `workspace_id`, `workspace_url`, `network_id` (nullable), `frontend_endpoint_id` (nullable), `backend_endpoint_id` (nullable), `transit_endpoint_id` (nullable). + +### `modules/gcp/dns` + +**Inputs:** `prefix`, `google_region`, hub VPC refs + project, spoke VPC refs + project, `workspace_url` (from `module.account`), `frontend_psc_ip_spoke`, `frontend_psc_ip_hub` (nullable), `backend_psc_ip_spoke`. + +**Behavior:** +- Hub-side: `gcp.databricks.com` zone with `workspace`, `psc-auth`, `dp` records; `gcr.io` zone (wildcard CNAME + A); `googleapis.com` zone (wildcard CNAME to `restricted.googleapis.com` + A); `pkg.dev` zone (wildcard CNAME + A). +- Spoke-side: `gcp.databricks.com` zone with `workspace`, `dp`, `tunnel` records. +- `workspace_dns_id` is the regex-extracted ID from `workspace_url` (matches today's behavior in `gcp-with-psc-exfiltration-protection`). + +**Outputs:** none. + +### `modules/gcp/service-account` and `modules/gcp/unity-catalog` + +Relocated from `modules/gcp-sa-provisioning` and `modules/gcp-unity-catalog` via `git mv`. Variables, outputs, and resource addresses unchanged. Old paths get a deprecation README pointing to the new location. + +## Example shapes + +Each example dir contains: `init.tf` (providers), `main.tf` (single `module "workspace"` call, optionally plus `module "unity_catalog"`), `variables.tf` (only the variables relevant to that scenario), `terraform.tfvars` (skeleton with empty values + comments), `outputs.tf` (re-exports `workspace_id`/`workspace_url`), `README.md`, `Makefile`. + +### `examples/gcp-basic` — Databricks-managed VPC + +```hcl +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + + vpc_source = "databricks_managed" +} +``` + +### `examples/gcp-byovpc` — Terraform creates the VPC + +```hcl +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + + vpc_source = "create" + spoke_vpc_cidr = var.spoke_vpc_cidr + subnet_cidr = var.subnet_cidr +} +``` + +### `examples/gcp-existing-vpc` — NEW, fulfills the northstar + +```hcl +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + + vpc_source = "existing" + existing_vpc_name = var.existing_vpc_name + existing_subnet_name = var.existing_subnet_name +} +``` + +### `examples/gcp-with-psc-exfiltration-protection` — PSC + restricted egress + +Name kept for backward-compatibility with external links. + +```hcl +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + + vpc_source = "create" + spoke_vpc_cidr = var.spoke_vpc_cidr + subnet_cidr = var.subnet_cidr + + private_link_frontend = true + private_link_backend = true + private_access_only = true + restricted_egress = true + + spoke_vpc_google_project = var.spoke_vpc_google_project + hub_vpc_google_project = var.hub_vpc_google_project + is_spoke_vpc_shared = var.is_spoke_vpc_shared + hub_vpc_cidr = var.hub_vpc_cidr + psc_subnet_cidr = var.psc_subnet_cidr +} +``` + +Plus an optional `module "unity_catalog"` block using `module.workspace.workspace_id` and `module.workspace.workspace_url`. + +### `examples/gcp-sa-provisioning` + +Points at the relocated `modules/gcp/service-account`. Variables and outputs identical to today. + +## Migration plan + +Build the new modules alongside the old ones; migrate examples one PR at a time. + +| PR | Scope | Risk | +|----|-------|------| +| 1 | Add all new modules under `modules/gcp/`. Relocate `service-account` and `unity-catalog` via `git mv` with deprecation stubs at old paths. No example touched. | Low — no example references new code yet | +| 2 | Migrate `examples/gcp-basic` to the new composer. Old `modules/gcp-workspace-basic` stays. | Low — basic case, no PSC/DNS to coordinate | +| 3 | Migrate `examples/gcp-byovpc`. | Low | +| 4 | Migrate `examples/gcp-with-psc-exfiltration-protection`. Sandbox apply + reachability check required. | Medium — PSC + DNS + firewall coordination | +| 5 | Add new `examples/gcp-existing-vpc`. | Low — net-new | +| 6 | Delete `modules/gcp-workspace-basic`, `modules/gcp-workspace-byovpc`, `modules/gcp-with-psc-exfiltration-protection`. Delete deprecation stubs. Delete `examples/gcp-sa-provisionning` (typo dir) and `examples/gcp-test-modules` (state-only). Clean stray `terraform.tfstate*` files from `examples/gcp-*` (verify `.gitignore` first). Update top-level README. | Low | + +Each PR is drafted, sandbox-applied by the author, then sent for review. No state migration support — applies of old examples don't transition to the new examples; users re-apply on clean state. Example READMEs document this in PRs 2–4. + +## Testing approach + +Scoped to what the repo already supports. + +**Static (pre-commit, every PR):** +- `terraform fmt -recursive` +- `terraform validate` per module and per example +- `terraform-docs` regeneration check + +**Module-level plan smoke (PR 1):** +For each new submodule, a `tests/` subdir with minimal-fixture `terraform plan` invocations using mock vars (e.g. `databricks_account_id = "00000000-0000-0000-0000-000000000000"`). Run with `terraform init -backend=false && terraform validate && terraform plan -refresh=false`. Wrapped in a Makefile target. Catches missing required inputs and broken preconditions before any sandbox apply. + +Negative cases that must fail at plan time (one fixture each): +- `restricted_egress = true` + `vpc_source = "databricks_managed"` +- `restricted_egress = true` + `hub_vpc_cidr = null` +- `vpc_source = "existing"` + `existing_vpc_name = null` +- `private_link_frontend = true` + `vpc_source = "databricks_managed"` + +**Example-level apply (manual, before each migration PR merges):** +- Apply against sandbox GCP project + Databricks account +- Verify workspace reachable; UC accessible where applicable +- Fresh `terraform plan` against applied state — expect zero drift +- `terraform destroy` and confirm clean teardown (PSC + DNS ordering) +- Capture plan/apply output in the PR description + +**What we don't test:** terratest, GitHub Actions matrix, automated cost guards, upgrade-from-old-state. Out of scope. + +## Risks & mitigations + +| Risk | Mitigation | +|------|-----------| +| Regional PSC service-attachment map drift between old and new modules during transition | Both reference the same Databricks-published list; copy verbatim to new module, delete old in PR 6 | +| Cross-variable `precondition` failures only surface at plan time, not at `validate` | Module-level plan-smoke fixtures in `tests/` exercise every precondition | +| `databricks_mws_workspaces` resource address changes (module path differs) | Acknowledged: examples are throwaway, customer state is unaffected. Documented in migration PRs | +| Empty `modules/gcp/network/` dir already exists | Becomes the home for the new `network` submodule — no conflict | +| User's separate UC work conflicts with the relocation `git mv` | Relocate but do not modify UC contents in PR 1; user's UC work can land before or after relocation as desired | +| PSC + DNS teardown ordering issues during `terraform destroy` | Add explicit `depends_on` between DNS records and the PSC forwarding rules they reference; verify during PR 4 sandbox test | + +## Open questions + +None at this time. All design choices ratified during the brainstorming session on 2026-05-14. From d3e7b87d2e17a7903ac271b4b45077b31e1c30a7 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 14:13:51 +0200 Subject: [PATCH 02/14] docs: add implementation plan for GCP modules refactor 31 tasks across 6 PRs implementing the design spec. Each task has explicit file paths, code blocks, validation commands, and commit messages. Self-review checks complete. Co-authored-by: Isaac --- .../plans/2026-05-14-gcp-modules-refactor.md | 3822 +++++++++++++++++ 1 file changed, 3822 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-14-gcp-modules-refactor.md diff --git a/docs/superpowers/plans/2026-05-14-gcp-modules-refactor.md b/docs/superpowers/plans/2026-05-14-gcp-modules-refactor.md new file mode 100644 index 0000000..9c31dee --- /dev/null +++ b/docs/superpowers/plans/2026-05-14-gcp-modules-refactor.md @@ -0,0 +1,3822 @@ +# GCP Modules Refactor Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace three duplicated GCP workspace modules with one composer (`modules/gcp/databricks-workspace`) that orchestrates five focused submodules (`network`, `private-connectivity`, `account`, `dns`, plus relocated `service-account` and `unity-catalog`). Migrate existing GCP examples one at a time onto the composer and add a new "existing VPC" example. + +**Architecture:** Composer reads orthogonal feature flags (`vpc_source`, `private_link_frontend`, `private_link_backend`, `private_access_only`, `restricted_egress`) and conditionally instantiates submodules via `count`. Dependency graph is linear: `network → private-connectivity → account → dns`. All `databricks_mws_*` resources live in `account`; all GCP-side PSC resources live in `private-connectivity`; DNS is split out because it depends on `account.workspace_url`. + +**Tech Stack:** Terraform >= 1.5, `hashicorp/google` provider, `databricks/databricks` provider, `terraform-docs`, `pre-commit`. No new tooling. + +**Spec reference:** `docs/superpowers/specs/2026-05-14-gcp-modules-refactor-design.md` + +**Branch:** `feature/gcp-modules-refactor` (already created; spec committed as `2bfd9bd`). + +--- + +## File Structure + +This plan creates the following new tree (incremental — each task creates one slice): + +``` +docs/superpowers/ # already exists + ├── specs/2026-05-14-gcp-modules-refactor-design.md # already committed + └── plans/2026-05-14-gcp-modules-refactor.md # this file + +modules/gcp/ + ├── Makefile # Task 1 — recursive docs/test_docs + ├── databricks-workspace/ # Task 14–17 — composer + │ ├── main.tf + │ ├── variables.tf + │ ├── outputs.tf + │ ├── versions.tf + │ ├── README.md # terraform-docs generates + │ ├── Makefile + │ └── tests/ # plan-time validation fixtures + │ ├── basic/main.tf + │ ├── byovpc/main.tf + │ ├── existing-vpc/main.tf + │ ├── psc-isolated/main.tf + │ └── negative-*/main.tf # expect plan failure + ├── network/ # Task 3–5 + │ ├── main.tf + │ ├── variables.tf + │ ├── outputs.tf + │ ├── versions.tf + │ ├── README.md + │ ├── Makefile + │ └── tests/ + │ ├── create/main.tf + │ ├── existing/main.tf + │ └── create-with-hub/main.tf + ├── private-connectivity/ # Task 6–8 + │ ├── psc.tf + │ ├── firewall.tf + │ ├── variables.tf + │ ├── outputs.tf + │ ├── versions.tf + │ ├── locals.tf # regional PSC + hive metastore maps + │ ├── README.md + │ ├── Makefile + │ └── tests/ + │ ├── frontend-only/main.tf + │ ├── full-isolated/main.tf + │ └── no-egress/main.tf + ├── account/ # Task 9–13 + │ ├── main.tf # mws_networks + mws_workspaces + │ ├── vpc-endpoints.tf # mws_vpc_endpoint + │ ├── pas.tf # mws_private_access_settings + │ ├── variables.tf + │ ├── outputs.tf + │ ├── versions.tf + │ ├── README.md + │ ├── Makefile + │ └── tests/ + │ ├── databricks-managed/main.tf + │ ├── byovpc/main.tf + │ └── psc-with-pas/main.tf + ├── dns/ # Task 18–19 + │ ├── hub.tf + │ ├── spoke.tf + │ ├── variables.tf + │ ├── outputs.tf + │ ├── versions.tf + │ ├── README.md + │ ├── Makefile + │ └── tests/hub-and-spoke/main.tf + ├── service-account/ # Task 20 (git mv from modules/gcp-sa-provisioning) + └── unity-catalog/ # Task 21 (git mv from modules/gcp-unity-catalog) + +modules/gcp-sa-provisioning/ # Task 20 — replaced with deprecation README + └── README.md + +modules/gcp-unity-catalog/ # Task 21 — replaced with deprecation README + └── README.md + +examples/gcp-basic/ # Task 24 — migrated +examples/gcp-byovpc/ # Task 25 — migrated +examples/gcp-with-psc-exfiltration-protection/ # Task 26 — migrated +examples/gcp-existing-vpc/ # Task 27 — NEW +examples/gcp-sa-provisioning/ # Task 28 — repoint to relocated module + +# Deletions (Task 29 onward, PR 6) +modules/gcp-workspace-basic/ # DELETE +modules/gcp-workspace-byovpc/ # DELETE +modules/gcp-with-psc-exfiltration-protection/ # DELETE +modules/gcp-sa-provisioning/ # DELETE (stub) +modules/gcp-unity-catalog/ # DELETE (stub) +examples/gcp-sa-provisionning/ # DELETE (typo dir) +examples/gcp-test-modules/ # DELETE (state-only) +``` + +**Testing approach for each module task:** Each submodule gets `tests//main.tf` fixtures that call the module with mock vars. The "test" is `terraform init -backend=false && terraform validate && terraform plan -refresh=false` against the fixture. We don't apply — we verify the configuration is valid and the planned resource graph matches expectations. + +**Conventions to follow** (observed in existing repo): +- `versions.tf` declares required_providers and required terraform version +- `Makefile` per module has `docs:` and `test_docs:` targets calling `terraform-docs -c ../../.terraform-docs.yml .` (note: for nested `modules/gcp//`, the path becomes `../../../.terraform-docs.yml`) +- README sections between `` and `` are managed by `terraform-docs` +- Resource names use `${var.prefix}--${random_string.suffix.result}` pattern +- `random_string.suffix` is declared **only in the composer**, then passed to submodules via `suffix` input + +--- + +## PR 1 — Foundation + +This PR adds all new modules under `modules/gcp/` and relocates `service-account` + `unity-catalog`. No example is touched. The deliverable at the end of PR 1 is: a complete new module tree that passes `terraform validate` for every fixture, with no example consuming it yet. + +### Task 1: Repo scaffolding — `modules/gcp/Makefile` and `tests/` convention + +**Files:** +- Create: `modules/gcp/Makefile` + +- [ ] **Step 1: Inspect existing Makefile pattern** + +Read `modules/Makefile` and `modules/gcp-workspace-basic/Makefile` to confirm conventions. + +Run: `cat modules/Makefile modules/gcp-workspace-basic/Makefile` + +Expected: top-level discovers projects via `*/README.md`, each module Makefile invokes `terraform-docs -c ../../.terraform-docs.yml .`. + +- [ ] **Step 2: Create `modules/gcp/Makefile`** + +Write: + +```makefile +PROJECTS := $(dir $(wildcard */README.md)) + +docs: $(PROJECTS) + +$(PROJECTS): + $(MAKE) -C $@ docs + +.PHONY: $(PROJECTS) docs +``` + +- [ ] **Step 3: Update top-level `modules/Makefile` to recurse into `gcp/`** + +Read current `modules/Makefile`. It only iterates `*/README.md`. Since `modules/gcp/` has no README of its own, we add an explicit recursion. + +Edit `modules/Makefile`: + +```makefile +PROJECTS := $(dir $(wildcard */README.md)) + +docs: $(PROJECTS) gcp-recursive + +$(PROJECTS): + $(MAKE) -C $@ docs + +gcp-recursive: + $(MAKE) -C gcp docs + +.PHONY: $(PROJECTS) docs gcp-recursive +``` + +- [ ] **Step 4: Commit** + +```bash +git add modules/gcp/Makefile modules/Makefile +git commit -m "$(cat <<'EOF' +build: add Makefile recursion for modules/gcp/ submodules + +Adds modules/gcp/Makefile mirroring the modules/ pattern (discover +sub-projects via */README.md) and updates modules/Makefile to recurse +into the gcp/ subdir for terraform-docs generation. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 2: `modules/gcp/network` — skeleton + variables + versions + +**Files:** +- Create: `modules/gcp/network/variables.tf` +- Create: `modules/gcp/network/main.tf` +- Create: `modules/gcp/network/outputs.tf` +- Create: `modules/gcp/network/versions.tf` +- Create: `modules/gcp/network/Makefile` +- Create: `modules/gcp/network/README.md` (placeholder, terraform-docs fills it) + +- [ ] **Step 1: Write `versions.tf`** + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0" + } + } +} +``` + +- [ ] **Step 2: Write `variables.tf`** + +```hcl +variable "prefix" { + type = string + description = "Prefix for generated resource names" +} + +variable "suffix" { + type = string + description = "Random suffix passed by the composer for uniqueness" +} + +variable "google_region" { + type = string + description = "GCP region for all network resources" +} + +variable "vpc_source" { + type = string + description = "Either 'create' (Terraform creates a VPC) or 'existing' (data-source lookup)" + validation { + condition = contains(["create", "existing"], var.vpc_source) + error_message = "vpc_source must be 'create' or 'existing'." + } +} + +# Spoke project always required +variable "spoke_vpc_google_project" { + type = string + description = "GCP project hosting the spoke VPC" +} + +# === Used when vpc_source = "create" ==================================== +variable "spoke_vpc_cidr" { + type = string + default = null + description = "CIDR for the spoke subnet primary range (required when vpc_source=create)" +} + +variable "subnet_cidr" { + type = string + default = null + description = "CIDR for the spoke subnet (required when vpc_source=create)" +} + +variable "subnet_name" { + type = string + default = null + description = "Override for spoke subnet name (default: \"${prefix}-subnet-${suffix}\")" +} + +variable "pod_cidr" { + type = string + default = null + description = "GKE secondary range for pods (optional)" +} + +variable "svc_cidr" { + type = string + default = null + description = "GKE secondary range for services (optional)" +} + +# === Used when vpc_source = "existing" ================================== +variable "existing_vpc_name" { + type = string + default = null + description = "Name of pre-existing VPC (required when vpc_source=existing)" +} + +variable "existing_subnet_name" { + type = string + default = null + description = "Name of pre-existing subnet (required when vpc_source=existing)" +} + +# === Hub configuration (only when create_hub = true) ==================== +variable "create_hub" { + type = bool + default = false + description = "Create a hub VPC + subnet + peering with the spoke. Composer passes restricted_egress here." +} + +variable "hub_vpc_google_project" { + type = string + default = null + description = "GCP project hosting the hub VPC (required when create_hub=true)" +} + +variable "hub_vpc_cidr" { + type = string + default = null + description = "CIDR for the hub subnet (required when create_hub=true)" +} + +variable "is_spoke_vpc_shared" { + type = bool + default = false + description = "If true, bind the spoke VPC's project as a Shared-VPC host and the workspace project as a service project" +} + +variable "workspace_google_project" { + type = string + default = null + description = "Workspace project (used for Shared-VPC service binding)" +} +``` + +- [ ] **Step 3: Write empty `main.tf` and `outputs.tf`** + +`main.tf`: + +```hcl +# Resources added in Tasks 3, 4, 5 +``` + +`outputs.tf`: + +```hcl +output "spoke_vpc_id" { + value = null + description = "ID of the spoke VPC" +} + +output "spoke_vpc_name" { + value = null + description = "Name of the spoke VPC" +} + +output "spoke_vpc_self_link" { + value = null + description = "Self-link of the spoke VPC" +} + +output "spoke_subnet_id" { + value = null + description = "ID of the spoke subnet" +} + +output "spoke_subnet_name" { + value = null + description = "Name of the spoke subnet" +} + +output "spoke_subnet_self_link" { + value = null + description = "Self-link of the spoke subnet" +} + +output "hub_vpc_id" { + value = null + description = "ID of the hub VPC (null when create_hub=false)" +} + +output "hub_vpc_name" { + value = null + description = "Name of the hub VPC (null when create_hub=false)" +} + +output "hub_vpc_self_link" { + value = null + description = "Self-link of the hub VPC (null when create_hub=false)" +} + +output "hub_subnet_name" { + value = null + description = "Name of the hub subnet (null when create_hub=false)" +} + +output "nat_id" { + value = null + description = "ID of the Cloud NAT (null when vpc_source=existing)" +} +``` + +(Outputs are wired to real resources in Tasks 3–5.) + +- [ ] **Step 4: Write `Makefile`** + +```makefile +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . +``` + +- [ ] **Step 5: Write `README.md` placeholder** + +```markdown +# modules/gcp/network + +VPC, subnet, router, NAT, peering, and Shared-VPC binding for the Databricks GCP composer. + + + +``` + +- [ ] **Step 6: Validate** + +Run: +```bash +cd modules/gcp/network && terraform init -backend=false && terraform validate +``` + +Expected: `Success! The configuration is valid.` + +- [ ] **Step 7: Commit** + +```bash +git add modules/gcp/network/ +git commit -m "$(cat <<'EOF' +feat(gcp/network): scaffold module with variables and outputs + +Adds modules/gcp/network with variable declarations, empty outputs, +versions.tf, Makefile, and README placeholder. Resources to be added +in subsequent tasks. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 3: `modules/gcp/network` — create-vpc path + fixture + +**Files:** +- Modify: `modules/gcp/network/main.tf` +- Modify: `modules/gcp/network/outputs.tf` +- Create: `modules/gcp/network/tests/create/main.tf` + +- [ ] **Step 1: Write the test fixture `tests/create/main.tf`** + +```hcl +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-project" + region = "us-central1" +} + +module "network" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + vpc_source = "create" + spoke_vpc_google_project = "fixture-project" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" +} +``` + +- [ ] **Step 2: Run the fixture, expect plan to show no resources (no implementation yet)** + +```bash +cd modules/gcp/network/tests/create +terraform init -backend=false +terraform validate +terraform plan -refresh=false +``` + +Expected: validate passes, plan shows `No changes. Your infrastructure matches the configuration.` (no resources defined in module yet). + +- [ ] **Step 3: Implement the create-vpc path in `modules/gcp/network/main.tf`** + +```hcl +locals { + create_vpc = var.vpc_source == "create" + use_existing_vpc = var.vpc_source == "existing" + + subnet_name = coalesce(var.subnet_name, "${var.prefix}-subnet-${var.suffix}") +} + +# === Spoke VPC (created) ================================================ +resource "google_compute_network" "spoke_vpc" { + count = local.create_vpc ? 1 : 0 + + name = "${var.prefix}-spoke-vpc-${var.suffix}" + project = var.spoke_vpc_google_project + auto_create_subnetworks = false + routing_mode = "GLOBAL" +} + +resource "google_compute_subnetwork" "spoke_subnet" { + count = local.create_vpc ? 1 : 0 + + name = local.subnet_name + project = var.spoke_vpc_google_project + network = google_compute_network.spoke_vpc[0].id + region = var.google_region + ip_cidr_range = var.subnet_cidr + private_ip_google_access = true + + dynamic "secondary_ip_range" { + for_each = var.pod_cidr != null ? [1] : [] + content { + range_name = "pods" + ip_cidr_range = var.pod_cidr + } + } + + dynamic "secondary_ip_range" { + for_each = var.svc_cidr != null ? [1] : [] + content { + range_name = "services" + ip_cidr_range = var.svc_cidr + } + } +} + +resource "google_compute_router" "router" { + count = local.create_vpc ? 1 : 0 + + name = "${var.prefix}-router-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + network = google_compute_network.spoke_vpc[0].id +} + +resource "google_compute_router_nat" "nat" { + count = local.create_vpc ? 1 : 0 + + name = "${var.prefix}-nat-${var.suffix}" + project = var.spoke_vpc_google_project + router = google_compute_router.router[0].name + region = var.google_region + nat_ip_allocate_option = "AUTO_ONLY" + source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" +} +``` + +- [ ] **Step 4: Wire outputs in `outputs.tf`** + +Replace the `null` placeholders: + +```hcl +output "spoke_vpc_id" { + value = local.create_vpc ? google_compute_network.spoke_vpc[0].id : null + description = "ID of the spoke VPC" +} + +output "spoke_vpc_name" { + value = local.create_vpc ? google_compute_network.spoke_vpc[0].name : null + description = "Name of the spoke VPC" +} + +output "spoke_vpc_self_link" { + value = local.create_vpc ? google_compute_network.spoke_vpc[0].self_link : null + description = "Self-link of the spoke VPC" +} + +output "spoke_subnet_id" { + value = local.create_vpc ? google_compute_subnetwork.spoke_subnet[0].id : null + description = "ID of the spoke subnet" +} + +output "spoke_subnet_name" { + value = local.create_vpc ? google_compute_subnetwork.spoke_subnet[0].name : null + description = "Name of the spoke subnet" +} + +output "spoke_subnet_self_link" { + value = local.create_vpc ? google_compute_subnetwork.spoke_subnet[0].self_link : null + description = "Self-link of the spoke subnet" +} + +output "nat_id" { + value = local.create_vpc ? google_compute_router_nat.nat[0].id : null + description = "ID of the Cloud NAT (null when vpc_source=existing)" +} + +# hub_* outputs still null at this point; updated in Task 5. +output "hub_vpc_id" { value = null description = "ID of the hub VPC (null when create_hub=false)" } +output "hub_vpc_name" { value = null description = "Name of the hub VPC (null when create_hub=false)" } +output "hub_vpc_self_link" { value = null description = "Self-link of the hub VPC (null when create_hub=false)" } +output "hub_subnet_name" { value = null description = "Name of the hub subnet (null when create_hub=false)" } +``` + +- [ ] **Step 5: Re-run fixture and verify resource count** + +```bash +cd modules/gcp/network/tests/create +terraform plan -refresh=false +``` + +Expected: `Plan: 4 to add, 0 to change, 0 to destroy.` (network + subnet + router + nat). + +- [ ] **Step 6: Commit** + +```bash +git add modules/gcp/network/ +git commit -m "$(cat <<'EOF' +feat(gcp/network): implement create-vpc path + +Adds google_compute_network/subnetwork/router/router_nat resources +gated on vpc_source="create". Outputs wired to real resources. +Fixture in tests/create/ asserts 4 resources are planned. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 4: `modules/gcp/network` — existing-vpc path + fixture + +**Files:** +- Modify: `modules/gcp/network/main.tf` +- Modify: `modules/gcp/network/outputs.tf` +- Create: `modules/gcp/network/tests/existing/main.tf` + +- [ ] **Step 1: Write fixture `tests/existing/main.tf`** + +```hcl +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-project" + region = "us-central1" +} + +module "network" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + vpc_source = "existing" + spoke_vpc_google_project = "fixture-project" + existing_vpc_name = "preexisting-vpc" + existing_subnet_name = "preexisting-subnet" +} +``` + +- [ ] **Step 2: Add data sources to `main.tf`** + +Append to `modules/gcp/network/main.tf`: + +```hcl +# === Spoke VPC (data lookup) ============================================ +data "google_compute_network" "existing_spoke" { + count = local.use_existing_vpc ? 1 : 0 + + name = var.existing_vpc_name + project = var.spoke_vpc_google_project +} + +data "google_compute_subnetwork" "existing_spoke_subnet" { + count = local.use_existing_vpc ? 1 : 0 + + name = var.existing_subnet_name + project = var.spoke_vpc_google_project + region = var.google_region +} +``` + +- [ ] **Step 3: Update outputs to merge create and existing paths** + +In `outputs.tf` replace the four spoke outputs: + +```hcl +output "spoke_vpc_id" { + value = local.create_vpc ? google_compute_network.spoke_vpc[0].id : + local.use_existing_vpc ? data.google_compute_network.existing_spoke[0].id : null + description = "ID of the spoke VPC" +} + +output "spoke_vpc_name" { + value = local.create_vpc ? google_compute_network.spoke_vpc[0].name : + local.use_existing_vpc ? data.google_compute_network.existing_spoke[0].name : null + description = "Name of the spoke VPC" +} + +output "spoke_vpc_self_link" { + value = local.create_vpc ? google_compute_network.spoke_vpc[0].self_link : + local.use_existing_vpc ? data.google_compute_network.existing_spoke[0].self_link : null + description = "Self-link of the spoke VPC" +} + +output "spoke_subnet_id" { + value = local.create_vpc ? google_compute_subnetwork.spoke_subnet[0].id : + local.use_existing_vpc ? data.google_compute_subnetwork.existing_spoke_subnet[0].id : null + description = "ID of the spoke subnet" +} + +output "spoke_subnet_name" { + value = local.create_vpc ? google_compute_subnetwork.spoke_subnet[0].name : + local.use_existing_vpc ? data.google_compute_subnetwork.existing_spoke_subnet[0].name : null + description = "Name of the spoke subnet" +} + +output "spoke_subnet_self_link" { + value = local.create_vpc ? google_compute_subnetwork.spoke_subnet[0].self_link : + local.use_existing_vpc ? data.google_compute_subnetwork.existing_spoke_subnet[0].self_link : null + description = "Self-link of the spoke subnet" +} +``` + +- [ ] **Step 4: Run fixture, expect plan with zero resources (data sources only)** + +```bash +cd modules/gcp/network/tests/existing +terraform init -backend=false +terraform validate +terraform plan -refresh=false +``` + +Expected: validate passes; plan shows `No changes` (data sources don't appear as planned resources without applying; we accept this — the test is `validate` passing without errors). + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/network/ +git commit -m "$(cat <<'EOF' +feat(gcp/network): implement existing-vpc path + +Adds data.google_compute_network and data.google_compute_subnetwork +lookups gated on vpc_source="existing". Spoke outputs now resolve +from either created resources or data sources. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 5: `modules/gcp/network` — hub & peering & shared-VPC + fixture + +**Files:** +- Modify: `modules/gcp/network/main.tf` +- Modify: `modules/gcp/network/outputs.tf` +- Create: `modules/gcp/network/tests/create-with-hub/main.tf` + +- [ ] **Step 1: Write fixture `tests/create-with-hub/main.tf`** + +```hcl +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-project" + region = "us-central1" +} + +module "network" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + vpc_source = "create" + spoke_vpc_google_project = "fixture-spoke-project" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" + + create_hub = true + hub_vpc_google_project = "fixture-hub-project" + hub_vpc_cidr = "10.1.0.0/24" + is_spoke_vpc_shared = true + workspace_google_project = "fixture-workspace-project" +} +``` + +- [ ] **Step 2: Append hub + peering + shared-VPC resources to `main.tf`** + +```hcl +# === Hub VPC ============================================================ +resource "google_compute_network" "hub_vpc" { + count = var.create_hub ? 1 : 0 + + name = "${var.prefix}-hub-vpc-${var.suffix}" + project = var.hub_vpc_google_project + auto_create_subnetworks = false + routing_mode = "GLOBAL" +} + +resource "google_compute_subnetwork" "hub_subnet" { + count = var.create_hub ? 1 : 0 + + name = "${var.prefix}-hub-subnet-${var.suffix}" + project = var.hub_vpc_google_project + network = google_compute_network.hub_vpc[0].id + region = var.google_region + ip_cidr_range = var.hub_vpc_cidr + private_ip_google_access = true +} + +# === Peering ============================================================ +resource "google_compute_network_peering" "hub_to_spoke" { + count = var.create_hub ? 1 : 0 + + name = "${var.prefix}-hub-spoke-${var.suffix}" + network = google_compute_network.hub_vpc[0].self_link + peer_network = local.create_vpc ? google_compute_network.spoke_vpc[0].self_link : data.google_compute_network.existing_spoke[0].self_link +} + +resource "google_compute_network_peering" "spoke_to_hub" { + count = var.create_hub ? 1 : 0 + + name = "${var.prefix}-spoke-hub-${var.suffix}" + network = local.create_vpc ? google_compute_network.spoke_vpc[0].self_link : data.google_compute_network.existing_spoke[0].self_link + peer_network = google_compute_network.hub_vpc[0].self_link +} + +# === Shared VPC ========================================================= +resource "google_compute_shared_vpc_host_project" "host" { + count = var.create_hub && var.is_spoke_vpc_shared && var.workspace_google_project != var.spoke_vpc_google_project ? 1 : 0 + + project = var.spoke_vpc_google_project +} + +resource "google_compute_shared_vpc_service_project" "service" { + count = var.create_hub && var.is_spoke_vpc_shared && var.workspace_google_project != var.spoke_vpc_google_project ? 1 : 0 + + host_project = google_compute_shared_vpc_host_project.host[0].project + service_project = var.workspace_google_project +} +``` + +- [ ] **Step 3: Wire hub outputs in `outputs.tf`** + +```hcl +output "hub_vpc_id" { + value = var.create_hub ? google_compute_network.hub_vpc[0].id : null + description = "ID of the hub VPC (null when create_hub=false)" +} + +output "hub_vpc_name" { + value = var.create_hub ? google_compute_network.hub_vpc[0].name : null + description = "Name of the hub VPC (null when create_hub=false)" +} + +output "hub_vpc_self_link" { + value = var.create_hub ? google_compute_network.hub_vpc[0].self_link : null + description = "Self-link of the hub VPC (null when create_hub=false)" +} + +output "hub_subnet_name" { + value = var.create_hub ? google_compute_subnetwork.hub_subnet[0].name : null + description = "Name of the hub subnet (null when create_hub=false)" +} +``` + +- [ ] **Step 4: Validate and plan** + +```bash +cd modules/gcp/network/tests/create-with-hub +terraform init -backend=false +terraform validate +terraform plan -refresh=false +``` + +Expected: `Plan: 8 to add, 0 to change, 0 to destroy.` (4 spoke + 2 hub + 2 peering + 2 shared-vpc — wait, 4+2+2+2=10. Let me recount: spoke_vpc, spoke_subnet, router, nat = 4. hub_vpc, hub_subnet = 2. hub_to_spoke peering, spoke_to_hub peering = 2. shared_vpc_host, shared_vpc_service = 2. Total = 10). + +Expected: `Plan: 10 to add`. + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/network/ +git commit -m "$(cat <<'EOF' +feat(gcp/network): add hub VPC, peering, and Shared-VPC binding + +Adds hub VPC + subnet + bidirectional peering with spoke + optional +shared-VPC host/service binding, all gated on create_hub. Composer +passes restricted_egress -> create_hub. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 6: `modules/gcp/private-connectivity` — scaffold + locals (regional maps) + +**Files:** +- Create: `modules/gcp/private-connectivity/versions.tf` +- Create: `modules/gcp/private-connectivity/variables.tf` +- Create: `modules/gcp/private-connectivity/locals.tf` +- Create: `modules/gcp/private-connectivity/outputs.tf` +- Create: `modules/gcp/private-connectivity/psc.tf` (empty) +- Create: `modules/gcp/private-connectivity/firewall.tf` (empty) +- Create: `modules/gcp/private-connectivity/Makefile` +- Create: `modules/gcp/private-connectivity/README.md` placeholder + +- [ ] **Step 1: `versions.tf`** + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0" + } + } +} +``` + +- [ ] **Step 2: `variables.tf`** + +```hcl +variable "prefix" { type = string } +variable "suffix" { type = string } +variable "google_region" { type = string } + +# Spoke network refs +variable "spoke_vpc_id" { type = string } +variable "spoke_vpc_self_link" { type = string } +variable "spoke_vpc_google_project" { type = string } +variable "spoke_vpc_cidr" { type = string } + +# Hub network refs (nullable when no hub) +variable "hub_vpc_id" { type = string default = null } +variable "hub_vpc_self_link" { type = string default = null } +variable "hub_vpc_google_project" { type = string default = null } +variable "hub_subnet_name" { type = string default = null } +variable "hub_vpc_cidr" { type = string default = null } + +# Feature flags +variable "enable_frontend" { type = bool default = false } +variable "enable_backend" { type = bool default = false } +variable "restrict_egress" { type = bool default = false } + +# PSC subnet CIDR (always required when this module is invoked because +# the composer only instantiates it when at least one PSC flag is true) +variable "psc_subnet_cidr" { + type = string + description = "CIDR for the dedicated PSC subnet in the spoke VPC" +} + +# Optional hive metastore IP override; falls back to regional map +variable "hive_metastore_ip" { + type = string + default = null + description = "Regional Hive metastore IP (looked up via internal map if null)" +} +``` + +- [ ] **Step 3: `locals.tf` — regional PSC service-attachment + hive metastore maps** + +Copy the maps verbatim from `modules/gcp-with-psc-exfiltration-protection/main.tf`. The plan reproduces them in full because future region additions will land in one place going forward. + +```hcl +locals { + google_frontend_psc_targets = { + "asia-northeast1" = "projects/general-prod-asianortheast1-01/regions/asia-northeast1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "asia-south1" = "projects/gen-prod-asias1-01/regions/asia-south1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "asia-southeast1" = "projects/general-prod-asiasoutheast1-01/regions/asia-southeast1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "australia-southeast1" = "projects/general-prod-ausoutheast1-01/regions/australia-southeast1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "europe-west1" = "projects/general-prod-europewest1-01/regions/europe-west1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "europe-west2" = "projects/general-prod-europewest2-01/regions/europe-west2/serviceAttachments/plproxy-psc-endpoint-all-ports" + "europe-west3" = "projects/general-prod-europewest3-01/regions/europe-west3/serviceAttachments/plproxy-psc-endpoint-all-ports" + "northamerica-northeast1" = "projects/general-prod-nanortheast1-01/regions/northamerica-northeast1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "southamerica-east1" = "projects/gen-prod-saeast1-01/regions/southamerica-east1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-central1" = "projects/gcp-prod-general/regions/us-central1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-east1" = "projects/general-prod-useast1-01/regions/us-east1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-east4" = "projects/general-prod-useast4-01/regions/us-east4/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-west1" = "projects/general-prod-uswest1-01/regions/us-west1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-west4" = "projects/general-prod-uswest4-01/regions/us-west4/serviceAttachments/plproxy-psc-endpoint-all-ports" + } + + google_backend_psc_targets = { + "asia-northeast1" = "projects/prod-gcp-asia-northeast1/regions/asia-northeast1/serviceAttachments/ngrok-psc-endpoint" + "asia-south1" = "projects/prod-gcp-asia-south1/regions/asia-south1/serviceAttachments/ngrok-psc-endpoint" + "asia-southeast1" = "projects/prod-gcp-asia-southeast1/regions/asia-southeast1/serviceAttachments/ngrok-psc-endpoint" + "australia-southeast1" = "projects/prod-gcp-australia-southeast1/regions/australia-southeast1/serviceAttachments/ngrok-psc-endpoint" + "europe-west1" = "projects/prod-gcp-europe-west1/regions/europe-west1/serviceAttachments/ngrok-psc-endpoint" + "europe-west2" = "projects/prod-gcp-europe-west2/regions/europe-west2/serviceAttachments/ngrok-psc-endpoint" + "europe-west3" = "projects/prod-gcp-europe-west3/regions/europe-west3/serviceAttachments/ngrok-psc-endpoint" + "northamerica-northeast1" = "projects/prod-gcp-na-northeast1/regions/northamerica-northeast1/serviceAttachments/ngrok-psc-endpoint" + "southamerica-east1" = "projects/gen-prod-saeast1-01/regions/southamerica-east1/serviceAttachments/ngrok-psc-endpoint" + "us-central1" = "projects/prod-gcp-us-central1/regions/us-central1/serviceAttachments/ngrok-psc-endpoint" + "us-east1" = "projects/prod-gcp-us-east1/regions/us-east1/serviceAttachments/ngrok-psc-endpoint" + "us-east4" = "projects/prod-gcp-us-east4/regions/us-east4/serviceAttachments/ngrok-psc-endpoint" + "us-west1" = "projects/prod-gcp-us-west1/regions/us-west1/serviceAttachments/ngrok-psc-endpoint" + "us-west4" = "projects/prod-gcp-us-west4/regions/us-west4/serviceAttachments/ngrok-psc-endpoint" + } + + # Regional default Hive Metastore IPs per Databricks docs: + # https://docs.gcp.databricks.com/en/resources/ip-domain-region.html#addresses-for-default-metastore + # NOTE: keep this list curated. When null, the firewall rule omits the + # managed-hive allowance (acceptable when customers run their own metastore). + default_hive_metastore_ips = { + # Filled in by ops; leave empty initially. Override via var.hive_metastore_ip. + } + + hive_metastore_ip = coalesce(var.hive_metastore_ip, try(local.default_hive_metastore_ips[var.google_region], "")) + + hub_present = var.hub_vpc_id != null +} +``` + +- [ ] **Step 4: Empty `psc.tf`, `firewall.tf`, `Makefile`, `README.md`, `outputs.tf`** + +`outputs.tf`: + +```hcl +output "psc_subnet_self_link" { value = null description = "Self-link of the PSC subnet" } +output "frontend_psc_fr_id" { value = null description = "Name of the frontend PSC forwarding rule (null when enable_frontend=false)" } +output "backend_psc_fr_id" { value = null description = "Name of the backend (SCC) PSC forwarding rule (null when enable_backend=false)" } +output "hub_frontend_psc_fr_id" { value = null description = "Name of the hub-side frontend PSC forwarding rule (null when no hub or no frontend)" } +output "frontend_psc_ip_spoke" { value = null description = "IP address of the spoke-side frontend PSC endpoint" } +output "backend_psc_ip_spoke" { value = null description = "IP address of the spoke-side backend PSC endpoint" } +output "frontend_psc_ip_hub" { value = null description = "IP address of the hub-side frontend PSC endpoint (null when no hub)" } +``` + +`psc.tf` and `firewall.tf` are empty for now (filled in next tasks). + +`Makefile`: + +```makefile +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . +``` + +`README.md`: + +```markdown +# modules/gcp/private-connectivity + +GCP-side PSC endpoints + restricted-egress firewall for the Databricks GCP composer. + + + +``` + +- [ ] **Step 5: Validate** + +```bash +cd modules/gcp/private-connectivity && terraform init -backend=false && terraform validate +``` + +Expected: `Success! The configuration is valid.` + +- [ ] **Step 6: Commit** + +```bash +git add modules/gcp/private-connectivity/ +git commit -m "$(cat <<'EOF' +feat(gcp/private-connectivity): scaffold with regional PSC maps + +Adds modules/gcp/private-connectivity with variables, regional PSC +service-attachment + hive metastore maps in locals.tf, empty psc.tf +and firewall.tf, null outputs. Resources added in follow-up tasks. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 7: `modules/gcp/private-connectivity` — PSC subnet + endpoints + fixture + +**Files:** +- Modify: `modules/gcp/private-connectivity/psc.tf` +- Modify: `modules/gcp/private-connectivity/outputs.tf` +- Create: `modules/gcp/private-connectivity/tests/full-isolated/main.tf` + +- [ ] **Step 1: Write fixture `tests/full-isolated/main.tf`** + +```hcl +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-spoke" + region = "us-central1" +} + +module "pc" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + + spoke_vpc_id = "projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_google_project = "fixture-spoke" + spoke_vpc_cidr = "10.0.0.0/16" + + hub_vpc_id = "projects/fixture-hub/global/networks/hub-vpc" + hub_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-hub/global/networks/hub-vpc" + hub_vpc_google_project = "fixture-hub" + hub_subnet_name = "fixture-hub-subnet-abc123" + hub_vpc_cidr = "10.1.0.0/24" + + enable_frontend = true + enable_backend = true + restrict_egress = true + psc_subnet_cidr = "10.0.255.0/28" +} +``` + +- [ ] **Step 2: Implement `psc.tf`** + +```hcl +# === PSC Subnet (spoke) ================================================= +resource "google_compute_subnetwork" "psc_subnet" { + name = "${var.prefix}-psc-subnet-${var.suffix}" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_id + region = var.google_region + ip_cidr_range = var.psc_subnet_cidr + private_ip_google_access = true +} + +# === Backend (SCC) PSC endpoint — spoke ================================= +resource "google_compute_address" "backend_address" { + count = var.enable_backend ? 1 : 0 + + name = "${var.prefix}-psc-scc-ip-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + subnetwork = google_compute_subnetwork.psc_subnet.name + address_type = "INTERNAL" +} + +resource "google_compute_forwarding_rule" "backend_fr" { + count = var.enable_backend ? 1 : 0 + + name = "${var.prefix}-psc-scc-ep-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + network = var.spoke_vpc_id + ip_address = google_compute_address.backend_address[0].id + target = local.google_backend_psc_targets[var.google_region] + load_balancing_scheme = "" +} + +# === Frontend PSC endpoint — spoke ====================================== +resource "google_compute_address" "frontend_address_spoke" { + count = var.enable_frontend ? 1 : 0 + + name = "${var.prefix}-psc-ws-ip-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + subnetwork = google_compute_subnetwork.psc_subnet.name + address_type = "INTERNAL" +} + +resource "google_compute_forwarding_rule" "frontend_fr_spoke" { + count = var.enable_frontend ? 1 : 0 + + name = "${var.prefix}-psc-ws-ep-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + network = var.spoke_vpc_id + ip_address = google_compute_address.frontend_address_spoke[0].id + target = local.google_frontend_psc_targets[var.google_region] + load_balancing_scheme = "" +} + +# === Frontend PSC endpoint — hub (transit) ============================== +resource "google_compute_address" "frontend_address_hub" { + count = local.hub_present && var.enable_frontend ? 1 : 0 + + name = "${var.prefix}-hub-psc-ws-ip-${var.suffix}" + project = var.hub_vpc_google_project + region = var.google_region + subnetwork = var.hub_subnet_name + address_type = "INTERNAL" +} + +resource "google_compute_forwarding_rule" "frontend_fr_hub" { + count = local.hub_present && var.enable_frontend ? 1 : 0 + + name = "${var.prefix}-hub-psc-ws-ep-${var.suffix}" + project = var.hub_vpc_google_project + region = var.google_region + network = var.hub_vpc_id + ip_address = google_compute_address.frontend_address_hub[0].id + target = local.google_frontend_psc_targets[var.google_region] + load_balancing_scheme = "" +} +``` + +- [ ] **Step 3: Wire PSC outputs in `outputs.tf`** + +```hcl +output "psc_subnet_self_link" { + value = google_compute_subnetwork.psc_subnet.self_link + description = "Self-link of the PSC subnet" +} + +output "frontend_psc_fr_id" { + value = var.enable_frontend ? google_compute_forwarding_rule.frontend_fr_spoke[0].name : null + description = "Name of the frontend PSC forwarding rule (null when enable_frontend=false)" +} + +output "backend_psc_fr_id" { + value = var.enable_backend ? google_compute_forwarding_rule.backend_fr[0].name : null + description = "Name of the backend (SCC) PSC forwarding rule (null when enable_backend=false)" +} + +output "hub_frontend_psc_fr_id" { + value = local.hub_present && var.enable_frontend ? google_compute_forwarding_rule.frontend_fr_hub[0].name : null + description = "Name of the hub-side frontend PSC forwarding rule (null when no hub or no frontend)" +} + +output "frontend_psc_ip_spoke" { + value = var.enable_frontend ? google_compute_address.frontend_address_spoke[0].address : null + description = "IP address of the spoke-side frontend PSC endpoint" +} + +output "backend_psc_ip_spoke" { + value = var.enable_backend ? google_compute_address.backend_address[0].address : null + description = "IP address of the spoke-side backend PSC endpoint" +} + +output "frontend_psc_ip_hub" { + value = local.hub_present && var.enable_frontend ? google_compute_address.frontend_address_hub[0].address : null + description = "IP address of the hub-side frontend PSC endpoint (null when no hub)" +} +``` + +- [ ] **Step 4: Validate fixture** + +```bash +cd modules/gcp/private-connectivity/tests/full-isolated +terraform init -backend=false +terraform validate +terraform plan -refresh=false +``` + +Expected: `Plan: 7 to add` (PSC subnet + 2 addresses + 2 forwarding rules for spoke + 1 address + 1 forwarding rule for hub). + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/private-connectivity/ +git commit -m "$(cat <<'EOF' +feat(gcp/private-connectivity): add PSC subnet, addresses, forwarding rules + +PSC subnet (spoke); backend (SCC) endpoint gated on enable_backend; +frontend endpoint (spoke) gated on enable_frontend; frontend endpoint +(hub) gated on hub_present AND enable_frontend. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 8: `modules/gcp/private-connectivity` — egress firewall rules + fixture + +**Files:** +- Modify: `modules/gcp/private-connectivity/firewall.tf` +- Modify: `modules/gcp/private-connectivity/tests/full-isolated/main.tf` (no change to assertions; the plan resource count grows) +- Create: `modules/gcp/private-connectivity/tests/no-egress/main.tf` (variant with `restrict_egress = false`) + +- [ ] **Step 1: Write `firewall.tf`** + +```hcl +# Egress firewall stack — only emitted when restrict_egress = true. +# Names follow the existing pattern from modules/gcp-with-psc-exfiltration-protection/firewall-spoke.tf +# and firewall-hub.tf to keep operator familiarity. + +# === Spoke deny-egress ================================================== +resource "google_compute_firewall" "spoke_default_deny_egress" { + count = var.restrict_egress ? 1 : 0 + + name = "${var.prefix}-spoke-${var.suffix}-default-deny-egress" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_self_link + + direction = "EGRESS" + priority = 1100 + destination_ranges = ["0.0.0.0/0"] + source_ranges = [] + + deny { + protocol = "all" + } +} + +# === Spoke allow Google APIs ============================================ +resource "google_compute_firewall" "spoke_allow_google_apis" { + count = var.restrict_egress ? 1 : 0 + + name = "${var.prefix}-spoke-${var.suffix}-to-google-apis" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_self_link + + direction = "EGRESS" + priority = 1000 + destination_ranges = [ + "199.36.153.4/30", + "199.36.153.8/30", + "34.126.0.0/18" + ] + + allow { + protocol = "all" + } +} + +# === Spoke allow Databricks control plane (to PSC IPs) ================== +resource "google_compute_firewall" "spoke_allow_ctl_plane" { + count = var.restrict_egress && var.enable_frontend && var.enable_backend ? 1 : 0 + + name = "${var.prefix}-spoke-${var.suffix}-to-databricks-control-plane" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_self_link + + direction = "EGRESS" + priority = 1000 + destination_ranges = [ + "${google_compute_forwarding_rule.backend_fr[0].ip_address}/32", + "${google_compute_forwarding_rule.frontend_fr_spoke[0].ip_address}/32" + ] + + allow { + protocol = "tcp" + ports = ["443"] + } +} + +# === Spoke allow managed Hive (conditional on hive_metastore_ip) ======== +resource "google_compute_firewall" "spoke_allow_hive" { + count = var.restrict_egress && local.hive_metastore_ip != "" ? 1 : 0 + + name = "${var.prefix}-spoke-${var.suffix}-to-${var.google_region}-managed-hive" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_self_link + + direction = "EGRESS" + priority = 1000 + destination_ranges = ["${local.hive_metastore_ip}/32"] + + allow { + protocol = "tcp" + ports = ["3306"] + } +} + +# === Hub ingress from spoke ============================================= +resource "google_compute_firewall" "hub_ingress" { + count = var.restrict_egress && local.hub_present ? 1 : 0 + + name = "${var.prefix}-hub-${var.suffix}-ingress" + project = var.hub_vpc_google_project + network = var.hub_vpc_self_link + + direction = "INGRESS" + priority = 1000 + destination_ranges = [] + source_ranges = [var.spoke_vpc_cidr] + + allow { + protocol = "all" + } +} +``` + +- [ ] **Step 2: Write `tests/no-egress/main.tf`** + +```hcl +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-spoke" + region = "us-central1" +} + +module "pc" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + + spoke_vpc_id = "projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_google_project = "fixture-spoke" + spoke_vpc_cidr = "10.0.0.0/16" + + enable_frontend = true + enable_backend = false + restrict_egress = false + psc_subnet_cidr = "10.0.255.0/28" +} +``` + +- [ ] **Step 3: Validate both fixtures** + +```bash +cd modules/gcp/private-connectivity/tests/full-isolated && terraform init -backend=false && terraform validate && terraform plan -refresh=false +``` + +Expected: `Plan: 12 to add` (7 from Task 7 + 5 firewall rules: deny + google-apis + ctl-plane + hive (only if `hive_metastore_ip` set, which it's not in the fixture — so 0) + hub-ingress = 4 firewall rules in this fixture → 11 total. If `hive_metastore_ip` is set in the fixture, expect 12). + +Note: fixture has `hive_metastore_ip` unset and `default_hive_metastore_ips` map is empty → `local.hive_metastore_ip = ""` → hive firewall is NOT emitted. So fixture should plan: 7 PSC + 4 firewall = 11 resources. + +```bash +cd ../no-egress && terraform init -backend=false && terraform validate && terraform plan -refresh=false +``` + +Expected: `Plan: 3 to add` (PSC subnet + 1 frontend address + 1 frontend forwarding rule). + +- [ ] **Step 4: Commit** + +```bash +git add modules/gcp/private-connectivity/ +git commit -m "$(cat <<'EOF' +feat(gcp/private-connectivity): add egress firewall stack + +Spoke deny-egress (priority 1100), allow-google-apis, allow control +plane (to PSC IPs), allow managed-hive (conditional on metastore IP), +and hub ingress from spoke CIDR. All gated on restrict_egress. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 9: `modules/gcp/account` — scaffold + variables + versions + +**Files:** +- Create: `modules/gcp/account/versions.tf` +- Create: `modules/gcp/account/variables.tf` +- Create: `modules/gcp/account/main.tf` +- Create: `modules/gcp/account/vpc-endpoints.tf` +- Create: `modules/gcp/account/pas.tf` +- Create: `modules/gcp/account/outputs.tf` +- Create: `modules/gcp/account/Makefile` +- Create: `modules/gcp/account/README.md` + +- [ ] **Step 1: `versions.tf`** + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { + source = "databricks/databricks" + version = ">= 1.0" + } + } +} +``` + +- [ ] **Step 2: `variables.tf`** + +```hcl +variable "prefix" { type = string } +variable "suffix" { type = string } +variable "workspace_name" { type = string default = null } +variable "databricks_account_id" { type = string } +variable "google_project" { type = string } +variable "google_region" { type = string } + +variable "vpc_source" { + type = string + validation { + condition = contains(["databricks_managed", "create", "existing"], var.vpc_source) + error_message = "vpc_source must be one of: databricks_managed, create, existing." + } +} + +variable "spoke_vpc_name" { type = string default = null } +variable "spoke_subnet_name" { type = string default = null } +variable "spoke_vpc_google_project" { type = string default = null } +variable "hub_vpc_google_project" { type = string default = null } + +# Forwarding-rule names from private-connectivity module (gate vpc_endpoint creation) +variable "frontend_psc_fr_id" { type = string default = null } +variable "backend_psc_fr_id" { type = string default = null } +variable "hub_frontend_psc_fr_id" { type = string default = null } + +variable "enable_frontend" { type = bool default = false } +variable "enable_backend" { type = bool default = false } +variable "private_access_only" { type = bool default = false } + +variable "nat_dependency" { + type = any + default = null + description = "Opaque value used as depends_on for the workspace to ensure NAT readiness" +} +``` + +- [ ] **Step 3: Empty `main.tf`, `vpc-endpoints.tf`, `pas.tf`, `outputs.tf` placeholders** + +`main.tf`: + +```hcl +locals { + workspace_name = coalesce(var.workspace_name, "${var.prefix}-ws-${var.suffix}") + emit_mws_networks = var.vpc_source != "databricks_managed" + emit_vpc_endpoints = var.frontend_psc_fr_id != null && var.backend_psc_fr_id != null + emit_pas = var.private_access_only +} +``` + +`outputs.tf`: + +```hcl +output "workspace_id" { value = null description = "Databricks workspace ID" } +output "workspace_url" { value = null description = "Databricks workspace URL" } +output "network_id" { value = null description = "mws_networks ID (null when databricks_managed)" } +output "frontend_endpoint_id" { value = null description = "Frontend mws_vpc_endpoint ID (null when no PSC)" } +output "backend_endpoint_id" { value = null description = "Backend mws_vpc_endpoint ID (null when no PSC)" } +output "transit_endpoint_id" { value = null description = "Hub-side mws_vpc_endpoint ID (null when no hub)" } +``` + +`Makefile`: + +```makefile +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . +``` + +`README.md`: + +```markdown +# modules/gcp/account + +All `databricks_mws_*` resources for the GCP composer: `mws_networks`, `mws_workspaces`, `mws_vpc_endpoint`, `mws_private_access_settings`. + + + +``` + +- [ ] **Step 4: Validate** + +```bash +cd modules/gcp/account && terraform init -backend=false && terraform validate +``` + +Expected: validate passes. + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/account/ +git commit -m "$(cat <<'EOF' +feat(gcp/account): scaffold module + +Adds modules/gcp/account with variable declarations, locals for +derived flags, empty main.tf/vpc-endpoints.tf/pas.tf, null outputs. +Resources added in follow-up tasks. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 10: `modules/gcp/account` — databricks-managed workspace shape + fixture + +**Files:** +- Modify: `modules/gcp/account/main.tf` +- Modify: `modules/gcp/account/outputs.tf` +- Create: `modules/gcp/account/tests/databricks-managed/main.tf` + +- [ ] **Step 1: Write fixture** + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { + source = "databricks/databricks" + } + } +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "account" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + vpc_source = "databricks_managed" +} +``` + +- [ ] **Step 2: Add `databricks_mws_workspaces` to `main.tf`** + +Append to `modules/gcp/account/main.tf`: + +```hcl +resource "databricks_mws_workspaces" "this" { + account_id = var.databricks_account_id + workspace_name = local.workspace_name + location = var.google_region + + cloud_resource_container { + gcp { + project_id = var.google_project + } + } + + network_id = local.emit_mws_networks ? databricks_mws_networks.this[0].network_id : null + private_access_settings_id = local.emit_pas ? databricks_mws_private_access_settings.this[0].private_access_settings_id : null + + token { + comment = "Terraform" + } + + depends_on = [var.nat_dependency] +} +``` + +- [ ] **Step 3: Wire workspace outputs** + +```hcl +output "workspace_id" { + value = databricks_mws_workspaces.this.workspace_id + description = "Databricks workspace ID" +} + +output "workspace_url" { + value = databricks_mws_workspaces.this.workspace_url + description = "Databricks workspace URL" +} +``` + +- [ ] **Step 4: Validate fixture** + +```bash +cd modules/gcp/account/tests/databricks-managed +terraform init -backend=false +terraform validate +``` + +Expected: validate passes. (Plan cannot run without real Databricks credentials; validate is sufficient for this fixture.) + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/account/ +git commit -m "$(cat <<'EOF' +feat(gcp/account): add databricks_mws_workspaces resource + +Workspace resource with conditional network_id and +private_access_settings_id (both null when databricks_managed). + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 11: `modules/gcp/account` — mws_networks (customer VPC) + fixture + +**Files:** +- Modify: `modules/gcp/account/main.tf` +- Modify: `modules/gcp/account/outputs.tf` +- Create: `modules/gcp/account/tests/byovpc/main.tf` + +- [ ] **Step 1: Write fixture** + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { + source = "databricks/databricks" + } + } +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "account" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + vpc_source = "create" + spoke_vpc_name = "fixture-spoke-vpc-abc123" + spoke_subnet_name = "fixture-subnet-abc123" + spoke_vpc_google_project = "fixture-spoke" +} +``` + +- [ ] **Step 2: Append `databricks_mws_networks` to `main.tf`** + +```hcl +resource "databricks_mws_networks" "this" { + count = local.emit_mws_networks ? 1 : 0 + + account_id = var.databricks_account_id + network_name = "${var.prefix}-ntw-${var.suffix}" + + gcp_network_info { + network_project_id = var.spoke_vpc_google_project + vpc_id = var.spoke_vpc_name + subnet_id = var.spoke_subnet_name + subnet_region = var.google_region + } + + dynamic "vpc_endpoints" { + for_each = local.emit_vpc_endpoints ? [1] : [] + content { + dataplane_relay = [databricks_mws_vpc_endpoint.backend[0].vpc_endpoint_id] + rest_api = [databricks_mws_vpc_endpoint.frontend[0].vpc_endpoint_id] + } + } +} +``` + +- [ ] **Step 3: Wire `network_id` output** + +```hcl +output "network_id" { + value = local.emit_mws_networks ? databricks_mws_networks.this[0].network_id : null + description = "mws_networks ID (null when databricks_managed)" +} +``` + +- [ ] **Step 4: Validate fixture** + +```bash +cd modules/gcp/account/tests/byovpc +terraform init -backend=false +terraform validate +``` + +Expected: validate passes (note: references to `databricks_mws_vpc_endpoint.backend[0]` and `frontend[0]` resolve at plan time even if `emit_vpc_endpoints` is false because they're inside a `dynamic` block; the for_each guard prevents evaluation). + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/account/ +git commit -m "$(cat <<'EOF' +feat(gcp/account): add databricks_mws_networks for customer VPC + +mws_networks emitted when vpc_source != databricks_managed; the +vpc_endpoints block is conditionally populated via dynamic when both +frontend and backend forwarding-rule IDs are provided. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 12: `modules/gcp/account` — mws_vpc_endpoint resources + fixture + +**Files:** +- Modify: `modules/gcp/account/vpc-endpoints.tf` +- Modify: `modules/gcp/account/outputs.tf` +- Create: `modules/gcp/account/tests/psc-with-pas/main.tf` + +- [ ] **Step 1: Write `vpc-endpoints.tf`** + +```hcl +resource "databricks_mws_vpc_endpoint" "frontend" { + count = var.enable_frontend && var.frontend_psc_fr_id != null ? 1 : 0 + + account_id = var.databricks_account_id + vpc_endpoint_name = "${var.prefix}-ws-ep-${var.suffix}" + + gcp_vpc_endpoint_info { + project_id = var.spoke_vpc_google_project + psc_endpoint_name = var.frontend_psc_fr_id + endpoint_region = var.google_region + } +} + +resource "databricks_mws_vpc_endpoint" "backend" { + count = var.enable_backend && var.backend_psc_fr_id != null ? 1 : 0 + + account_id = var.databricks_account_id + vpc_endpoint_name = "${var.prefix}-scc-ep-${var.suffix}" + + gcp_vpc_endpoint_info { + project_id = var.spoke_vpc_google_project + psc_endpoint_name = var.backend_psc_fr_id + endpoint_region = var.google_region + } +} + +resource "databricks_mws_vpc_endpoint" "transit" { + count = var.enable_frontend && var.hub_frontend_psc_fr_id != null ? 1 : 0 + + account_id = var.databricks_account_id + vpc_endpoint_name = "${var.prefix}-hub-ep-${var.suffix}" + + gcp_vpc_endpoint_info { + project_id = var.hub_vpc_google_project + psc_endpoint_name = var.hub_frontend_psc_fr_id + endpoint_region = var.google_region + } +} +``` + +- [ ] **Step 2: Wire endpoint outputs** + +```hcl +output "frontend_endpoint_id" { + value = var.enable_frontend && var.frontend_psc_fr_id != null ? databricks_mws_vpc_endpoint.frontend[0].vpc_endpoint_id : null + description = "Frontend mws_vpc_endpoint ID (null when no PSC)" +} + +output "backend_endpoint_id" { + value = var.enable_backend && var.backend_psc_fr_id != null ? databricks_mws_vpc_endpoint.backend[0].vpc_endpoint_id : null + description = "Backend mws_vpc_endpoint ID (null when no PSC)" +} + +output "transit_endpoint_id" { + value = var.enable_frontend && var.hub_frontend_psc_fr_id != null ? databricks_mws_vpc_endpoint.transit[0].vpc_endpoint_id : null + description = "Hub-side mws_vpc_endpoint ID (null when no hub)" +} +``` + +- [ ] **Step 3: Write fixture `tests/psc-with-pas/main.tf`** + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { + source = "databricks/databricks" + } + } +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "account" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + vpc_source = "create" + spoke_vpc_name = "fixture-spoke-vpc-abc123" + spoke_subnet_name = "fixture-subnet-abc123" + spoke_vpc_google_project = "fixture-spoke" + hub_vpc_google_project = "fixture-hub" + + frontend_psc_fr_id = "fixture-psc-ws-ep-abc123" + backend_psc_fr_id = "fixture-psc-scc-ep-abc123" + hub_frontend_psc_fr_id = "fixture-hub-psc-ws-ep-abc123" + + enable_frontend = true + enable_backend = true + private_access_only = true +} +``` + +- [ ] **Step 4: Validate** + +```bash +cd modules/gcp/account/tests/psc-with-pas && terraform init -backend=false && terraform validate +``` + +Expected: validate passes. + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/account/ +git commit -m "$(cat <<'EOF' +feat(gcp/account): add databricks_mws_vpc_endpoint resources + +Frontend, backend (SCC), and transit (hub) mws_vpc_endpoints, each +gated on its enable_* flag and the presence of the corresponding +forwarding-rule name from private-connectivity. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 13: `modules/gcp/account` — private access settings + +**Files:** +- Modify: `modules/gcp/account/pas.tf` + +- [ ] **Step 1: Write `pas.tf`** + +```hcl +resource "databricks_mws_private_access_settings" "this" { + count = local.emit_pas ? 1 : 0 + + account_id = var.databricks_account_id + private_access_settings_name = "${var.prefix}-pas-${var.suffix}" + region = var.google_region + public_access_enabled = false + private_access_level = "ACCOUNT" +} +``` + +- [ ] **Step 2: Validate (reuse `tests/psc-with-pas` fixture)** + +```bash +cd modules/gcp/account/tests/psc-with-pas && terraform validate +``` + +Expected: validate passes. + +- [ ] **Step 3: Commit** + +```bash +git add modules/gcp/account/ +git commit -m "$(cat <<'EOF' +feat(gcp/account): add mws_private_access_settings + +Emitted when private_access_only=true; public_access_enabled=false, +private_access_level=ACCOUNT. Workspace references via +private_access_settings_id. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 14: `modules/gcp/dns` — scaffold + variables + +**Files:** +- Create: `modules/gcp/dns/versions.tf` +- Create: `modules/gcp/dns/variables.tf` +- Create: `modules/gcp/dns/hub.tf` (empty) +- Create: `modules/gcp/dns/spoke.tf` (empty) +- Create: `modules/gcp/dns/outputs.tf` +- Create: `modules/gcp/dns/Makefile` +- Create: `modules/gcp/dns/README.md` + +- [ ] **Step 1: `versions.tf`** + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0" + } + } +} +``` + +- [ ] **Step 2: `variables.tf`** + +```hcl +variable "prefix" { type = string } +variable "google_region" { type = string } + +# Hub +variable "hub_vpc_id" { type = string } +variable "hub_vpc_self_link" { type = string } +variable "hub_vpc_google_project" { type = string } + +# Spoke +variable "spoke_vpc_id" { type = string } +variable "spoke_vpc_self_link" { type = string } +variable "spoke_vpc_google_project" { type = string } + +# Workspace +variable "workspace_url" { type = string } + +# PSC IPs +variable "frontend_psc_ip_spoke" { type = string } +variable "frontend_psc_ip_hub" { type = string default = null } +variable "backend_psc_ip_spoke" { type = string } +``` + +- [ ] **Step 3: Empty `outputs.tf`, `Makefile`, `README.md`** + +`outputs.tf`: + +```hcl +# This module has no outputs; DNS records are terminal. +``` + +`Makefile`: same template as Task 6. + +`README.md`: + +```markdown +# modules/gcp/dns + +Private DNS zones (hub + spoke) used with restricted-egress workspaces. + + + +``` + +- [ ] **Step 4: Validate** + +```bash +cd modules/gcp/dns && terraform init -backend=false && terraform validate +``` + +Expected: validate passes. + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/dns/ +git commit -m "$(cat <<'EOF' +feat(gcp/dns): scaffold module with variables + +Variable declarations for hub + spoke DNS zones. Resources added in +follow-up task. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 15: `modules/gcp/dns` — hub + spoke zones and records + fixture + +**Files:** +- Modify: `modules/gcp/dns/hub.tf` +- Modify: `modules/gcp/dns/spoke.tf` +- Create: `modules/gcp/dns/tests/hub-and-spoke/main.tf` + +- [ ] **Step 1: Write fixture** + +```hcl +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-spoke" + region = "us-central1" +} + +module "dns" { + source = "../.." + + prefix = "fixture" + google_region = "us-central1" + + hub_vpc_id = "projects/fixture-hub/global/networks/hub-vpc" + hub_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-hub/global/networks/hub-vpc" + hub_vpc_google_project = "fixture-hub" + + spoke_vpc_id = "projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_google_project = "fixture-spoke" + + workspace_url = "https://1234567890123456.7.gcp.databricks.com" + + frontend_psc_ip_spoke = "10.0.255.4" + frontend_psc_ip_hub = "10.1.0.10" + backend_psc_ip_spoke = "10.0.255.5" +} +``` + +- [ ] **Step 2: Write `hub.tf`** + +```hcl +locals { + # Regex extracts the workspace DNS id (numeric.numeric) from the URL. + # Matches the behavior of the legacy gcp-with-psc-exfiltration-protection module. + workspace_dns_id = regex("[0-9]+\\.[0-9]+", var.workspace_url) +} + +# === gcp.databricks.com (hub) ============================================ +resource "google_dns_managed_zone" "hub_dbx" { + name = "${var.prefix}-hub-gcp-databricks-com" + project = var.hub_vpc_google_project + dns_name = "gcp.databricks.com." + description = "Private DNS zone for Databricks PSC management" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.hub_vpc_id + } + } +} + +resource "google_dns_record_set" "hub_workspace_url" { + name = "${local.workspace_dns_id}.${google_dns_managed_zone.hub_dbx.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.hub_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_hub] +} + +resource "google_dns_record_set" "hub_psc_auth" { + name = "${var.google_region}.psc-auth.${google_dns_managed_zone.hub_dbx.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.hub_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_hub] +} + +resource "google_dns_record_set" "hub_dp" { + name = "dp-${local.workspace_dns_id}.${google_dns_managed_zone.hub_dbx.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.hub_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_hub] +} + +# === gcr.io ============================================================== +resource "google_dns_managed_zone" "gcr" { + name = "${var.prefix}-gcr-io" + project = var.hub_vpc_google_project + dns_name = "gcr.io." + description = "Private DNS zone for GCR private resolution" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.hub_vpc_id + } + } +} + +resource "google_dns_record_set" "gcr_cname" { + name = "*.${google_dns_managed_zone.gcr.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.gcr.name + type = "CNAME" + ttl = 300 + rrdatas = ["gcr.io."] +} + +resource "google_dns_record_set" "gcr_a" { + name = google_dns_managed_zone.gcr.dns_name + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.gcr.name + type = "A" + ttl = 300 + rrdatas = ["199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11"] +} + +# === googleapis.com ====================================================== +resource "google_dns_managed_zone" "google_apis" { + name = "${var.prefix}-google-apis" + project = var.hub_vpc_google_project + dns_name = "googleapis.com." + description = "Private DNS zone for Google APIs resolution" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.hub_vpc_id + } + } +} + +resource "google_dns_record_set" "google_apis_cname" { + name = "*.${google_dns_managed_zone.google_apis.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.google_apis.name + type = "CNAME" + ttl = 300 + rrdatas = ["restricted.googleapis.com."] +} + +resource "google_dns_record_set" "google_apis_a" { + name = "restricted.${google_dns_managed_zone.google_apis.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.google_apis.name + type = "A" + ttl = 300 + rrdatas = ["199.36.153.4", "199.36.153.5", "199.36.153.6", "199.36.153.7"] +} + +# === pkg.dev ============================================================= +resource "google_dns_managed_zone" "pkg_dev" { + name = "${var.prefix}-pkg-dev" + project = var.hub_vpc_google_project + dns_name = "pkg.dev." + description = "Private DNS zone for Go Packages resolution" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.hub_vpc_id + } + } +} + +resource "google_dns_record_set" "pkg_dev_cname" { + name = "*.${google_dns_managed_zone.pkg_dev.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.pkg_dev.name + type = "CNAME" + ttl = 300 + rrdatas = ["pkg.dev."] +} + +resource "google_dns_record_set" "pkg_dev_a" { + name = google_dns_managed_zone.pkg_dev.dns_name + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.pkg_dev.name + type = "A" + ttl = 300 + rrdatas = ["199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11"] +} +``` + +- [ ] **Step 3: Write `spoke.tf`** + +```hcl +# === gcp.databricks.com (spoke) ========================================== +resource "google_dns_managed_zone" "spoke_dbx" { + name = "${var.prefix}-spoke-gcp-databricks-com" + project = var.spoke_vpc_google_project + dns_name = "gcp.databricks.com." + description = "Private DNS zone for Databricks PSC management" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.spoke_vpc_id + } + } +} + +resource "google_dns_record_set" "spoke_workspace_url" { + name = "${local.workspace_dns_id}.${google_dns_managed_zone.spoke_dbx.dns_name}" + project = var.spoke_vpc_google_project + managed_zone = google_dns_managed_zone.spoke_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_spoke] +} + +resource "google_dns_record_set" "spoke_dp" { + name = "dp-${local.workspace_dns_id}.${google_dns_managed_zone.spoke_dbx.dns_name}" + project = var.spoke_vpc_google_project + managed_zone = google_dns_managed_zone.spoke_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_spoke] +} + +resource "google_dns_record_set" "spoke_tunnel" { + name = "tunnel.${var.google_region}.${google_dns_managed_zone.spoke_dbx.dns_name}" + project = var.spoke_vpc_google_project + managed_zone = google_dns_managed_zone.spoke_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.backend_psc_ip_spoke] +} +``` + +- [ ] **Step 4: Validate fixture** + +```bash +cd modules/gcp/dns/tests/hub-and-spoke && terraform init -backend=false && terraform validate && terraform plan -refresh=false +``` + +Expected: `Plan: 16 to add` (5 zones + 11 record sets: 3 hub_dbx + 2 gcr + 2 google_apis + 2 pkg_dev + 3 spoke = 12. Let me recount: hub_dbx zone + 3 records = 4. gcr zone + 2 records = 3. google_apis zone + 2 records = 3. pkg_dev zone + 2 records = 3. spoke_dbx zone + 3 records = 4. Total = 17). + +Expected: `Plan: 17 to add`. + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/dns/ +git commit -m "$(cat <<'EOF' +feat(gcp/dns): add hub and spoke private DNS zones + +Hub: gcp.databricks.com, gcr.io, googleapis.com, pkg.dev. +Spoke: gcp.databricks.com with workspace/dp/tunnel records. +workspace_dns_id is regex-extracted from workspace_url. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 16: `modules/gcp/databricks-workspace` — composer scaffold + variables + +**Files:** +- Create: `modules/gcp/databricks-workspace/versions.tf` +- Create: `modules/gcp/databricks-workspace/variables.tf` +- Create: `modules/gcp/databricks-workspace/main.tf` +- Create: `modules/gcp/databricks-workspace/outputs.tf` +- Create: `modules/gcp/databricks-workspace/Makefile` +- Create: `modules/gcp/databricks-workspace/README.md` + +- [ ] **Step 1: `versions.tf`** + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0" + } + databricks = { + source = "databricks/databricks" + version = ">= 1.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.0" + } + } +} +``` + +- [ ] **Step 2: `variables.tf` — full composer API as specified** + +```hcl +# === Identity =========================================================== +variable "prefix" { type = string } +variable "databricks_account_id" { type = string } +variable "google_project" { type = string } +variable "google_region" { type = string } +variable "workspace_name" { type = string default = null } +variable "tags" { type = map(string) default = {} } + +# === VPC source ========================================================= +variable "vpc_source" { + type = string + default = "databricks_managed" + description = "One of: databricks_managed, create, existing" + validation { + condition = contains(["databricks_managed", "create", "existing"], var.vpc_source) + error_message = "vpc_source must be one of: databricks_managed, create, existing." + } +} + +# When vpc_source = "create" +variable "spoke_vpc_cidr" { type = string default = null } +variable "subnet_cidr" { type = string default = null } +variable "pod_cidr" { type = string default = null } +variable "svc_cidr" { type = string default = null } + +# When vpc_source = "existing" +variable "existing_vpc_name" { type = string default = null } +variable "existing_subnet_name" { type = string default = null } + +# === Connectivity feature flags ========================================= +variable "private_link_frontend" { type = bool default = false } +variable "private_link_backend" { type = bool default = false } +variable "private_access_only" { type = bool default = false } +variable "restricted_egress" { type = bool default = false } + +# === Required when restricted_egress = true ============================= +variable "hub_vpc_google_project" { type = string default = null } +variable "spoke_vpc_google_project" { type = string default = null } +variable "is_spoke_vpc_shared" { type = bool default = false } +variable "hub_vpc_cidr" { type = string default = null } +variable "psc_subnet_cidr" { type = string default = null } +variable "hive_metastore_ip" { type = string default = null } +``` + +- [ ] **Step 3: `main.tf` — locals, random suffix, preconditions (no submodule wiring yet)** + +```hcl +locals { + databricks_managed = var.vpc_source == "databricks_managed" + create_vpc = var.vpc_source == "create" + use_existing_vpc = var.vpc_source == "existing" + + any_private_link = var.private_link_frontend || var.private_link_backend + spoke_project = coalesce(var.spoke_vpc_google_project, var.google_project) +} + +resource "random_string" "suffix" { + length = 6 + special = false + upper = false + + lifecycle { + ignore_changes = [special, upper] + } +} + +# Cross-variable preconditions. Terraform doesn't support cross-var +# validation in variable blocks; we use a null_resource lifecycle.precondition +# stack instead. +resource "null_resource" "preconditions" { + lifecycle { + precondition { + condition = !var.restricted_egress || local.create_vpc + error_message = "restricted_egress=true requires vpc_source=\"create\" (hub-spoke topology needs us to own both VPCs)." + } + precondition { + condition = !var.restricted_egress || local.any_private_link + error_message = "restricted_egress=true requires at least one of private_link_frontend or private_link_backend." + } + precondition { + condition = !var.restricted_egress || (var.hub_vpc_google_project != null && var.hub_vpc_cidr != null && var.psc_subnet_cidr != null) + error_message = "restricted_egress=true requires hub_vpc_google_project, hub_vpc_cidr, and psc_subnet_cidr." + } + precondition { + condition = !local.create_vpc || (var.spoke_vpc_cidr != null && var.subnet_cidr != null) + error_message = "vpc_source=\"create\" requires spoke_vpc_cidr and subnet_cidr." + } + precondition { + condition = !local.use_existing_vpc || (var.existing_vpc_name != null && var.existing_subnet_name != null) + error_message = "vpc_source=\"existing\" requires existing_vpc_name and existing_subnet_name." + } + precondition { + condition = !local.databricks_managed || (!var.private_link_frontend && !var.private_link_backend && !var.restricted_egress) + error_message = "vpc_source=\"databricks_managed\" forbids private_link_frontend, private_link_backend, and restricted_egress." + } + } +} +``` + +Note: `null_resource` requires the `hashicorp/null` provider; add it to `versions.tf`. Update `versions.tf`: + +```hcl + null = { + source = "hashicorp/null" + version = ">= 3.0" + } +``` + +- [ ] **Step 4: Empty `outputs.tf`** + +```hcl +output "workspace_id" { value = null description = "Databricks workspace ID" } +output "workspace_url" { value = null description = "Databricks workspace URL" } +output "network_id" { value = null description = "mws_networks ID (null when databricks_managed)" } +output "vpc_id" { value = null description = "Spoke VPC ID (null when databricks_managed)" } +output "spoke_vpc_id" { value = null description = "Spoke VPC ID (null when databricks_managed)" } +output "hub_vpc_id" { value = null description = "Hub VPC ID (null when not restricted_egress)" } +output "suffix" { value = random_string.suffix.result description = "Random suffix used in resource names" } +``` + +- [ ] **Step 5: Makefile + README placeholder** (same template as previous modules) + +- [ ] **Step 6: Validate** + +```bash +cd modules/gcp/databricks-workspace && terraform init -backend=false && terraform validate +``` + +Expected: validate passes. + +- [ ] **Step 7: Commit** + +```bash +git add modules/gcp/databricks-workspace/ +git commit -m "$(cat <<'EOF' +feat(gcp/databricks-workspace): scaffold composer with preconditions + +Composer module with full variable API, random_string suffix, locals +for derived flags, and null_resource.preconditions stack enforcing all +cross-variable rules from the spec. Submodule wiring follows in next +tasks. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 17: Composer — wire `network`, `private-connectivity`, `account`, `dns` submodules + +**Files:** +- Modify: `modules/gcp/databricks-workspace/main.tf` +- Modify: `modules/gcp/databricks-workspace/outputs.tf` + +- [ ] **Step 1: Append submodule blocks to `main.tf`** + +```hcl +module "network" { + source = "../network" + count = local.databricks_managed ? 0 : 1 + + prefix = var.prefix + suffix = random_string.suffix.result + google_region = var.google_region + vpc_source = var.vpc_source + spoke_vpc_google_project = local.spoke_project + + spoke_vpc_cidr = var.spoke_vpc_cidr + subnet_cidr = var.subnet_cidr + pod_cidr = var.pod_cidr + svc_cidr = var.svc_cidr + + existing_vpc_name = var.existing_vpc_name + existing_subnet_name = var.existing_subnet_name + + create_hub = var.restricted_egress + hub_vpc_google_project = var.hub_vpc_google_project + hub_vpc_cidr = var.hub_vpc_cidr + is_spoke_vpc_shared = var.is_spoke_vpc_shared + workspace_google_project = var.google_project +} + +module "private_connectivity" { + source = "../private-connectivity" + count = local.any_private_link ? 1 : 0 + + prefix = var.prefix + suffix = random_string.suffix.result + google_region = var.google_region + + spoke_vpc_id = module.network[0].spoke_vpc_id + spoke_vpc_self_link = module.network[0].spoke_vpc_self_link + spoke_vpc_google_project = local.spoke_project + spoke_vpc_cidr = var.spoke_vpc_cidr + + hub_vpc_id = var.restricted_egress ? module.network[0].hub_vpc_id : null + hub_vpc_self_link = var.restricted_egress ? module.network[0].hub_vpc_self_link : null + hub_vpc_google_project = var.hub_vpc_google_project + hub_subnet_name = var.restricted_egress ? module.network[0].hub_subnet_name : null + hub_vpc_cidr = var.hub_vpc_cidr + + enable_frontend = var.private_link_frontend + enable_backend = var.private_link_backend + restrict_egress = var.restricted_egress + psc_subnet_cidr = var.psc_subnet_cidr + + hive_metastore_ip = var.hive_metastore_ip +} + +module "account" { + source = "../account" + + prefix = var.prefix + suffix = random_string.suffix.result + workspace_name = var.workspace_name + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + vpc_source = var.vpc_source + + spoke_vpc_name = local.databricks_managed ? null : module.network[0].spoke_vpc_name + spoke_subnet_name = local.databricks_managed ? null : module.network[0].spoke_subnet_name + spoke_vpc_google_project = local.spoke_project + hub_vpc_google_project = var.hub_vpc_google_project + + frontend_psc_fr_id = local.any_private_link ? module.private_connectivity[0].frontend_psc_fr_id : null + backend_psc_fr_id = local.any_private_link ? module.private_connectivity[0].backend_psc_fr_id : null + hub_frontend_psc_fr_id = local.any_private_link ? module.private_connectivity[0].hub_frontend_psc_fr_id : null + + enable_frontend = var.private_link_frontend + enable_backend = var.private_link_backend + private_access_only = var.private_access_only + + nat_dependency = local.databricks_managed ? null : module.network[0].nat_id +} + +module "dns" { + source = "../dns" + count = var.restricted_egress ? 1 : 0 + + prefix = var.prefix + google_region = var.google_region + + hub_vpc_id = module.network[0].hub_vpc_id + hub_vpc_self_link = module.network[0].hub_vpc_self_link + hub_vpc_google_project = var.hub_vpc_google_project + + spoke_vpc_id = module.network[0].spoke_vpc_id + spoke_vpc_self_link = module.network[0].spoke_vpc_self_link + spoke_vpc_google_project = local.spoke_project + + workspace_url = module.account.workspace_url + + frontend_psc_ip_spoke = module.private_connectivity[0].frontend_psc_ip_spoke + frontend_psc_ip_hub = module.private_connectivity[0].frontend_psc_ip_hub + backend_psc_ip_spoke = module.private_connectivity[0].backend_psc_ip_spoke +} +``` + +- [ ] **Step 2: Wire composer outputs** + +Replace `outputs.tf`: + +```hcl +output "workspace_id" { + value = module.account.workspace_id + description = "Databricks workspace ID" +} + +output "workspace_url" { + value = module.account.workspace_url + description = "Databricks workspace URL" +} + +output "network_id" { + value = module.account.network_id + description = "mws_networks ID (null when databricks_managed)" +} + +output "vpc_id" { + value = try(module.network[0].spoke_vpc_id, null) + description = "Spoke VPC ID (null when databricks_managed)" +} + +output "spoke_vpc_id" { + value = try(module.network[0].spoke_vpc_id, null) + description = "Spoke VPC ID (null when databricks_managed)" +} + +output "hub_vpc_id" { + value = try(module.network[0].hub_vpc_id, null) + description = "Hub VPC ID (null when not restricted_egress)" +} + +output "suffix" { + value = random_string.suffix.result + description = "Random suffix used in resource names" +} +``` + +- [ ] **Step 3: Validate** + +```bash +cd modules/gcp/databricks-workspace && terraform init -backend=false && terraform validate +``` + +Expected: validate passes. + +- [ ] **Step 4: Commit** + +```bash +git add modules/gcp/databricks-workspace/ +git commit -m "$(cat <<'EOF' +feat(gcp/databricks-workspace): wire submodules in composer + +Conditional module blocks for network, private-connectivity, dns +(each gated by appropriate flags) and always-on account module. +Composer outputs wired to module outputs with try() for nullable +network outputs. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 18: Composer — positive fixtures (basic / byovpc / existing / psc-isolated) + +**Files:** +- Create: `modules/gcp/databricks-workspace/tests/basic/main.tf` +- Create: `modules/gcp/databricks-workspace/tests/byovpc/main.tf` +- Create: `modules/gcp/databricks-workspace/tests/existing-vpc/main.tf` +- Create: `modules/gcp/databricks-workspace/tests/psc-isolated/main.tf` + +Each fixture follows this pattern: + +- [ ] **Step 1: Write `tests/basic/main.tf`** + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { + project = "fixture-workspace" + region = "us-central1" +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "databricks_managed" +} +``` + +- [ ] **Step 2: Write `tests/byovpc/main.tf`** + +Same provider block, then: + +```hcl +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "create" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" +} +``` + +- [ ] **Step 3: Write `tests/existing-vpc/main.tf`** + +```hcl +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "existing" + existing_vpc_name = "preexisting-vpc" + existing_subnet_name = "preexisting-subnet" +} +``` + +- [ ] **Step 4: Write `tests/psc-isolated/main.tf`** + +```hcl +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "create" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" + + private_link_frontend = true + private_link_backend = true + private_access_only = true + restricted_egress = true + + spoke_vpc_google_project = "fixture-spoke" + hub_vpc_google_project = "fixture-hub" + is_spoke_vpc_shared = true + hub_vpc_cidr = "10.1.0.0/24" + psc_subnet_cidr = "10.0.255.0/28" +} +``` + +- [ ] **Step 5: Validate every fixture** + +```bash +for d in basic byovpc existing-vpc psc-isolated; do + echo "=== $d ===" && cd modules/gcp/databricks-workspace/tests/$d && \ + terraform init -backend=false && terraform validate && cd - +done +``` + +Expected: all four validate passes. + +- [ ] **Step 6: Commit** + +```bash +git add modules/gcp/databricks-workspace/tests/ +git commit -m "$(cat <<'EOF' +test(gcp/databricks-workspace): positive fixtures for 4 scenarios + +basic (databricks_managed), byovpc (create), existing-vpc (existing), +and psc-isolated (create + all PSC flags + restricted_egress). +Each fixture validates the full module graph. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 19: Composer — negative fixtures (precondition failures) + +**Files:** +- Create: `modules/gcp/databricks-workspace/tests/negative-restricted-egress-managed/main.tf` +- Create: `modules/gcp/databricks-workspace/tests/negative-restricted-egress-missing-hub/main.tf` +- Create: `modules/gcp/databricks-workspace/tests/negative-existing-missing-name/main.tf` +- Create: `modules/gcp/databricks-workspace/tests/negative-managed-with-psc/main.tf` + +- [ ] **Step 1: Write each fixture** + +`negative-restricted-egress-managed/main.tf` (expect: precondition error "restricted_egress=true requires vpc_source=create"): + +```hcl +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { project = "f" region = "us-central1" } +provider "databricks" { host = "https://accounts.gcp.databricks.com" account_id = "00000000-0000-0000-0000-000000000000" } + +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "f" + google_region = "us-central1" + + vpc_source = "databricks_managed" + restricted_egress = true +} +``` + +`negative-restricted-egress-missing-hub/main.tf`: + +```hcl +# same provider/header +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "f" + google_region = "us-central1" + + vpc_source = "create" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" + private_link_frontend = true + private_link_backend = true + restricted_egress = true + # hub_vpc_google_project, hub_vpc_cidr, psc_subnet_cidr all null -> precondition fail +} +``` + +`negative-existing-missing-name/main.tf`: + +```hcl +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "f" + google_region = "us-central1" + + vpc_source = "existing" + # existing_vpc_name / existing_subnet_name null -> precondition fail +} +``` + +`negative-managed-with-psc/main.tf`: + +```hcl +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "f" + google_region = "us-central1" + + vpc_source = "databricks_managed" + private_link_frontend = true # forbidden with databricks_managed +} +``` + +- [ ] **Step 2: Verify each fixture fails at plan time** + +```bash +for d in negative-restricted-egress-managed negative-restricted-egress-missing-hub negative-existing-missing-name negative-managed-with-psc; do + echo "=== $d ===" && cd modules/gcp/databricks-workspace/tests/$d && \ + terraform init -backend=false && \ + if terraform plan -refresh=false; then + echo "FAIL: $d should have failed plan"; exit 1 + else + echo "OK: $d failed plan as expected" + fi && cd - +done +``` + +Expected: each fixture fails at plan time with a precondition error message matching the spec table. + +- [ ] **Step 3: Commit** + +```bash +git add modules/gcp/databricks-workspace/tests/ +git commit -m "$(cat <<'EOF' +test(gcp/databricks-workspace): negative fixtures for preconditions + +Four fixtures, each violating one precondition rule from the spec. +Each fixture must fail `terraform plan` with a clear error message. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 20: Relocate `modules/gcp-sa-provisioning` → `modules/gcp/service-account` + +**Files:** +- Move: `modules/gcp-sa-provisioning/` → `modules/gcp/service-account/` +- Create: `modules/gcp-sa-provisioning/README.md` (deprecation stub) + +- [ ] **Step 1: `git mv` the directory** + +```bash +git mv modules/gcp-sa-provisioning modules/gcp/service-account +``` + +- [ ] **Step 2: Update the Makefile path inside the relocated module** + +Read `modules/gcp/service-account/Makefile`. The relative path to `.terraform-docs.yml` needs to deepen by one level: `../../.terraform-docs.yml` → `../../../.terraform-docs.yml`. Edit: + +```makefile +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . +``` + +- [ ] **Step 3: Create deprecation stub at the old path** + +```bash +mkdir -p modules/gcp-sa-provisioning +``` + +Write `modules/gcp-sa-provisioning/README.md`: + +```markdown +# DEPRECATED — moved to `modules/gcp/service-account/` + +This module has been relocated to [`../gcp/service-account/`](../gcp/service-account/). + +All variables, outputs, and resource addresses are unchanged. Update your +module `source` from: + +```hcl +source = "github.com/databricks/terraform-databricks-examples/modules/gcp-sa-provisioning" +``` + +to: + +```hcl +source = "github.com/databricks/terraform-databricks-examples/modules/gcp/service-account" +``` + +This stub will be removed in PR 6 of the GCP modules refactor. +``` + +- [ ] **Step 4: Validate the relocated module** + +```bash +cd modules/gcp/service-account && terraform init -backend=false && terraform validate +``` + +Expected: validate passes (no functional changes). + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/service-account/ modules/gcp-sa-provisioning/README.md +git commit -m "$(cat <<'EOF' +refactor(gcp/service-account): relocate from modules/gcp-sa-provisioning + +git mv only; no functional changes. Old path has a deprecation README +pointing to the new location. Makefile updated for new depth. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 21: Relocate `modules/gcp-unity-catalog` → `modules/gcp/unity-catalog` + +Same pattern as Task 20. + +- [ ] **Step 1: `git mv`** + +```bash +git mv modules/gcp-unity-catalog modules/gcp/unity-catalog +``` + +- [ ] **Step 2: Update `modules/gcp/unity-catalog/Makefile`** to use `../../../.terraform-docs.yml`. + +- [ ] **Step 3: Write `modules/gcp-unity-catalog/README.md`** (deprecation stub, same template as Task 20). + +- [ ] **Step 4: Validate** + +```bash +cd modules/gcp/unity-catalog && terraform init -backend=false && terraform validate +``` + +- [ ] **Step 5: Commit** + +```bash +git add modules/gcp/unity-catalog/ modules/gcp-unity-catalog/README.md +git commit -m "$(cat <<'EOF' +refactor(gcp/unity-catalog): relocate from modules/gcp-unity-catalog + +git mv only; no functional changes. Old path has a deprecation README. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 22: Regenerate `terraform-docs` READMEs for all new modules + +**Files:** +- Modify: `modules/gcp/*/README.md` (every submodule, via `terraform-docs`) + +- [ ] **Step 1: Run `make docs` recursively** + +```bash +make -C modules/gcp docs +``` + +Expected: each module's README has its `` ... `` block populated with inputs/outputs tables. + +- [ ] **Step 2: Verify `pre-commit` passes** + +```bash +pre-commit run --all-files +``` + +Expected: all hooks pass (terraform_fmt, terraform_validate, terraform_docs). + +- [ ] **Step 3: Commit** + +```bash +git add modules/gcp/ +git commit -m "$(cat <<'EOF' +docs(gcp): regenerate terraform-docs for all new submodules + +Generated README content for network, private-connectivity, account, +dns, and databricks-workspace via `make -C modules/gcp docs`. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 23: Open PR 1 (draft) + +- [ ] **Step 1: Push branch** + +```bash +git push -u origin feature/gcp-modules-refactor +``` + +- [ ] **Step 2: Open draft PR** + +```bash +gh pr create --draft --title "feat(gcp): add modules/gcp/ composer + submodules (PR 1 of 6)" --body "$(cat <<'EOF' +## Summary + +First PR of the GCP modules refactor described in `docs/superpowers/specs/2026-05-14-gcp-modules-refactor-design.md`. Adds: + +- `modules/gcp/databricks-workspace` — top-level composer +- `modules/gcp/network` — VPC/subnet/router/NAT/peering/shared-VPC +- `modules/gcp/private-connectivity` — PSC + egress firewall +- `modules/gcp/account` — all `databricks_mws_*` resources +- `modules/gcp/dns` — private DNS zones (hub + spoke) +- Relocations: `modules/gcp-sa-provisioning` → `modules/gcp/service-account`, `modules/gcp-unity-catalog` → `modules/gcp/unity-catalog` + +No example consumes these yet — they will be migrated one PR at a time. + +## Test plan + +- [ ] `pre-commit run --all-files` passes +- [ ] Every fixture under `modules/gcp/*/tests//` validates +- [ ] Every negative fixture under `modules/gcp/databricks-workspace/tests/negative-*/` fails at plan time +EOF +)" +``` + +--- + +## PR 2 — Migrate `examples/gcp-basic` + +### Task 24: Rewrite `examples/gcp-basic` against the new composer + +**Files:** +- Modify: `examples/gcp-basic/main.tf` +- Modify: `examples/gcp-basic/variables.tf` +- Modify: `examples/gcp-basic/outputs.tf` +- Modify: `examples/gcp-basic/README.md` +- Modify: `examples/gcp-basic/terraform.tfvars` +- (Leave `init.tf` and `Makefile` unchanged.) + +- [ ] **Step 1: Rewrite `main.tf`** + +```hcl +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + workspace_name = var.workspace_name + + vpc_source = "databricks_managed" +} +``` + +- [ ] **Step 2: Trim `variables.tf` to only what this example needs** + +```hcl +variable "databricks_account_id" { + type = string + description = "Databricks Account ID" +} + +variable "databricks_google_service_account" { + type = string + description = "Service account email used for Databricks provider authentication" +} + +variable "google_project" { + type = string + description = "GCP project where the workspace will be created" +} + +variable "google_region" { + type = string + description = "GCP region for workspace deployment" +} + +variable "google_zone" { + type = string + description = "GCP zone (used by the google provider)" +} + +variable "prefix" { + type = string + description = "Prefix used to name generated resources" +} + +variable "workspace_name" { + type = string + description = "Workspace name" +} +``` + +(Drop `delegate_from` — that variable belongs to SA-provisioning, not basic.) + +- [ ] **Step 3: Rewrite `outputs.tf`** + +```hcl +output "workspace_id" { + value = module.workspace.workspace_id + description = "Databricks workspace ID" +} + +output "workspace_url" { + value = module.workspace.workspace_url + description = "Databricks workspace URL" +} +``` + +- [ ] **Step 4: Update `terraform.tfvars` skeleton** + +```hcl +databricks_account_id = "" +databricks_google_service_account = "" +google_project = "" +google_region = "" +google_zone = "" +prefix = "" +workspace_name = "" +``` + +- [ ] **Step 5: Rewrite `README.md`** + +```markdown +# examples/gcp-basic — Databricks-managed VPC + +Calls `modules/gcp/databricks-workspace` with `vpc_source = "databricks_managed"`. +The Databricks platform provisions the workspace VPC; you provide only the GCP +project, region, and prefix. + +## Prerequisites + +- A GCP project with the Databricks platform onboarded +- A service account with workspace-creator role (see `examples/gcp-sa-provisioning`) +- Databricks account ID + +## Apply + +```bash +terraform init +terraform apply +``` + +## Migrating from the old example + +This example previously called `modules/gcp-workspace-basic`. State from the +old apply does **not** migrate cleanly to the new composer because the +`databricks_mws_workspaces` resource address differs. Re-apply on clean state. + + + +``` + +- [ ] **Step 6: Regenerate docs and validate** + +```bash +cd examples/gcp-basic && make docs && terraform init -backend=false && terraform validate +``` + +Expected: validate passes. + +- [ ] **Step 7: Sandbox apply (manual)** + +The author runs `terraform apply` against a sandbox project and confirms the workspace is reachable. Capture plan output as a PR comment. Run `terraform destroy` after. + +- [ ] **Step 8: Commit + open PR** + +```bash +git add examples/gcp-basic/ +git commit -m "$(cat <<'EOF' +refactor(examples/gcp-basic): migrate to modules/gcp/databricks-workspace + +Replaces the call to modules/gcp-workspace-basic with the new composer +using vpc_source="databricks_managed". Variables trimmed to scenario +inputs; README documents the migration caveat. + +Co-authored-by: Isaac +EOF +)" + +gh pr create --draft --title "refactor(examples/gcp-basic): migrate to new composer (PR 2 of 6)" --body "$(cat <<'EOF' +## Summary + +Migrates `examples/gcp-basic` to call `modules/gcp/databricks-workspace`. Old +`modules/gcp-workspace-basic` remains untouched (deleted in PR 6). + +## Test plan + +- [ ] Sandbox `terraform apply` succeeds; workspace reachable +- [ ] Fresh `terraform plan` shows zero drift after apply +- [ ] `terraform destroy` cleans up without orphans +EOF +)" +``` + +--- + +## PR 3 — Migrate `examples/gcp-byovpc` + +### Task 25: Rewrite `examples/gcp-byovpc` against the new composer + +Same pattern as Task 24, with these differences: + +- [ ] **Step 1: `main.tf`** + +```hcl +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + workspace_name = var.workspace_name + + vpc_source = "create" + spoke_vpc_cidr = var.spoke_vpc_cidr + subnet_cidr = var.subnet_cidr + pod_cidr = var.pod_cidr + svc_cidr = var.svc_cidr +} +``` + +- [ ] **Step 2: `variables.tf`** + +```hcl +variable "databricks_account_id" { type = string } +variable "databricks_google_service_account" { type = string } +variable "google_project" { type = string } +variable "google_region" { type = string } +variable "google_zone" { type = string } +variable "prefix" { type = string } +variable "workspace_name" { type = string } + +variable "spoke_vpc_cidr" { type = string } +variable "subnet_cidr" { type = string } +variable "pod_cidr" { type = string default = null } +variable "svc_cidr" { type = string default = null } +``` + +- [ ] **Step 3–8:** Same as Task 24 (outputs, tfvars, README, docs, validate, sandbox apply, commit + PR). + +Note: variable names changed — `subnet_ip_cidr_range` → `subnet_cidr`, `pod_ip_cidr_range` → `pod_cidr`, `svc_ip_cidr_range` → `svc_cidr`, etc. README must explicitly call this out: + +> **Breaking change for migrating users:** variable names changed to match the new composer (`subnet_ip_cidr_range` → `subnet_cidr`, etc.). Update your tfvars accordingly. + +Commit message: + +``` +refactor(examples/gcp-byovpc): migrate to modules/gcp/databricks-workspace + +vpc_source="create" with spoke + subnet CIDRs. Variable names changed +to match the composer; README documents the migration. +``` + +--- + +## PR 4 — Migrate `examples/gcp-with-psc-exfiltration-protection` + +### Task 26: Rewrite the PSC example against the new composer + +**Files:** +- Modify: `examples/gcp-with-psc-exfiltration-protection/main.tf` +- Modify: `examples/gcp-with-psc-exfiltration-protection/unity-catalog.tf` +- Modify: `examples/gcp-with-psc-exfiltration-protection/variables.tf` +- Modify: `examples/gcp-with-psc-exfiltration-protection/outputs.tf` +- Modify: `examples/gcp-with-psc-exfiltration-protection/README.md` +- Modify: `examples/gcp-with-psc-exfiltration-protection/terraform.tfvars` + +- [ ] **Step 1: Rewrite `main.tf`** + +```hcl +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.workspace_google_project + google_region = var.google_region + + vpc_source = "create" + spoke_vpc_cidr = var.spoke_vpc_cidr + subnet_cidr = var.subnet_cidr + + private_link_frontend = true + private_link_backend = true + private_access_only = true + restricted_egress = true + + spoke_vpc_google_project = var.spoke_vpc_google_project + hub_vpc_google_project = var.hub_vpc_google_project + is_spoke_vpc_shared = var.is_spoke_vpc_shared + hub_vpc_cidr = var.hub_vpc_cidr + psc_subnet_cidr = var.psc_subnet_cidr + hive_metastore_ip = var.hive_metastore_ip + + tags = var.tags +} +``` + +- [ ] **Step 2: Rewrite `unity-catalog.tf` to consume composer outputs** + +```hcl +module "unity_catalog" { + source = "../../modules/gcp/unity-catalog" + + providers = { + databricks = databricks + databricks.workspace = databricks.workspace + } + + databricks_workspace_id = module.workspace.workspace_id + databricks_workspace_url = module.workspace.workspace_url + google_project = var.workspace_google_project + google_region = var.google_region + prefix = var.prefix + metastore_name = var.metastore_name + catalog_name = var.catalog_name +} +``` + +- [ ] **Step 3: Trim `variables.tf`** + +Drop variables that no longer apply (none — all current vars still map). Rename `subnet_cidr_var` references if any. + +Add new required vars: `spoke_vpc_cidr` (was `spoke_vpc_cidr` already), `subnet_cidr` (NEW — split from existing single CIDR var if needed). Refer to the current `terraform.tfvars` to confirm whether `subnet_cidr` was already exposed or needs to be added. + +Check current vars file: + +```bash +cat examples/gcp-with-psc-exfiltration-protection/variables.tf +``` + +If `subnet_cidr` isn't there, add: + +```hcl +variable "subnet_cidr" { + type = string + description = "CIDR for the spoke subnet" +} +``` + +- [ ] **Step 4: Update `terraform.tfvars`** to include `subnet_cidr` and remove any orphaned vars. + +- [ ] **Step 5: Update README** + +Document the migration. Note that `private_link_frontend`, `private_link_backend`, `private_access_only`, `restricted_egress` are now the explicit feature flags; the example sets all four to `true`. + +- [ ] **Step 6: Regenerate docs and validate** + +```bash +cd examples/gcp-with-psc-exfiltration-protection && make docs && terraform init -backend=false && terraform validate +``` + +- [ ] **Step 7: Sandbox apply (manual, with extra care)** + +- Snapshot state before apply +- Apply against sandbox +- Verify workspace reachable through PSC +- Verify UC catalog accessible +- Fresh plan — confirm zero drift +- `terraform destroy` — confirm PSC + DNS teardown is clean (no orphans) +- Capture all plan/apply/destroy output in the PR description + +- [ ] **Step 8: Commit + open PR** + +```bash +git add examples/gcp-with-psc-exfiltration-protection/ +git commit -m "$(cat <<'EOF' +refactor(examples/gcp-with-psc): migrate to new composer + +Single module call to modules/gcp/databricks-workspace with all four +connectivity flags enabled and restricted_egress=true. Unity Catalog +wired separately via modules/gcp/unity-catalog. + +Co-authored-by: Isaac +EOF +)" + +gh pr create --draft --title "refactor(examples/gcp-with-psc): migrate to new composer (PR 4 of 6)" --body "$(cat <<'EOF' +## Summary + +Migrates the PSC + exfiltration-protection example to the new composer. +The most complex of the migration PRs. + +## Test plan + +- [ ] Sandbox `terraform apply` succeeds end-to-end +- [ ] Workspace reachable through PSC (frontend) +- [ ] UC catalog accessible +- [ ] Fresh `terraform plan` shows zero drift +- [ ] `terraform destroy` cleans up PSC + DNS without orphans +EOF +)" +``` + +--- + +## PR 5 — New `examples/gcp-existing-vpc` + +### Task 27: Add the new "existing VPC" example + +**Files:** +- Create: `examples/gcp-existing-vpc/main.tf` +- Create: `examples/gcp-existing-vpc/init.tf` +- Create: `examples/gcp-existing-vpc/variables.tf` +- Create: `examples/gcp-existing-vpc/outputs.tf` +- Create: `examples/gcp-existing-vpc/terraform.tfvars` +- Create: `examples/gcp-existing-vpc/README.md` +- Create: `examples/gcp-existing-vpc/Makefile` + +- [ ] **Step 1: Copy `init.tf` and `Makefile` from `examples/gcp-basic`** (identical provider setup). + +- [ ] **Step 2: Write `main.tf`** + +```hcl +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + workspace_name = var.workspace_name + + vpc_source = "existing" + existing_vpc_name = var.existing_vpc_name + existing_subnet_name = var.existing_subnet_name +} +``` + +- [ ] **Step 3: Write `variables.tf`** + +```hcl +variable "databricks_account_id" { type = string } +variable "databricks_google_service_account" { type = string } +variable "google_project" { type = string } +variable "google_region" { type = string } +variable "google_zone" { type = string } +variable "prefix" { type = string } +variable "workspace_name" { type = string } +variable "existing_vpc_name" { type = string } +variable "existing_subnet_name" { type = string } +``` + +- [ ] **Step 4: Write `outputs.tf`, `terraform.tfvars`, `README.md`** (same templates as Task 24, scenario-appropriate). + +- [ ] **Step 5: Validate** + +```bash +cd examples/gcp-existing-vpc && make docs && terraform init -backend=false && terraform validate +``` + +- [ ] **Step 6: Sandbox apply (requires a pre-existing VPC and subnet in the sandbox project)** + +- [ ] **Step 7: Commit + PR** + +```bash +git add examples/gcp-existing-vpc/ +git commit -m "$(cat <<'EOF' +feat(examples/gcp-existing-vpc): new example using existing VPC + +New scenario unsupported by the legacy modules. Calls the composer +with vpc_source="existing" and looks up the pre-existing VPC and +subnet via the network submodule's data sources. + +Co-authored-by: Isaac +EOF +)" + +gh pr create --draft --title "feat(examples/gcp-existing-vpc): new example (PR 5 of 6)" --body "..." +``` + +--- + +## PR 6 — Cleanup + +### Task 28: Repoint `examples/gcp-sa-provisioning` at the relocated module + +**Files:** +- Modify: `examples/gcp-sa-provisioning/main.tf` + +- [ ] **Step 1: Update the `source` line** + +In `examples/gcp-sa-provisioning/main.tf` change: + +```hcl +source = "github.com/databricks/terraform-databricks-examples/modules/gcp-sa-provisioning" +``` + +to: + +```hcl +source = "github.com/databricks/terraform-databricks-examples/modules/gcp/service-account" +``` + +(Or the relative path `../../modules/gcp/service-account` if the example uses relative sources — match existing convention.) + +- [ ] **Step 2: Validate** + +```bash +cd examples/gcp-sa-provisioning && terraform init -backend=false && terraform validate +``` + +- [ ] **Step 3: Commit** + +```bash +git add examples/gcp-sa-provisioning/ +git commit -m "$(cat <<'EOF' +refactor(examples/gcp-sa-provisioning): repoint to modules/gcp/service-account + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 29: Delete deprecated modules + +**Files:** +- Delete: `modules/gcp-workspace-basic/` +- Delete: `modules/gcp-workspace-byovpc/` +- Delete: `modules/gcp-with-psc-exfiltration-protection/` +- Delete: `modules/gcp-sa-provisioning/` (deprecation stub from Task 20) +- Delete: `modules/gcp-unity-catalog/` (deprecation stub from Task 21) + +- [ ] **Step 1: Confirm no example still references the old paths** + +```bash +grep -rn "modules/gcp-workspace-basic\|modules/gcp-workspace-byovpc\|modules/gcp-with-psc-exfiltration-protection\|modules/gcp-sa-provisioning\|modules/gcp-unity-catalog" examples/ modules/ +``` + +Expected: no matches (every match should already point to `modules/gcp/...`). + +- [ ] **Step 2: Delete** + +```bash +git rm -r modules/gcp-workspace-basic modules/gcp-workspace-byovpc modules/gcp-with-psc-exfiltration-protection modules/gcp-sa-provisioning modules/gcp-unity-catalog +``` + +- [ ] **Step 3: Commit** + +```bash +git commit -m "$(cat <<'EOF' +refactor: remove deprecated GCP modules + +Removes modules/gcp-workspace-basic, modules/gcp-workspace-byovpc, +modules/gcp-with-psc-exfiltration-protection, and the deprecation +stubs for modules/gcp-sa-provisioning and modules/gcp-unity-catalog. +All examples now point at modules/gcp/*. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 30: Delete junk directories and stray state files + +**Files:** +- Delete: `examples/gcp-sa-provisionning/` (typo dir, only contains a Makefile) +- Delete: `examples/gcp-test-modules/` (only state files) +- Delete: stray `terraform.tfstate*` files under `examples/gcp-*/` (verify `.gitignore` first) + +- [ ] **Step 1: Check `.gitignore`** + +```bash +grep -n "tfstate" .gitignore +``` + +Expected: tfstate files should already be gitignored. If not, add patterns and stage that change. + +- [ ] **Step 2: Delete junk dirs** + +```bash +git rm -r examples/gcp-sa-provisionning examples/gcp-test-modules +``` + +- [ ] **Step 3: Untrack stray state files** + +```bash +git rm --cached examples/gcp-basic/terraform.tfstate* 2>/dev/null || true +git rm --cached examples/gcp-byovpc/terraform.tfstate* 2>/dev/null || true +git rm --cached examples/gcp-with-psc-exfiltration-protection/terraform.tfstate* 2>/dev/null || true +``` + +- [ ] **Step 4: Commit** + +```bash +git commit -m "$(cat <<'EOF' +chore: remove junk dirs and untrack stray terraform state + +Deletes examples/gcp-sa-provisionning (typo dir, Makefile only) and +examples/gcp-test-modules (state-only). Untracks accidentally-committed +terraform.tfstate files under examples/gcp-*. + +Co-authored-by: Isaac +EOF +)" +``` + +--- + +### Task 31: Update top-level README + +**Files:** +- Modify: `README.md` + +- [ ] **Step 1: Identify the GCP section in the top-level README** + +```bash +grep -n -A 5 "gcp" README.md | head -40 +``` + +- [ ] **Step 2: Rewrite the GCP examples table** + +Update the listing to: + +| Example | Description | +|---------|-------------| +| `examples/gcp-basic` | Databricks-managed VPC | +| `examples/gcp-byovpc` | Customer VPC (Terraform creates it) | +| `examples/gcp-existing-vpc` | Use an existing customer VPC | +| `examples/gcp-with-psc-exfiltration-protection` | Full PSC + private DNS + restricted egress | +| `examples/gcp-sa-provisioning` | Bootstrap the workspace-creator service account | + +Update the modules listing to reflect the new structure under `modules/gcp/`. + +- [ ] **Step 3: Commit and open final PR** + +```bash +git add README.md +git commit -m "$(cat <<'EOF' +docs: update README for new GCP module layout + +Updates the GCP examples and modules tables to reflect the +modules/gcp/ composer + submodules and the new gcp-existing-vpc +example. + +Co-authored-by: Isaac +EOF +)" + +gh pr create --draft --title "chore: delete legacy GCP modules and dirs (PR 6 of 6)" --body "..." +``` + +--- + +## Self-Review + +(Performed after writing the plan; issues found and fixed inline.) + +**1. Spec coverage:** +- Problem statement → Tasks 24–31 migrate every existing GCP example ✓ +- Goals 1–7 → All addressed; thin examples in Tasks 24–27 ✓ +- Module layout (5 submodules + service-account + unity-catalog) → Tasks 2–22 ✓ +- Composer API → Task 16 (variables), Task 17 (wiring), Task 19 (preconditions) ✓ +- Cross-variable validation table → Task 19 (negative fixtures verify each rule) ✓ +- Submodule contracts → Tasks 2–15 implement each contract ✓ +- Example shapes (4 scenarios) → Tasks 18 (composer fixtures) + 24–27 (real examples) ✓ +- Migration plan (6 PRs) → Tasks grouped under "PR 1" through "PR 6" headers ✓ +- Testing approach → Tasks 18 (positive), 19 (negative), per-task validate steps ✓ +- Risks → Hive metastore IP fallback acknowledged via the empty `default_hive_metastore_ips` map; teardown ordering is sandbox-tested in PR 4 ✓ + +**2. Placeholders:** Scanned for "TBD", "TODO", "implement later", vague "add error handling". Found none. The Hive metastore IP map is intentionally empty initially (variable falls back to "" and gates the hive firewall rule); this is a documented behavior, not a placeholder. + +**3. Type consistency:** +- `frontend_psc_fr_id` / `backend_psc_fr_id` / `hub_frontend_psc_fr_id` used consistently across `private-connectivity` outputs (Task 7), `account` inputs (Task 9), and composer wiring (Task 17) ✓ +- `spoke_vpc_self_link`, `hub_vpc_self_link` consistent between `network` outputs (Tasks 3, 5), `private-connectivity` inputs (Task 6), and `dns` inputs (Task 14) ✓ +- `workspace_url` flows from `account` (Task 10) → `dns` (Task 14, 15) → composer outputs (Task 17) ✓ +- `nat_dependency` is `type = any` in `account` (Task 9), wired to `module.network[0].nat_id` (Task 17) — matches ✓ + +**4. Spec requirements with no task:** None found. + +--- + +## Execution Handoff + +Plan complete and saved to `docs/superpowers/plans/2026-05-14-gcp-modules-refactor.md`. + +Two execution options: + +**1. Subagent-Driven (recommended)** — fresh subagent per task, review between tasks, fast iteration. + +**2. Inline Execution** — execute tasks in this session using executing-plans, batch execution with checkpoints. + +Which approach? From a4ce05a2734a2c73b9309764e34af3d23ae9a864 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 14:20:08 +0200 Subject: [PATCH 03/14] build: add Makefile recursion for modules/gcp/ submodules Adds modules/gcp/Makefile mirroring the modules/ pattern (discover sub-projects via */README.md) and updates modules/Makefile to recurse into the gcp/ subdir for terraform-docs generation. Co-authored-by: Isaac --- modules/Makefile | 7 +++++-- modules/gcp/Makefile | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 modules/gcp/Makefile diff --git a/modules/Makefile b/modules/Makefile index 98c80a8..a23fed0 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -1,8 +1,11 @@ PROJECTS := $(dir $(wildcard */README.md)) -docs: $(PROJECTS) +docs: $(PROJECTS) gcp-recursive $(PROJECTS): $(MAKE) -C $@ docs -.PHONY: $(PROJECTS) +gcp-recursive: + $(MAKE) -C gcp docs + +.PHONY: $(PROJECTS) docs gcp-recursive diff --git a/modules/gcp/Makefile b/modules/gcp/Makefile new file mode 100644 index 0000000..30b525d --- /dev/null +++ b/modules/gcp/Makefile @@ -0,0 +1,8 @@ +PROJECTS := $(dir $(wildcard */README.md)) + +docs: $(PROJECTS) + +$(PROJECTS): + $(MAKE) -C $@ docs + +.PHONY: $(PROJECTS) docs From bc0312c10807aaa1d33b0c687603b3c0bd8e8016 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 14:23:38 +0200 Subject: [PATCH 04/14] feat(gcp/network): VPC create/existing/hub + fixtures Adds modules/gcp/network module supporting three VPC provenance modes: - vpc_source="create": Terraform creates spoke VPC + subnet + Cloud Router + NAT - vpc_source="existing": data-source lookup for pre-existing VPC + subnet - create_hub=true: adds hub VPC + subnet + bidirectional peering + optional shared-VPC host/service binding Outputs cover all spoke and hub identifiers. Test fixtures in tests/ cover create, existing, and create-with-hub scenarios. Co-authored-by: Isaac --- modules/gcp/network/Makefile | 7 + modules/gcp/network/README.md | 6 + modules/gcp/network/main.tf | 131 ++++++++++++++++++ modules/gcp/network/outputs.tf | 66 +++++++++ .../gcp/network/tests/create-with-hub/main.tf | 26 ++++ modules/gcp/network/tests/create/main.tf | 20 +++ modules/gcp/network/tests/existing/main.tf | 20 +++ modules/gcp/network/variables.tf | 104 ++++++++++++++ modules/gcp/network/versions.tf | 9 ++ 9 files changed, 389 insertions(+) create mode 100644 modules/gcp/network/Makefile create mode 100644 modules/gcp/network/README.md create mode 100644 modules/gcp/network/main.tf create mode 100644 modules/gcp/network/outputs.tf create mode 100644 modules/gcp/network/tests/create-with-hub/main.tf create mode 100644 modules/gcp/network/tests/create/main.tf create mode 100644 modules/gcp/network/tests/existing/main.tf create mode 100644 modules/gcp/network/variables.tf create mode 100644 modules/gcp/network/versions.tf diff --git a/modules/gcp/network/Makefile b/modules/gcp/network/Makefile new file mode 100644 index 0000000..17b32ec --- /dev/null +++ b/modules/gcp/network/Makefile @@ -0,0 +1,7 @@ +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . diff --git a/modules/gcp/network/README.md b/modules/gcp/network/README.md new file mode 100644 index 0000000..c27fc06 --- /dev/null +++ b/modules/gcp/network/README.md @@ -0,0 +1,6 @@ +# modules/gcp/network + +VPC, subnet, router, NAT, peering, and Shared-VPC binding for the Databricks GCP composer. + + + diff --git a/modules/gcp/network/main.tf b/modules/gcp/network/main.tf new file mode 100644 index 0000000..4828356 --- /dev/null +++ b/modules/gcp/network/main.tf @@ -0,0 +1,131 @@ +locals { + create_vpc = var.vpc_source == "create" + use_existing_vpc = var.vpc_source == "existing" + + subnet_name = coalesce(var.subnet_name, "${var.prefix}-subnet-${var.suffix}") +} + +# === Spoke VPC (created) ================================================ +resource "google_compute_network" "spoke_vpc" { + count = local.create_vpc ? 1 : 0 + + name = "${var.prefix}-spoke-vpc-${var.suffix}" + project = var.spoke_vpc_google_project + auto_create_subnetworks = false + routing_mode = "GLOBAL" +} + +resource "google_compute_subnetwork" "spoke_subnet" { + count = local.create_vpc ? 1 : 0 + + name = local.subnet_name + project = var.spoke_vpc_google_project + network = google_compute_network.spoke_vpc[0].id + region = var.google_region + ip_cidr_range = var.subnet_cidr + private_ip_google_access = true + + dynamic "secondary_ip_range" { + for_each = var.pod_cidr != null ? [1] : [] + content { + range_name = "pods" + ip_cidr_range = var.pod_cidr + } + } + + dynamic "secondary_ip_range" { + for_each = var.svc_cidr != null ? [1] : [] + content { + range_name = "services" + ip_cidr_range = var.svc_cidr + } + } +} + +resource "google_compute_router" "router" { + count = local.create_vpc ? 1 : 0 + + name = "${var.prefix}-router-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + network = google_compute_network.spoke_vpc[0].id +} + +resource "google_compute_router_nat" "nat" { + count = local.create_vpc ? 1 : 0 + + name = "${var.prefix}-nat-${var.suffix}" + project = var.spoke_vpc_google_project + router = google_compute_router.router[0].name + region = var.google_region + nat_ip_allocate_option = "AUTO_ONLY" + source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" +} + +# === Spoke VPC (data lookup) ============================================ +data "google_compute_network" "existing_spoke" { + count = local.use_existing_vpc ? 1 : 0 + + name = var.existing_vpc_name + project = var.spoke_vpc_google_project +} + +data "google_compute_subnetwork" "existing_spoke_subnet" { + count = local.use_existing_vpc ? 1 : 0 + + name = var.existing_subnet_name + project = var.spoke_vpc_google_project + region = var.google_region +} + +# === Hub VPC ============================================================ +resource "google_compute_network" "hub_vpc" { + count = var.create_hub ? 1 : 0 + + name = "${var.prefix}-hub-vpc-${var.suffix}" + project = var.hub_vpc_google_project + auto_create_subnetworks = false + routing_mode = "GLOBAL" +} + +resource "google_compute_subnetwork" "hub_subnet" { + count = var.create_hub ? 1 : 0 + + name = "${var.prefix}-hub-subnet-${var.suffix}" + project = var.hub_vpc_google_project + network = google_compute_network.hub_vpc[0].id + region = var.google_region + ip_cidr_range = var.hub_vpc_cidr + private_ip_google_access = true +} + +# === Peering ============================================================ +resource "google_compute_network_peering" "hub_to_spoke" { + count = var.create_hub ? 1 : 0 + + name = "${var.prefix}-hub-spoke-${var.suffix}" + network = google_compute_network.hub_vpc[0].self_link + peer_network = local.create_vpc ? google_compute_network.spoke_vpc[0].self_link : data.google_compute_network.existing_spoke[0].self_link +} + +resource "google_compute_network_peering" "spoke_to_hub" { + count = var.create_hub ? 1 : 0 + + name = "${var.prefix}-spoke-hub-${var.suffix}" + network = local.create_vpc ? google_compute_network.spoke_vpc[0].self_link : data.google_compute_network.existing_spoke[0].self_link + peer_network = google_compute_network.hub_vpc[0].self_link +} + +# === Shared VPC ========================================================= +resource "google_compute_shared_vpc_host_project" "host" { + count = var.create_hub && var.is_spoke_vpc_shared && var.workspace_google_project != var.spoke_vpc_google_project ? 1 : 0 + + project = var.spoke_vpc_google_project +} + +resource "google_compute_shared_vpc_service_project" "service" { + count = var.create_hub && var.is_spoke_vpc_shared && var.workspace_google_project != var.spoke_vpc_google_project ? 1 : 0 + + host_project = google_compute_shared_vpc_host_project.host[0].project + service_project = var.workspace_google_project +} diff --git a/modules/gcp/network/outputs.tf b/modules/gcp/network/outputs.tf new file mode 100644 index 0000000..958fe27 --- /dev/null +++ b/modules/gcp/network/outputs.tf @@ -0,0 +1,66 @@ +output "spoke_vpc_id" { + value = local.create_vpc ? google_compute_network.spoke_vpc[0].id : ( + local.use_existing_vpc ? data.google_compute_network.existing_spoke[0].id : null + ) + description = "ID of the spoke VPC" +} + +output "spoke_vpc_name" { + value = local.create_vpc ? google_compute_network.spoke_vpc[0].name : ( + local.use_existing_vpc ? data.google_compute_network.existing_spoke[0].name : null + ) + description = "Name of the spoke VPC" +} + +output "spoke_vpc_self_link" { + value = local.create_vpc ? google_compute_network.spoke_vpc[0].self_link : ( + local.use_existing_vpc ? data.google_compute_network.existing_spoke[0].self_link : null + ) + description = "Self-link of the spoke VPC" +} + +output "spoke_subnet_id" { + value = local.create_vpc ? google_compute_subnetwork.spoke_subnet[0].id : ( + local.use_existing_vpc ? data.google_compute_subnetwork.existing_spoke_subnet[0].id : null + ) + description = "ID of the spoke subnet" +} + +output "spoke_subnet_name" { + value = local.create_vpc ? google_compute_subnetwork.spoke_subnet[0].name : ( + local.use_existing_vpc ? data.google_compute_subnetwork.existing_spoke_subnet[0].name : null + ) + description = "Name of the spoke subnet" +} + +output "spoke_subnet_self_link" { + value = local.create_vpc ? google_compute_subnetwork.spoke_subnet[0].self_link : ( + local.use_existing_vpc ? data.google_compute_subnetwork.existing_spoke_subnet[0].self_link : null + ) + description = "Self-link of the spoke subnet" +} + +output "nat_id" { + value = local.create_vpc ? google_compute_router_nat.nat[0].id : null + description = "ID of the Cloud NAT (null when vpc_source=existing)" +} + +output "hub_vpc_id" { + value = var.create_hub ? google_compute_network.hub_vpc[0].id : null + description = "ID of the hub VPC (null when create_hub=false)" +} + +output "hub_vpc_name" { + value = var.create_hub ? google_compute_network.hub_vpc[0].name : null + description = "Name of the hub VPC (null when create_hub=false)" +} + +output "hub_vpc_self_link" { + value = var.create_hub ? google_compute_network.hub_vpc[0].self_link : null + description = "Self-link of the hub VPC (null when create_hub=false)" +} + +output "hub_subnet_name" { + value = var.create_hub ? google_compute_subnetwork.hub_subnet[0].name : null + description = "Name of the hub subnet (null when create_hub=false)" +} diff --git a/modules/gcp/network/tests/create-with-hub/main.tf b/modules/gcp/network/tests/create-with-hub/main.tf new file mode 100644 index 0000000..2d27e0b --- /dev/null +++ b/modules/gcp/network/tests/create-with-hub/main.tf @@ -0,0 +1,26 @@ +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-project" + region = "us-central1" +} + +module "network" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + vpc_source = "create" + spoke_vpc_google_project = "fixture-spoke-project" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" + + create_hub = true + hub_vpc_google_project = "fixture-hub-project" + hub_vpc_cidr = "10.1.0.0/24" + is_spoke_vpc_shared = true + workspace_google_project = "fixture-workspace-project" +} diff --git a/modules/gcp/network/tests/create/main.tf b/modules/gcp/network/tests/create/main.tf new file mode 100644 index 0000000..29d729b --- /dev/null +++ b/modules/gcp/network/tests/create/main.tf @@ -0,0 +1,20 @@ +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-project" + region = "us-central1" +} + +module "network" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + vpc_source = "create" + spoke_vpc_google_project = "fixture-project" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" +} diff --git a/modules/gcp/network/tests/existing/main.tf b/modules/gcp/network/tests/existing/main.tf new file mode 100644 index 0000000..8935be2 --- /dev/null +++ b/modules/gcp/network/tests/existing/main.tf @@ -0,0 +1,20 @@ +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-project" + region = "us-central1" +} + +module "network" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + vpc_source = "existing" + spoke_vpc_google_project = "fixture-project" + existing_vpc_name = "preexisting-vpc" + existing_subnet_name = "preexisting-subnet" +} diff --git a/modules/gcp/network/variables.tf b/modules/gcp/network/variables.tf new file mode 100644 index 0000000..17e96d6 --- /dev/null +++ b/modules/gcp/network/variables.tf @@ -0,0 +1,104 @@ +variable "prefix" { + type = string + description = "Prefix for generated resource names" +} + +variable "suffix" { + type = string + description = "Random suffix passed by the composer for uniqueness" +} + +variable "google_region" { + type = string + description = "GCP region for all network resources" +} + +variable "vpc_source" { + type = string + description = "Either 'create' (Terraform creates a VPC) or 'existing' (data-source lookup)" + validation { + condition = contains(["create", "existing"], var.vpc_source) + error_message = "vpc_source must be 'create' or 'existing'." + } +} + +# Spoke project always required +variable "spoke_vpc_google_project" { + type = string + description = "GCP project hosting the spoke VPC" +} + +# === Used when vpc_source = "create" ==================================== +variable "spoke_vpc_cidr" { + type = string + default = null + description = "CIDR for the spoke subnet primary range (required when vpc_source=create)" +} + +variable "subnet_cidr" { + type = string + default = null + description = "CIDR for the spoke subnet (required when vpc_source=create)" +} + +variable "subnet_name" { + type = string + default = null + description = "Override for spoke subnet name (default: \"{prefix}-subnet-{suffix}\")" +} + +variable "pod_cidr" { + type = string + default = null + description = "GKE secondary range for pods (optional)" +} + +variable "svc_cidr" { + type = string + default = null + description = "GKE secondary range for services (optional)" +} + +# === Used when vpc_source = "existing" ================================== +variable "existing_vpc_name" { + type = string + default = null + description = "Name of pre-existing VPC (required when vpc_source=existing)" +} + +variable "existing_subnet_name" { + type = string + default = null + description = "Name of pre-existing subnet (required when vpc_source=existing)" +} + +# === Hub configuration (only when create_hub = true) ==================== +variable "create_hub" { + type = bool + default = false + description = "Create a hub VPC + subnet + peering with the spoke. Composer passes restricted_egress here." +} + +variable "hub_vpc_google_project" { + type = string + default = null + description = "GCP project hosting the hub VPC (required when create_hub=true)" +} + +variable "hub_vpc_cidr" { + type = string + default = null + description = "CIDR for the hub subnet (required when create_hub=true)" +} + +variable "is_spoke_vpc_shared" { + type = bool + default = false + description = "If true, bind the spoke VPC's project as a Shared-VPC host and the workspace project as a service project" +} + +variable "workspace_google_project" { + type = string + default = null + description = "Workspace project (used for Shared-VPC service binding)" +} diff --git a/modules/gcp/network/versions.tf b/modules/gcp/network/versions.tf new file mode 100644 index 0000000..de067e7 --- /dev/null +++ b/modules/gcp/network/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0" + } + } +} From 4cd9b5b0ffbe33b973ce8ac285821485dc6880b8 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 14:39:48 +0200 Subject: [PATCH 05/14] feat(gcp/private-connectivity): PSC + firewall + fixtures Adds modules/gcp/private-connectivity with: - Dedicated PSC subnet in spoke VPC - Backend (SCC) PSC endpoint gated on enable_backend - Frontend PSC endpoint (spoke) gated on enable_frontend - Hub-side frontend PSC endpoint when hub present + frontend enabled - Regional PSC service-attachment maps for 14 GCP regions - Egress firewall stack gated on restrict_egress: deny-egress (priority 1100), allow Google APIs, allow Databricks control plane (to PSC IPs), optional allow managed Hive, hub ingress from spoke CIDR Test fixtures: full-isolated and no-egress. Co-authored-by: Isaac --- modules/gcp/private-connectivity/Makefile | 7 ++ modules/gcp/private-connectivity/README.md | 6 ++ modules/gcp/private-connectivity/firewall.tf | 97 +++++++++++++++++++ modules/gcp/private-connectivity/locals.tf | 45 +++++++++ modules/gcp/private-connectivity/outputs.tf | 34 +++++++ modules/gcp/private-connectivity/psc.tf | 78 +++++++++++++++ .../tests/full-isolated/main.tf | 32 ++++++ .../tests/no-egress/main.tf | 26 +++++ modules/gcp/private-connectivity/variables.tf | 57 +++++++++++ modules/gcp/private-connectivity/versions.tf | 9 ++ 10 files changed, 391 insertions(+) create mode 100644 modules/gcp/private-connectivity/Makefile create mode 100644 modules/gcp/private-connectivity/README.md create mode 100644 modules/gcp/private-connectivity/firewall.tf create mode 100644 modules/gcp/private-connectivity/locals.tf create mode 100644 modules/gcp/private-connectivity/outputs.tf create mode 100644 modules/gcp/private-connectivity/psc.tf create mode 100644 modules/gcp/private-connectivity/tests/full-isolated/main.tf create mode 100644 modules/gcp/private-connectivity/tests/no-egress/main.tf create mode 100644 modules/gcp/private-connectivity/variables.tf create mode 100644 modules/gcp/private-connectivity/versions.tf diff --git a/modules/gcp/private-connectivity/Makefile b/modules/gcp/private-connectivity/Makefile new file mode 100644 index 0000000..17b32ec --- /dev/null +++ b/modules/gcp/private-connectivity/Makefile @@ -0,0 +1,7 @@ +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . diff --git a/modules/gcp/private-connectivity/README.md b/modules/gcp/private-connectivity/README.md new file mode 100644 index 0000000..0c37c8f --- /dev/null +++ b/modules/gcp/private-connectivity/README.md @@ -0,0 +1,6 @@ +# modules/gcp/private-connectivity + +GCP-side PSC endpoints + restricted-egress firewall for the Databricks GCP composer. + + + diff --git a/modules/gcp/private-connectivity/firewall.tf b/modules/gcp/private-connectivity/firewall.tf new file mode 100644 index 0000000..97342c6 --- /dev/null +++ b/modules/gcp/private-connectivity/firewall.tf @@ -0,0 +1,97 @@ +# Egress firewall stack — only emitted when restrict_egress = true. + +# === Spoke deny-egress ================================================== +resource "google_compute_firewall" "spoke_default_deny_egress" { + count = var.restrict_egress ? 1 : 0 + + name = "${var.prefix}-spoke-${var.suffix}-default-deny-egress" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_self_link + + direction = "EGRESS" + priority = 1100 + destination_ranges = ["0.0.0.0/0"] + source_ranges = [] + + deny { + protocol = "all" + } +} + +# === Spoke allow Google APIs ============================================ +resource "google_compute_firewall" "spoke_allow_google_apis" { + count = var.restrict_egress ? 1 : 0 + + name = "${var.prefix}-spoke-${var.suffix}-to-google-apis" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_self_link + + direction = "EGRESS" + priority = 1000 + destination_ranges = [ + "199.36.153.4/30", + "199.36.153.8/30", + "34.126.0.0/18" + ] + + allow { + protocol = "all" + } +} + +# === Spoke allow Databricks control plane (to PSC IPs) ================== +resource "google_compute_firewall" "spoke_allow_ctl_plane" { + count = var.restrict_egress && var.enable_frontend && var.enable_backend ? 1 : 0 + + name = "${var.prefix}-spoke-${var.suffix}-to-databricks-control-plane" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_self_link + + direction = "EGRESS" + priority = 1000 + destination_ranges = [ + "${google_compute_forwarding_rule.backend_fr[0].ip_address}/32", + "${google_compute_forwarding_rule.frontend_fr_spoke[0].ip_address}/32" + ] + + allow { + protocol = "tcp" + ports = ["443"] + } +} + +# === Spoke allow managed Hive (conditional on hive_metastore_ip) ======== +resource "google_compute_firewall" "spoke_allow_hive" { + count = var.restrict_egress && local.hive_metastore_ip != "" ? 1 : 0 + + name = "${var.prefix}-spoke-${var.suffix}-to-${var.google_region}-managed-hive" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_self_link + + direction = "EGRESS" + priority = 1000 + destination_ranges = ["${local.hive_metastore_ip}/32"] + + allow { + protocol = "tcp" + ports = ["3306"] + } +} + +# === Hub ingress from spoke ============================================= +resource "google_compute_firewall" "hub_ingress" { + count = var.restrict_egress && local.hub_present ? 1 : 0 + + name = "${var.prefix}-hub-${var.suffix}-ingress" + project = var.hub_vpc_google_project + network = var.hub_vpc_self_link + + direction = "INGRESS" + priority = 1000 + destination_ranges = [] + source_ranges = [var.spoke_vpc_cidr] + + allow { + protocol = "all" + } +} diff --git a/modules/gcp/private-connectivity/locals.tf b/modules/gcp/private-connectivity/locals.tf new file mode 100644 index 0000000..e2c8cea --- /dev/null +++ b/modules/gcp/private-connectivity/locals.tf @@ -0,0 +1,45 @@ +locals { + google_frontend_psc_targets = { + "asia-northeast1" = "projects/general-prod-asianortheast1-01/regions/asia-northeast1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "asia-south1" = "projects/gen-prod-asias1-01/regions/asia-south1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "asia-southeast1" = "projects/general-prod-asiasoutheast1-01/regions/asia-southeast1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "australia-southeast1" = "projects/general-prod-ausoutheast1-01/regions/australia-southeast1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "europe-west1" = "projects/general-prod-europewest1-01/regions/europe-west1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "europe-west2" = "projects/general-prod-europewest2-01/regions/europe-west2/serviceAttachments/plproxy-psc-endpoint-all-ports" + "europe-west3" = "projects/general-prod-europewest3-01/regions/europe-west3/serviceAttachments/plproxy-psc-endpoint-all-ports" + "northamerica-northeast1" = "projects/general-prod-nanortheast1-01/regions/northamerica-northeast1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "southamerica-east1" = "projects/gen-prod-saeast1-01/regions/southamerica-east1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-central1" = "projects/gcp-prod-general/regions/us-central1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-east1" = "projects/general-prod-useast1-01/regions/us-east1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-east4" = "projects/general-prod-useast4-01/regions/us-east4/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-west1" = "projects/general-prod-uswest1-01/regions/us-west1/serviceAttachments/plproxy-psc-endpoint-all-ports" + "us-west4" = "projects/general-prod-uswest4-01/regions/us-west4/serviceAttachments/plproxy-psc-endpoint-all-ports" + } + + google_backend_psc_targets = { + "asia-northeast1" = "projects/prod-gcp-asia-northeast1/regions/asia-northeast1/serviceAttachments/ngrok-psc-endpoint" + "asia-south1" = "projects/prod-gcp-asia-south1/regions/asia-south1/serviceAttachments/ngrok-psc-endpoint" + "asia-southeast1" = "projects/prod-gcp-asia-southeast1/regions/asia-southeast1/serviceAttachments/ngrok-psc-endpoint" + "australia-southeast1" = "projects/prod-gcp-australia-southeast1/regions/australia-southeast1/serviceAttachments/ngrok-psc-endpoint" + "europe-west1" = "projects/prod-gcp-europe-west1/regions/europe-west1/serviceAttachments/ngrok-psc-endpoint" + "europe-west2" = "projects/prod-gcp-europe-west2/regions/europe-west2/serviceAttachments/ngrok-psc-endpoint" + "europe-west3" = "projects/prod-gcp-europe-west3/regions/europe-west3/serviceAttachments/ngrok-psc-endpoint" + "northamerica-northeast1" = "projects/prod-gcp-na-northeast1/regions/northamerica-northeast1/serviceAttachments/ngrok-psc-endpoint" + "southamerica-east1" = "projects/gen-prod-saeast1-01/regions/southamerica-east1/serviceAttachments/ngrok-psc-endpoint" + "us-central1" = "projects/prod-gcp-us-central1/regions/us-central1/serviceAttachments/ngrok-psc-endpoint" + "us-east1" = "projects/prod-gcp-us-east1/regions/us-east1/serviceAttachments/ngrok-psc-endpoint" + "us-east4" = "projects/prod-gcp-us-east4/regions/us-east4/serviceAttachments/ngrok-psc-endpoint" + "us-west1" = "projects/prod-gcp-us-west1/regions/us-west1/serviceAttachments/ngrok-psc-endpoint" + "us-west4" = "projects/prod-gcp-us-west4/regions/us-west4/serviceAttachments/ngrok-psc-endpoint" + } + + # Regional default Hive Metastore IPs per Databricks docs: + # https://docs.gcp.databricks.com/en/resources/ip-domain-region.html#addresses-for-default-metastore + # NOTE: kept empty initially; override via var.hive_metastore_ip. + default_hive_metastore_ips = { + } + + hive_metastore_ip = var.hive_metastore_ip != null ? var.hive_metastore_ip : try(local.default_hive_metastore_ips[var.google_region], "") + + hub_present = var.hub_vpc_id != null +} diff --git a/modules/gcp/private-connectivity/outputs.tf b/modules/gcp/private-connectivity/outputs.tf new file mode 100644 index 0000000..916bcf9 --- /dev/null +++ b/modules/gcp/private-connectivity/outputs.tf @@ -0,0 +1,34 @@ +output "psc_subnet_self_link" { + value = google_compute_subnetwork.psc_subnet.self_link + description = "Self-link of the PSC subnet" +} + +output "frontend_psc_fr_id" { + value = var.enable_frontend ? google_compute_forwarding_rule.frontend_fr_spoke[0].name : null + description = "Name of the frontend PSC forwarding rule (null when enable_frontend=false)" +} + +output "backend_psc_fr_id" { + value = var.enable_backend ? google_compute_forwarding_rule.backend_fr[0].name : null + description = "Name of the backend (SCC) PSC forwarding rule (null when enable_backend=false)" +} + +output "hub_frontend_psc_fr_id" { + value = local.hub_present && var.enable_frontend ? google_compute_forwarding_rule.frontend_fr_hub[0].name : null + description = "Name of the hub-side frontend PSC forwarding rule (null when no hub or no frontend)" +} + +output "frontend_psc_ip_spoke" { + value = var.enable_frontend ? google_compute_address.frontend_address_spoke[0].address : null + description = "IP address of the spoke-side frontend PSC endpoint" +} + +output "backend_psc_ip_spoke" { + value = var.enable_backend ? google_compute_address.backend_address[0].address : null + description = "IP address of the spoke-side backend PSC endpoint" +} + +output "frontend_psc_ip_hub" { + value = local.hub_present && var.enable_frontend ? google_compute_address.frontend_address_hub[0].address : null + description = "IP address of the hub-side frontend PSC endpoint (null when no hub)" +} diff --git a/modules/gcp/private-connectivity/psc.tf b/modules/gcp/private-connectivity/psc.tf new file mode 100644 index 0000000..485abb2 --- /dev/null +++ b/modules/gcp/private-connectivity/psc.tf @@ -0,0 +1,78 @@ +# === PSC Subnet (spoke) ================================================= +resource "google_compute_subnetwork" "psc_subnet" { + name = "${var.prefix}-psc-subnet-${var.suffix}" + project = var.spoke_vpc_google_project + network = var.spoke_vpc_id + region = var.google_region + ip_cidr_range = var.psc_subnet_cidr + private_ip_google_access = true +} + +# === Backend (SCC) PSC endpoint — spoke ================================= +resource "google_compute_address" "backend_address" { + count = var.enable_backend ? 1 : 0 + + name = "${var.prefix}-psc-scc-ip-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + subnetwork = google_compute_subnetwork.psc_subnet.name + address_type = "INTERNAL" +} + +resource "google_compute_forwarding_rule" "backend_fr" { + count = var.enable_backend ? 1 : 0 + + name = "${var.prefix}-psc-scc-ep-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + network = var.spoke_vpc_id + ip_address = google_compute_address.backend_address[0].id + target = local.google_backend_psc_targets[var.google_region] + load_balancing_scheme = "" +} + +# === Frontend PSC endpoint — spoke ====================================== +resource "google_compute_address" "frontend_address_spoke" { + count = var.enable_frontend ? 1 : 0 + + name = "${var.prefix}-psc-ws-ip-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + subnetwork = google_compute_subnetwork.psc_subnet.name + address_type = "INTERNAL" +} + +resource "google_compute_forwarding_rule" "frontend_fr_spoke" { + count = var.enable_frontend ? 1 : 0 + + name = "${var.prefix}-psc-ws-ep-${var.suffix}" + project = var.spoke_vpc_google_project + region = var.google_region + network = var.spoke_vpc_id + ip_address = google_compute_address.frontend_address_spoke[0].id + target = local.google_frontend_psc_targets[var.google_region] + load_balancing_scheme = "" +} + +# === Frontend PSC endpoint — hub (transit) ============================== +resource "google_compute_address" "frontend_address_hub" { + count = local.hub_present && var.enable_frontend ? 1 : 0 + + name = "${var.prefix}-hub-psc-ws-ip-${var.suffix}" + project = var.hub_vpc_google_project + region = var.google_region + subnetwork = var.hub_subnet_name + address_type = "INTERNAL" +} + +resource "google_compute_forwarding_rule" "frontend_fr_hub" { + count = local.hub_present && var.enable_frontend ? 1 : 0 + + name = "${var.prefix}-hub-psc-ws-ep-${var.suffix}" + project = var.hub_vpc_google_project + region = var.google_region + network = var.hub_vpc_id + ip_address = google_compute_address.frontend_address_hub[0].id + target = local.google_frontend_psc_targets[var.google_region] + load_balancing_scheme = "" +} diff --git a/modules/gcp/private-connectivity/tests/full-isolated/main.tf b/modules/gcp/private-connectivity/tests/full-isolated/main.tf new file mode 100644 index 0000000..f7a11a3 --- /dev/null +++ b/modules/gcp/private-connectivity/tests/full-isolated/main.tf @@ -0,0 +1,32 @@ +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-spoke" + region = "us-central1" +} + +module "pc" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + + spoke_vpc_id = "projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_google_project = "fixture-spoke" + spoke_vpc_cidr = "10.0.0.0/16" + + hub_vpc_id = "projects/fixture-hub/global/networks/hub-vpc" + hub_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-hub/global/networks/hub-vpc" + hub_vpc_google_project = "fixture-hub" + hub_subnet_name = "fixture-hub-subnet-abc123" + hub_vpc_cidr = "10.1.0.0/24" + + enable_frontend = true + enable_backend = true + restrict_egress = true + psc_subnet_cidr = "10.0.255.0/28" +} diff --git a/modules/gcp/private-connectivity/tests/no-egress/main.tf b/modules/gcp/private-connectivity/tests/no-egress/main.tf new file mode 100644 index 0000000..6c318d4 --- /dev/null +++ b/modules/gcp/private-connectivity/tests/no-egress/main.tf @@ -0,0 +1,26 @@ +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-spoke" + region = "us-central1" +} + +module "pc" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + google_region = "us-central1" + + spoke_vpc_id = "projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_google_project = "fixture-spoke" + spoke_vpc_cidr = "10.0.0.0/16" + + enable_frontend = true + enable_backend = false + restrict_egress = false + psc_subnet_cidr = "10.0.255.0/28" +} diff --git a/modules/gcp/private-connectivity/variables.tf b/modules/gcp/private-connectivity/variables.tf new file mode 100644 index 0000000..a539e9d --- /dev/null +++ b/modules/gcp/private-connectivity/variables.tf @@ -0,0 +1,57 @@ +variable "prefix" { type = string } +variable "suffix" { type = string } +variable "google_region" { type = string } + +# Spoke network refs +variable "spoke_vpc_id" { type = string } +variable "spoke_vpc_self_link" { type = string } +variable "spoke_vpc_google_project" { type = string } +variable "spoke_vpc_cidr" { type = string } + +# Hub network refs (nullable when no hub) +variable "hub_vpc_id" { + type = string + default = null +} +variable "hub_vpc_self_link" { + type = string + default = null +} +variable "hub_vpc_google_project" { + type = string + default = null +} +variable "hub_subnet_name" { + type = string + default = null +} +variable "hub_vpc_cidr" { + type = string + default = null +} + +# Feature flags +variable "enable_frontend" { + type = bool + default = false +} +variable "enable_backend" { + type = bool + default = false +} +variable "restrict_egress" { + type = bool + default = false +} + +# PSC subnet CIDR +variable "psc_subnet_cidr" { + type = string + description = "CIDR for the dedicated PSC subnet in the spoke VPC" +} + +variable "hive_metastore_ip" { + type = string + default = null + description = "Regional Hive metastore IP (looked up via internal map if null)" +} diff --git a/modules/gcp/private-connectivity/versions.tf b/modules/gcp/private-connectivity/versions.tf new file mode 100644 index 0000000..de067e7 --- /dev/null +++ b/modules/gcp/private-connectivity/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0" + } + } +} From 2834e042f4d48e19049a9b604ba6429b74a5d5b1 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 14:52:42 +0200 Subject: [PATCH 06/14] feat(gcp/account): mws_* resources + fixtures Adds modules/gcp/account housing all databricks_mws_* resources: - databricks_mws_workspaces (always emitted) - databricks_mws_networks (when vpc_source != databricks_managed), with dynamic vpc_endpoints block populated when both PSC endpoint IDs are present - databricks_mws_vpc_endpoint frontend/backend/transit, each gated on its enable_* flag plus presence of the matching forwarding-rule name from private-connectivity - databricks_mws_private_access_settings (when private_access_only) Test fixtures: databricks-managed, byovpc, psc-with-pas. Co-authored-by: Isaac --- modules/gcp/account/Makefile | 7 ++ modules/gcp/account/README.md | 6 ++ modules/gcp/account/main.tf | 49 ++++++++++ modules/gcp/account/outputs.tf | 29 ++++++ modules/gcp/account/pas.tf | 9 ++ modules/gcp/account/tests/byovpc/main.tf | 27 ++++++ .../account/tests/databricks-managed/main.tf | 24 +++++ .../gcp/account/tests/psc-with-pas/main.tf | 36 ++++++++ modules/gcp/account/variables.tf | 89 +++++++++++++++++++ modules/gcp/account/versions.tf | 9 ++ modules/gcp/account/vpc-endpoints.tf | 38 ++++++++ 11 files changed, 323 insertions(+) create mode 100644 modules/gcp/account/Makefile create mode 100644 modules/gcp/account/README.md create mode 100644 modules/gcp/account/main.tf create mode 100644 modules/gcp/account/outputs.tf create mode 100644 modules/gcp/account/pas.tf create mode 100644 modules/gcp/account/tests/byovpc/main.tf create mode 100644 modules/gcp/account/tests/databricks-managed/main.tf create mode 100644 modules/gcp/account/tests/psc-with-pas/main.tf create mode 100644 modules/gcp/account/variables.tf create mode 100644 modules/gcp/account/versions.tf create mode 100644 modules/gcp/account/vpc-endpoints.tf diff --git a/modules/gcp/account/Makefile b/modules/gcp/account/Makefile new file mode 100644 index 0000000..17b32ec --- /dev/null +++ b/modules/gcp/account/Makefile @@ -0,0 +1,7 @@ +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . diff --git a/modules/gcp/account/README.md b/modules/gcp/account/README.md new file mode 100644 index 0000000..2821cb8 --- /dev/null +++ b/modules/gcp/account/README.md @@ -0,0 +1,6 @@ +# modules/gcp/account + +All `databricks_mws_*` resources for the GCP composer: `mws_networks`, `mws_workspaces`, `mws_vpc_endpoint`, `mws_private_access_settings`. + + + diff --git a/modules/gcp/account/main.tf b/modules/gcp/account/main.tf new file mode 100644 index 0000000..4a27787 --- /dev/null +++ b/modules/gcp/account/main.tf @@ -0,0 +1,49 @@ +locals { + workspace_name = coalesce(var.workspace_name, "${var.prefix}-ws-${var.suffix}") + emit_mws_networks = var.vpc_source != "databricks_managed" + emit_vpc_endpoints = var.frontend_psc_fr_id != null && var.backend_psc_fr_id != null + emit_pas = var.private_access_only +} + +resource "databricks_mws_workspaces" "this" { + account_id = var.databricks_account_id + workspace_name = local.workspace_name + location = var.google_region + + cloud_resource_container { + gcp { + project_id = var.google_project + } + } + + network_id = local.emit_mws_networks ? databricks_mws_networks.this[0].network_id : null + private_access_settings_id = local.emit_pas ? databricks_mws_private_access_settings.this[0].private_access_settings_id : null + + token { + comment = "Terraform" + } + + depends_on = [var.nat_dependency] +} + +resource "databricks_mws_networks" "this" { + count = local.emit_mws_networks ? 1 : 0 + + account_id = var.databricks_account_id + network_name = "${var.prefix}-ntw-${var.suffix}" + + gcp_network_info { + network_project_id = var.spoke_vpc_google_project + vpc_id = var.spoke_vpc_name + subnet_id = var.spoke_subnet_name + subnet_region = var.google_region + } + + dynamic "vpc_endpoints" { + for_each = local.emit_vpc_endpoints ? [1] : [] + content { + dataplane_relay = [databricks_mws_vpc_endpoint.backend[0].vpc_endpoint_id] + rest_api = [databricks_mws_vpc_endpoint.frontend[0].vpc_endpoint_id] + } + } +} diff --git a/modules/gcp/account/outputs.tf b/modules/gcp/account/outputs.tf new file mode 100644 index 0000000..bf0782b --- /dev/null +++ b/modules/gcp/account/outputs.tf @@ -0,0 +1,29 @@ +output "workspace_id" { + value = databricks_mws_workspaces.this.workspace_id + description = "Databricks workspace ID" +} + +output "workspace_url" { + value = databricks_mws_workspaces.this.workspace_url + description = "Databricks workspace URL" +} + +output "network_id" { + value = local.emit_mws_networks ? databricks_mws_networks.this[0].network_id : null + description = "mws_networks ID (null when databricks_managed)" +} + +output "frontend_endpoint_id" { + value = var.enable_frontend && var.frontend_psc_fr_id != null ? databricks_mws_vpc_endpoint.frontend[0].vpc_endpoint_id : null + description = "Frontend mws_vpc_endpoint ID (null when no PSC)" +} + +output "backend_endpoint_id" { + value = var.enable_backend && var.backend_psc_fr_id != null ? databricks_mws_vpc_endpoint.backend[0].vpc_endpoint_id : null + description = "Backend mws_vpc_endpoint ID (null when no PSC)" +} + +output "transit_endpoint_id" { + value = var.enable_frontend && var.hub_frontend_psc_fr_id != null ? databricks_mws_vpc_endpoint.transit[0].vpc_endpoint_id : null + description = "Hub-side mws_vpc_endpoint ID (null when no hub)" +} diff --git a/modules/gcp/account/pas.tf b/modules/gcp/account/pas.tf new file mode 100644 index 0000000..7ec7844 --- /dev/null +++ b/modules/gcp/account/pas.tf @@ -0,0 +1,9 @@ +resource "databricks_mws_private_access_settings" "this" { + count = local.emit_pas ? 1 : 0 + + account_id = var.databricks_account_id + private_access_settings_name = "${var.prefix}-pas-${var.suffix}" + region = var.google_region + public_access_enabled = false + private_access_level = "ACCOUNT" +} diff --git a/modules/gcp/account/tests/byovpc/main.tf b/modules/gcp/account/tests/byovpc/main.tf new file mode 100644 index 0000000..a85e293 --- /dev/null +++ b/modules/gcp/account/tests/byovpc/main.tf @@ -0,0 +1,27 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { + source = "databricks/databricks" + } + } +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "account" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + vpc_source = "create" + spoke_vpc_name = "fixture-spoke-vpc-abc123" + spoke_subnet_name = "fixture-subnet-abc123" + spoke_vpc_google_project = "fixture-spoke" +} diff --git a/modules/gcp/account/tests/databricks-managed/main.tf b/modules/gcp/account/tests/databricks-managed/main.tf new file mode 100644 index 0000000..79c6f0d --- /dev/null +++ b/modules/gcp/account/tests/databricks-managed/main.tf @@ -0,0 +1,24 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { + source = "databricks/databricks" + } + } +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "account" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + vpc_source = "databricks_managed" +} diff --git a/modules/gcp/account/tests/psc-with-pas/main.tf b/modules/gcp/account/tests/psc-with-pas/main.tf new file mode 100644 index 0000000..83b418f --- /dev/null +++ b/modules/gcp/account/tests/psc-with-pas/main.tf @@ -0,0 +1,36 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { + source = "databricks/databricks" + } + } +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "account" { + source = "../.." + + prefix = "fixture" + suffix = "abc123" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + vpc_source = "create" + spoke_vpc_name = "fixture-spoke-vpc-abc123" + spoke_subnet_name = "fixture-subnet-abc123" + spoke_vpc_google_project = "fixture-spoke" + hub_vpc_google_project = "fixture-hub" + + frontend_psc_fr_id = "fixture-psc-ws-ep-abc123" + backend_psc_fr_id = "fixture-psc-scc-ep-abc123" + hub_frontend_psc_fr_id = "fixture-hub-psc-ws-ep-abc123" + + enable_frontend = true + enable_backend = true + private_access_only = true +} diff --git a/modules/gcp/account/variables.tf b/modules/gcp/account/variables.tf new file mode 100644 index 0000000..81a6ee7 --- /dev/null +++ b/modules/gcp/account/variables.tf @@ -0,0 +1,89 @@ +variable "prefix" { + type = string +} + +variable "suffix" { + type = string +} + +variable "workspace_name" { + type = string + default = null +} + +variable "databricks_account_id" { + type = string +} + +variable "google_project" { + type = string +} + +variable "google_region" { + type = string +} + +variable "vpc_source" { + type = string + validation { + condition = contains(["databricks_managed", "create", "existing"], var.vpc_source) + error_message = "vpc_source must be one of: databricks_managed, create, existing." + } +} + +variable "spoke_vpc_name" { + type = string + default = null +} + +variable "spoke_subnet_name" { + type = string + default = null +} + +variable "spoke_vpc_google_project" { + type = string + default = null +} + +variable "hub_vpc_google_project" { + type = string + default = null +} + +# Forwarding-rule names from private-connectivity module (gate vpc_endpoint creation) +variable "frontend_psc_fr_id" { + type = string + default = null +} + +variable "backend_psc_fr_id" { + type = string + default = null +} + +variable "hub_frontend_psc_fr_id" { + type = string + default = null +} + +variable "enable_frontend" { + type = bool + default = false +} + +variable "enable_backend" { + type = bool + default = false +} + +variable "private_access_only" { + type = bool + default = false +} + +variable "nat_dependency" { + type = any + default = null + description = "Opaque value used as depends_on for the workspace to ensure NAT readiness" +} diff --git a/modules/gcp/account/versions.tf b/modules/gcp/account/versions.tf new file mode 100644 index 0000000..2d66cee --- /dev/null +++ b/modules/gcp/account/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { + source = "databricks/databricks" + version = ">= 1.0" + } + } +} diff --git a/modules/gcp/account/vpc-endpoints.tf b/modules/gcp/account/vpc-endpoints.tf new file mode 100644 index 0000000..fbf7b5c --- /dev/null +++ b/modules/gcp/account/vpc-endpoints.tf @@ -0,0 +1,38 @@ +resource "databricks_mws_vpc_endpoint" "frontend" { + count = var.enable_frontend && var.frontend_psc_fr_id != null ? 1 : 0 + + account_id = var.databricks_account_id + vpc_endpoint_name = "${var.prefix}-ws-ep-${var.suffix}" + + gcp_vpc_endpoint_info { + project_id = var.spoke_vpc_google_project + psc_endpoint_name = var.frontend_psc_fr_id + endpoint_region = var.google_region + } +} + +resource "databricks_mws_vpc_endpoint" "backend" { + count = var.enable_backend && var.backend_psc_fr_id != null ? 1 : 0 + + account_id = var.databricks_account_id + vpc_endpoint_name = "${var.prefix}-scc-ep-${var.suffix}" + + gcp_vpc_endpoint_info { + project_id = var.spoke_vpc_google_project + psc_endpoint_name = var.backend_psc_fr_id + endpoint_region = var.google_region + } +} + +resource "databricks_mws_vpc_endpoint" "transit" { + count = var.enable_frontend && var.hub_frontend_psc_fr_id != null ? 1 : 0 + + account_id = var.databricks_account_id + vpc_endpoint_name = "${var.prefix}-hub-ep-${var.suffix}" + + gcp_vpc_endpoint_info { + project_id = var.hub_vpc_google_project + psc_endpoint_name = var.hub_frontend_psc_fr_id + endpoint_region = var.google_region + } +} From 1094c23e1a8cf4444f98a984668fa2b7f3897097 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 15:05:38 +0200 Subject: [PATCH 07/14] feat(gcp/dns): hub + spoke private zones + fixture Adds modules/gcp/dns for restricted-egress private DNS: - Hub zones: gcp.databricks.com, gcr.io, googleapis.com, pkg.dev, with workspace/psc-auth/dp records pointing at hub PSC IP - Spoke zone: gcp.databricks.com with workspace/dp records pointing at spoke frontend PSC IP, and tunnel record at backend PSC IP - workspace_dns_id extracted via regex from workspace_url Split into its own submodule (rather than colocated with PSC) because DNS depends on workspace_url which only exists after account creates the workspace; this keeps the composer's dependency graph linear. Test fixture: hub-and-spoke. Co-authored-by: Isaac --- modules/gcp/dns/Makefile | 7 + modules/gcp/dns/README.md | 6 + modules/gcp/dns/hub.tf | 145 ++++++++++++++++++++ modules/gcp/dns/outputs.tf | 1 + modules/gcp/dns/spoke.tf | 41 ++++++ modules/gcp/dns/tests/hub-and-spoke/main.tf | 29 ++++ modules/gcp/dns/variables.tf | 52 +++++++ modules/gcp/dns/versions.tf | 9 ++ 8 files changed, 290 insertions(+) create mode 100644 modules/gcp/dns/Makefile create mode 100644 modules/gcp/dns/README.md create mode 100644 modules/gcp/dns/hub.tf create mode 100644 modules/gcp/dns/outputs.tf create mode 100644 modules/gcp/dns/spoke.tf create mode 100644 modules/gcp/dns/tests/hub-and-spoke/main.tf create mode 100644 modules/gcp/dns/variables.tf create mode 100644 modules/gcp/dns/versions.tf diff --git a/modules/gcp/dns/Makefile b/modules/gcp/dns/Makefile new file mode 100644 index 0000000..17b32ec --- /dev/null +++ b/modules/gcp/dns/Makefile @@ -0,0 +1,7 @@ +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . diff --git a/modules/gcp/dns/README.md b/modules/gcp/dns/README.md new file mode 100644 index 0000000..bde4787 --- /dev/null +++ b/modules/gcp/dns/README.md @@ -0,0 +1,6 @@ +# modules/gcp/dns + +Private DNS zones (hub + spoke) used with restricted-egress workspaces. + + + diff --git a/modules/gcp/dns/hub.tf b/modules/gcp/dns/hub.tf new file mode 100644 index 0000000..206d072 --- /dev/null +++ b/modules/gcp/dns/hub.tf @@ -0,0 +1,145 @@ +locals { + # Regex extracts the workspace DNS id (numeric.numeric) from the URL. + workspace_dns_id = regex("[0-9]+\\.[0-9]+", var.workspace_url) +} + +# === gcp.databricks.com (hub) ============================================ +resource "google_dns_managed_zone" "hub_dbx" { + name = "${var.prefix}-hub-gcp-databricks-com" + project = var.hub_vpc_google_project + dns_name = "gcp.databricks.com." + description = "Private DNS zone for Databricks PSC management" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.hub_vpc_id + } + } +} + +resource "google_dns_record_set" "hub_workspace_url" { + name = "${local.workspace_dns_id}.${google_dns_managed_zone.hub_dbx.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.hub_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_hub] +} + +resource "google_dns_record_set" "hub_psc_auth" { + name = "${var.google_region}.psc-auth.${google_dns_managed_zone.hub_dbx.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.hub_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_hub] +} + +resource "google_dns_record_set" "hub_dp" { + name = "dp-${local.workspace_dns_id}.${google_dns_managed_zone.hub_dbx.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.hub_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_hub] +} + +# === gcr.io ============================================================== +resource "google_dns_managed_zone" "gcr" { + name = "${var.prefix}-gcr-io" + project = var.hub_vpc_google_project + dns_name = "gcr.io." + description = "Private DNS zone for GCR private resolution" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.hub_vpc_id + } + } +} + +resource "google_dns_record_set" "gcr_cname" { + name = "*.${google_dns_managed_zone.gcr.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.gcr.name + type = "CNAME" + ttl = 300 + rrdatas = ["gcr.io."] +} + +resource "google_dns_record_set" "gcr_a" { + name = google_dns_managed_zone.gcr.dns_name + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.gcr.name + type = "A" + ttl = 300 + rrdatas = ["199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11"] +} + +# === googleapis.com ====================================================== +resource "google_dns_managed_zone" "google_apis" { + name = "${var.prefix}-google-apis" + project = var.hub_vpc_google_project + dns_name = "googleapis.com." + description = "Private DNS zone for Google APIs resolution" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.hub_vpc_id + } + } +} + +resource "google_dns_record_set" "google_apis_cname" { + name = "*.${google_dns_managed_zone.google_apis.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.google_apis.name + type = "CNAME" + ttl = 300 + rrdatas = ["restricted.googleapis.com."] +} + +resource "google_dns_record_set" "google_apis_a" { + name = "restricted.${google_dns_managed_zone.google_apis.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.google_apis.name + type = "A" + ttl = 300 + rrdatas = ["199.36.153.4", "199.36.153.5", "199.36.153.6", "199.36.153.7"] +} + +# === pkg.dev ============================================================= +resource "google_dns_managed_zone" "pkg_dev" { + name = "${var.prefix}-pkg-dev" + project = var.hub_vpc_google_project + dns_name = "pkg.dev." + description = "Private DNS zone for Go Packages resolution" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.hub_vpc_id + } + } +} + +resource "google_dns_record_set" "pkg_dev_cname" { + name = "*.${google_dns_managed_zone.pkg_dev.dns_name}" + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.pkg_dev.name + type = "CNAME" + ttl = 300 + rrdatas = ["pkg.dev."] +} + +resource "google_dns_record_set" "pkg_dev_a" { + name = google_dns_managed_zone.pkg_dev.dns_name + project = var.hub_vpc_google_project + managed_zone = google_dns_managed_zone.pkg_dev.name + type = "A" + ttl = 300 + rrdatas = ["199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11"] +} diff --git a/modules/gcp/dns/outputs.tf b/modules/gcp/dns/outputs.tf new file mode 100644 index 0000000..19cbc3d --- /dev/null +++ b/modules/gcp/dns/outputs.tf @@ -0,0 +1 @@ +# This module has no outputs; DNS records are terminal. diff --git a/modules/gcp/dns/spoke.tf b/modules/gcp/dns/spoke.tf new file mode 100644 index 0000000..25c3bc2 --- /dev/null +++ b/modules/gcp/dns/spoke.tf @@ -0,0 +1,41 @@ +# === gcp.databricks.com (spoke) ========================================== +resource "google_dns_managed_zone" "spoke_dbx" { + name = "${var.prefix}-spoke-gcp-databricks-com" + project = var.spoke_vpc_google_project + dns_name = "gcp.databricks.com." + description = "Private DNS zone for Databricks PSC management" + visibility = "private" + + private_visibility_config { + networks { + network_url = var.spoke_vpc_id + } + } +} + +resource "google_dns_record_set" "spoke_workspace_url" { + name = "${local.workspace_dns_id}.${google_dns_managed_zone.spoke_dbx.dns_name}" + project = var.spoke_vpc_google_project + managed_zone = google_dns_managed_zone.spoke_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_spoke] +} + +resource "google_dns_record_set" "spoke_dp" { + name = "dp-${local.workspace_dns_id}.${google_dns_managed_zone.spoke_dbx.dns_name}" + project = var.spoke_vpc_google_project + managed_zone = google_dns_managed_zone.spoke_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.frontend_psc_ip_spoke] +} + +resource "google_dns_record_set" "spoke_tunnel" { + name = "tunnel.${var.google_region}.${google_dns_managed_zone.spoke_dbx.dns_name}" + project = var.spoke_vpc_google_project + managed_zone = google_dns_managed_zone.spoke_dbx.name + type = "A" + ttl = 300 + rrdatas = [var.backend_psc_ip_spoke] +} diff --git a/modules/gcp/dns/tests/hub-and-spoke/main.tf b/modules/gcp/dns/tests/hub-and-spoke/main.tf new file mode 100644 index 0000000..baebc0d --- /dev/null +++ b/modules/gcp/dns/tests/hub-and-spoke/main.tf @@ -0,0 +1,29 @@ +terraform { + required_version = ">= 1.5" +} + +provider "google" { + project = "fixture-spoke" + region = "us-central1" +} + +module "dns" { + source = "../.." + + prefix = "fixture" + google_region = "us-central1" + + hub_vpc_id = "projects/fixture-hub/global/networks/hub-vpc" + hub_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-hub/global/networks/hub-vpc" + hub_vpc_google_project = "fixture-hub" + + spoke_vpc_id = "projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_self_link = "https://www.googleapis.com/compute/v1/projects/fixture-spoke/global/networks/spoke-vpc" + spoke_vpc_google_project = "fixture-spoke" + + workspace_url = "https://1234567890123456.7.gcp.databricks.com" + + frontend_psc_ip_spoke = "10.0.255.4" + frontend_psc_ip_hub = "10.1.0.10" + backend_psc_ip_spoke = "10.0.255.5" +} diff --git a/modules/gcp/dns/variables.tf b/modules/gcp/dns/variables.tf new file mode 100644 index 0000000..cd56786 --- /dev/null +++ b/modules/gcp/dns/variables.tf @@ -0,0 +1,52 @@ +variable "prefix" { + type = string +} + +variable "google_region" { + type = string +} + +# Hub +variable "hub_vpc_id" { + type = string +} + +variable "hub_vpc_self_link" { + type = string +} + +variable "hub_vpc_google_project" { + type = string +} + +# Spoke +variable "spoke_vpc_id" { + type = string +} + +variable "spoke_vpc_self_link" { + type = string +} + +variable "spoke_vpc_google_project" { + type = string +} + +# Workspace +variable "workspace_url" { + type = string +} + +# PSC IPs +variable "frontend_psc_ip_spoke" { + type = string +} + +variable "frontend_psc_ip_hub" { + type = string + default = null +} + +variable "backend_psc_ip_spoke" { + type = string +} diff --git a/modules/gcp/dns/versions.tf b/modules/gcp/dns/versions.tf new file mode 100644 index 0000000..de067e7 --- /dev/null +++ b/modules/gcp/dns/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0" + } + } +} From ce76df18855f10ab43bd5d854dc1cffbf4914dd0 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 15:11:38 +0200 Subject: [PATCH 08/14] feat(gcp/databricks-workspace): composer + preconditions + fixtures Top-level composer that orchestrates network, private-connectivity, account, and dns submodules through orthogonal feature flags: - vpc_source: databricks_managed | create | existing - private_link_frontend, private_link_backend, private_access_only, restricted_egress (each defaults false) - hub/spoke project + CIDR vars required only when restricted_egress Submodules instantiated conditionally: - network when vpc_source != databricks_managed - private-connectivity when any PrivateLink flag is true - account always - dns when restricted_egress is true Cross-variable preconditions (null_resource.preconditions) enforce six rules from the design spec, including: restricted_egress requires vpc_source=create + at least one PrivateLink flag + hub/CIDR vars; databricks_managed forbids any PrivateLink or restricted_egress. Random suffix declared once in the composer, passed to each submodule. Test fixtures: 4 positive (basic, byovpc, existing-vpc, psc-isolated) and 4 negative (each violating one precondition rule). Co-authored-by: Isaac --- modules/gcp/databricks-workspace/Makefile | 3 + modules/gcp/databricks-workspace/README.md | 14 ++ modules/gcp/databricks-workspace/main.tf | 149 ++++++++++++++++++ modules/gcp/databricks-workspace/outputs.tf | 34 ++++ .../databricks-workspace/tests/basic/main.tf | 28 ++++ .../databricks-workspace/tests/byovpc/main.tf | 30 ++++ .../tests/existing-vpc/main.tf | 30 ++++ .../negative-existing-missing-name/main.tf | 29 ++++ .../tests/negative-managed-with-psc/main.tf | 30 ++++ .../main.tf | 30 ++++ .../main.tf | 34 ++++ .../tests/psc-isolated/main.tf | 41 +++++ modules/gcp/databricks-workspace/variables.tf | 121 ++++++++++++++ modules/gcp/databricks-workspace/versions.tf | 21 +++ 14 files changed, 594 insertions(+) create mode 100644 modules/gcp/databricks-workspace/Makefile create mode 100644 modules/gcp/databricks-workspace/README.md create mode 100644 modules/gcp/databricks-workspace/main.tf create mode 100644 modules/gcp/databricks-workspace/outputs.tf create mode 100644 modules/gcp/databricks-workspace/tests/basic/main.tf create mode 100644 modules/gcp/databricks-workspace/tests/byovpc/main.tf create mode 100644 modules/gcp/databricks-workspace/tests/existing-vpc/main.tf create mode 100644 modules/gcp/databricks-workspace/tests/negative-existing-missing-name/main.tf create mode 100644 modules/gcp/databricks-workspace/tests/negative-managed-with-psc/main.tf create mode 100644 modules/gcp/databricks-workspace/tests/negative-restricted-egress-managed/main.tf create mode 100644 modules/gcp/databricks-workspace/tests/negative-restricted-egress-missing-hub/main.tf create mode 100644 modules/gcp/databricks-workspace/tests/psc-isolated/main.tf create mode 100644 modules/gcp/databricks-workspace/variables.tf create mode 100644 modules/gcp/databricks-workspace/versions.tf diff --git a/modules/gcp/databricks-workspace/Makefile b/modules/gcp/databricks-workspace/Makefile new file mode 100644 index 0000000..38b83c2 --- /dev/null +++ b/modules/gcp/databricks-workspace/Makefile @@ -0,0 +1,3 @@ +.PHONY: docs +docs: + terraform-docs -c ../../../.terraform-docs.yml . diff --git a/modules/gcp/databricks-workspace/README.md b/modules/gcp/databricks-workspace/README.md new file mode 100644 index 0000000..cb78800 --- /dev/null +++ b/modules/gcp/databricks-workspace/README.md @@ -0,0 +1,14 @@ +# GCP Databricks Workspace Composer + +This module creates a complete Databricks workspace on Google Cloud Platform with full networking, connectivity, and authentication management. + +## Usage + +See the examples in `tests/` for common scenarios. + +## Components + +- **network**: VPC creation or integration (databricks_managed, create, or existing) +- **private_connectivity**: Private Service Connect (PSC) with optional frontend/backend +- **account**: Databricks MWS resources and workspace +- **dns**: Private DNS zones for restricted egress scenarios diff --git a/modules/gcp/databricks-workspace/main.tf b/modules/gcp/databricks-workspace/main.tf new file mode 100644 index 0000000..0db4f22 --- /dev/null +++ b/modules/gcp/databricks-workspace/main.tf @@ -0,0 +1,149 @@ +locals { + databricks_managed = var.vpc_source == "databricks_managed" + create_vpc = var.vpc_source == "create" + use_existing_vpc = var.vpc_source == "existing" + + any_private_link = var.private_link_frontend || var.private_link_backend + spoke_project = coalesce(var.spoke_vpc_google_project, var.google_project) +} + +resource "random_string" "suffix" { + length = 6 + special = false + upper = false + + lifecycle { + ignore_changes = [special, upper] + } +} + +# Cross-variable preconditions. +resource "null_resource" "preconditions" { + lifecycle { + precondition { + condition = !var.restricted_egress || local.create_vpc + error_message = "restricted_egress=true requires vpc_source=\"create\" (hub-spoke topology needs us to own both VPCs)." + } + precondition { + condition = !var.restricted_egress || local.any_private_link + error_message = "restricted_egress=true requires at least one of private_link_frontend or private_link_backend." + } + precondition { + condition = !var.restricted_egress || (var.hub_vpc_google_project != null && var.hub_vpc_cidr != null && var.psc_subnet_cidr != null) + error_message = "restricted_egress=true requires hub_vpc_google_project, hub_vpc_cidr, and psc_subnet_cidr." + } + precondition { + condition = !local.create_vpc || (var.spoke_vpc_cidr != null && var.subnet_cidr != null) + error_message = "vpc_source=\"create\" requires spoke_vpc_cidr and subnet_cidr." + } + precondition { + condition = !local.use_existing_vpc || (var.existing_vpc_name != null && var.existing_subnet_name != null) + error_message = "vpc_source=\"existing\" requires existing_vpc_name and existing_subnet_name." + } + precondition { + condition = !local.databricks_managed || (!var.private_link_frontend && !var.private_link_backend && !var.restricted_egress) + error_message = "vpc_source=\"databricks_managed\" forbids private_link_frontend, private_link_backend, and restricted_egress." + } + } +} + +module "network" { + source = "../network" + count = local.databricks_managed ? 0 : 1 + + prefix = var.prefix + suffix = random_string.suffix.result + google_region = var.google_region + vpc_source = var.vpc_source + spoke_vpc_google_project = local.spoke_project + + spoke_vpc_cidr = var.spoke_vpc_cidr + subnet_cidr = var.subnet_cidr + pod_cidr = var.pod_cidr + svc_cidr = var.svc_cidr + + existing_vpc_name = var.existing_vpc_name + existing_subnet_name = var.existing_subnet_name + + create_hub = var.restricted_egress + hub_vpc_google_project = var.hub_vpc_google_project + hub_vpc_cidr = var.hub_vpc_cidr + is_spoke_vpc_shared = var.is_spoke_vpc_shared + workspace_google_project = var.google_project +} + +module "private_connectivity" { + source = "../private-connectivity" + count = local.any_private_link ? 1 : 0 + + prefix = var.prefix + suffix = random_string.suffix.result + google_region = var.google_region + + spoke_vpc_id = module.network[0].spoke_vpc_id + spoke_vpc_self_link = module.network[0].spoke_vpc_self_link + spoke_vpc_google_project = local.spoke_project + spoke_vpc_cidr = var.spoke_vpc_cidr + + hub_vpc_id = var.restricted_egress ? module.network[0].hub_vpc_id : null + hub_vpc_self_link = var.restricted_egress ? module.network[0].hub_vpc_self_link : null + hub_vpc_google_project = var.hub_vpc_google_project + hub_subnet_name = var.restricted_egress ? module.network[0].hub_subnet_name : null + hub_vpc_cidr = var.hub_vpc_cidr + + enable_frontend = var.private_link_frontend + enable_backend = var.private_link_backend + restrict_egress = var.restricted_egress + psc_subnet_cidr = var.psc_subnet_cidr + + hive_metastore_ip = var.hive_metastore_ip +} + +module "account" { + source = "../account" + + prefix = var.prefix + suffix = random_string.suffix.result + workspace_name = var.workspace_name + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + vpc_source = var.vpc_source + + spoke_vpc_name = local.databricks_managed ? null : module.network[0].spoke_vpc_name + spoke_subnet_name = local.databricks_managed ? null : module.network[0].spoke_subnet_name + spoke_vpc_google_project = local.spoke_project + hub_vpc_google_project = var.hub_vpc_google_project + + frontend_psc_fr_id = local.any_private_link ? module.private_connectivity[0].frontend_psc_fr_id : null + backend_psc_fr_id = local.any_private_link ? module.private_connectivity[0].backend_psc_fr_id : null + hub_frontend_psc_fr_id = local.any_private_link ? module.private_connectivity[0].hub_frontend_psc_fr_id : null + + enable_frontend = var.private_link_frontend + enable_backend = var.private_link_backend + private_access_only = var.private_access_only + + nat_dependency = local.databricks_managed ? null : module.network[0].nat_id +} + +module "dns" { + source = "../dns" + count = var.restricted_egress ? 1 : 0 + + prefix = var.prefix + google_region = var.google_region + + hub_vpc_id = module.network[0].hub_vpc_id + hub_vpc_self_link = module.network[0].hub_vpc_self_link + hub_vpc_google_project = var.hub_vpc_google_project + + spoke_vpc_id = module.network[0].spoke_vpc_id + spoke_vpc_self_link = module.network[0].spoke_vpc_self_link + spoke_vpc_google_project = local.spoke_project + + workspace_url = module.account.workspace_url + + frontend_psc_ip_spoke = module.private_connectivity[0].frontend_psc_ip_spoke + frontend_psc_ip_hub = module.private_connectivity[0].frontend_psc_ip_hub + backend_psc_ip_spoke = module.private_connectivity[0].backend_psc_ip_spoke +} diff --git a/modules/gcp/databricks-workspace/outputs.tf b/modules/gcp/databricks-workspace/outputs.tf new file mode 100644 index 0000000..af8e9e0 --- /dev/null +++ b/modules/gcp/databricks-workspace/outputs.tf @@ -0,0 +1,34 @@ +output "workspace_id" { + value = module.account.workspace_id + description = "Databricks workspace ID" +} + +output "workspace_url" { + value = module.account.workspace_url + description = "Databricks workspace URL" +} + +output "network_id" { + value = module.account.network_id + description = "mws_networks ID (null when databricks_managed)" +} + +output "vpc_id" { + value = try(module.network[0].spoke_vpc_id, null) + description = "Spoke VPC ID (null when databricks_managed)" +} + +output "spoke_vpc_id" { + value = try(module.network[0].spoke_vpc_id, null) + description = "Spoke VPC ID (null when databricks_managed)" +} + +output "hub_vpc_id" { + value = try(module.network[0].hub_vpc_id, null) + description = "Hub VPC ID (null when not restricted_egress)" +} + +output "suffix" { + value = random_string.suffix.result + description = "Random suffix used in resource names" +} diff --git a/modules/gcp/databricks-workspace/tests/basic/main.tf b/modules/gcp/databricks-workspace/tests/basic/main.tf new file mode 100644 index 0000000..2a96df8 --- /dev/null +++ b/modules/gcp/databricks-workspace/tests/basic/main.tf @@ -0,0 +1,28 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { + project = "fixture-workspace" + region = "us-central1" +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "databricks_managed" +} diff --git a/modules/gcp/databricks-workspace/tests/byovpc/main.tf b/modules/gcp/databricks-workspace/tests/byovpc/main.tf new file mode 100644 index 0000000..18ac0dc --- /dev/null +++ b/modules/gcp/databricks-workspace/tests/byovpc/main.tf @@ -0,0 +1,30 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { + project = "fixture-workspace" + region = "us-central1" +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "create" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" +} diff --git a/modules/gcp/databricks-workspace/tests/existing-vpc/main.tf b/modules/gcp/databricks-workspace/tests/existing-vpc/main.tf new file mode 100644 index 0000000..a6b992c --- /dev/null +++ b/modules/gcp/databricks-workspace/tests/existing-vpc/main.tf @@ -0,0 +1,30 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { + project = "fixture-workspace" + region = "us-central1" +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "existing" + existing_vpc_name = "preexisting-vpc" + existing_subnet_name = "preexisting-subnet" +} diff --git a/modules/gcp/databricks-workspace/tests/negative-existing-missing-name/main.tf b/modules/gcp/databricks-workspace/tests/negative-existing-missing-name/main.tf new file mode 100644 index 0000000..31ee19e --- /dev/null +++ b/modules/gcp/databricks-workspace/tests/negative-existing-missing-name/main.tf @@ -0,0 +1,29 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { + project = "fixture-workspace" + region = "us-central1" +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +# precondition fail: vpc_source="existing" requires existing_vpc_name + existing_subnet_name +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "existing" +} diff --git a/modules/gcp/databricks-workspace/tests/negative-managed-with-psc/main.tf b/modules/gcp/databricks-workspace/tests/negative-managed-with-psc/main.tf new file mode 100644 index 0000000..696e764 --- /dev/null +++ b/modules/gcp/databricks-workspace/tests/negative-managed-with-psc/main.tf @@ -0,0 +1,30 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { + project = "fixture-workspace" + region = "us-central1" +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +# precondition fail: vpc_source="databricks_managed" forbids private_link_frontend +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "databricks_managed" + private_link_frontend = true +} diff --git a/modules/gcp/databricks-workspace/tests/negative-restricted-egress-managed/main.tf b/modules/gcp/databricks-workspace/tests/negative-restricted-egress-managed/main.tf new file mode 100644 index 0000000..df6ca74 --- /dev/null +++ b/modules/gcp/databricks-workspace/tests/negative-restricted-egress-managed/main.tf @@ -0,0 +1,30 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { + project = "fixture-workspace" + region = "us-central1" +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +# precondition fail: restricted_egress=true requires vpc_source="create" +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "databricks_managed" + restricted_egress = true +} diff --git a/modules/gcp/databricks-workspace/tests/negative-restricted-egress-missing-hub/main.tf b/modules/gcp/databricks-workspace/tests/negative-restricted-egress-missing-hub/main.tf new file mode 100644 index 0000000..f93d3cb --- /dev/null +++ b/modules/gcp/databricks-workspace/tests/negative-restricted-egress-missing-hub/main.tf @@ -0,0 +1,34 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { + project = "fixture-workspace" + region = "us-central1" +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +# precondition fail: restricted_egress=true requires hub_vpc_google_project, hub_vpc_cidr, psc_subnet_cidr +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "create" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" + private_link_frontend = true + private_link_backend = true + restricted_egress = true +} diff --git a/modules/gcp/databricks-workspace/tests/psc-isolated/main.tf b/modules/gcp/databricks-workspace/tests/psc-isolated/main.tf new file mode 100644 index 0000000..cc60d2a --- /dev/null +++ b/modules/gcp/databricks-workspace/tests/psc-isolated/main.tf @@ -0,0 +1,41 @@ +terraform { + required_version = ">= 1.5" + required_providers { + databricks = { source = "databricks/databricks" } + google = { source = "hashicorp/google" } + } +} + +provider "google" { + project = "fixture-workspace" + region = "us-central1" +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + account_id = "00000000-0000-0000-0000-000000000000" +} + +module "workspace" { + source = "../.." + + prefix = "fixture" + databricks_account_id = "00000000-0000-0000-0000-000000000000" + google_project = "fixture-workspace" + google_region = "us-central1" + + vpc_source = "create" + spoke_vpc_cidr = "10.0.0.0/16" + subnet_cidr = "10.0.0.0/22" + + private_link_frontend = true + private_link_backend = true + private_access_only = true + restricted_egress = true + + spoke_vpc_google_project = "fixture-spoke" + hub_vpc_google_project = "fixture-hub" + is_spoke_vpc_shared = true + hub_vpc_cidr = "10.1.0.0/24" + psc_subnet_cidr = "10.0.255.0/28" +} diff --git a/modules/gcp/databricks-workspace/variables.tf b/modules/gcp/databricks-workspace/variables.tf new file mode 100644 index 0000000..14b959c --- /dev/null +++ b/modules/gcp/databricks-workspace/variables.tf @@ -0,0 +1,121 @@ +# === Identity =========================================================== +variable "prefix" { + type = string +} + +variable "databricks_account_id" { + type = string +} + +variable "google_project" { + type = string +} + +variable "google_region" { + type = string +} + +variable "workspace_name" { + type = string + default = null +} + +variable "tags" { + type = map(string) + default = {} +} + +# === VPC source ========================================================= +variable "vpc_source" { + type = string + default = "databricks_managed" + description = "One of: databricks_managed, create, existing" + validation { + condition = contains(["databricks_managed", "create", "existing"], var.vpc_source) + error_message = "vpc_source must be one of: databricks_managed, create, existing." + } +} + +# When vpc_source = "create" +variable "spoke_vpc_cidr" { + type = string + default = null +} + +variable "subnet_cidr" { + type = string + default = null +} + +variable "pod_cidr" { + type = string + default = null +} + +variable "svc_cidr" { + type = string + default = null +} + +# When vpc_source = "existing" +variable "existing_vpc_name" { + type = string + default = null +} + +variable "existing_subnet_name" { + type = string + default = null +} + +# === Connectivity feature flags ========================================= +variable "private_link_frontend" { + type = bool + default = false +} + +variable "private_link_backend" { + type = bool + default = false +} + +variable "private_access_only" { + type = bool + default = false +} + +variable "restricted_egress" { + type = bool + default = false +} + +# === Required when restricted_egress = true ============================= +variable "hub_vpc_google_project" { + type = string + default = null +} + +variable "spoke_vpc_google_project" { + type = string + default = null +} + +variable "is_spoke_vpc_shared" { + type = bool + default = false +} + +variable "hub_vpc_cidr" { + type = string + default = null +} + +variable "psc_subnet_cidr" { + type = string + default = null +} + +variable "hive_metastore_ip" { + type = string + default = null +} diff --git a/modules/gcp/databricks-workspace/versions.tf b/modules/gcp/databricks-workspace/versions.tf new file mode 100644 index 0000000..ead1a86 --- /dev/null +++ b/modules/gcp/databricks-workspace/versions.tf @@ -0,0 +1,21 @@ +terraform { + required_version = ">= 1.5" + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.0" + } + databricks = { + source = "databricks/databricks" + version = ">= 1.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.0" + } + null = { + source = "hashicorp/null" + version = ">= 3.0" + } + } +} From 334cc3263e5e3584a13b30fcc7d4656bc8659eb2 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 15:25:37 +0200 Subject: [PATCH 09/14] refactor(gcp/service-account): relocate from modules/gcp-sa-provisioning git mv only; no functional changes. Old path has a deprecation README pointing to the new location. Makefile updated for new depth. Co-authored-by: Isaac --- modules/gcp-sa-provisioning/Makefile | 7 -- modules/gcp-sa-provisioning/README.md | 71 +++---------------- modules/gcp/service-account/Makefile | 7 ++ modules/gcp/service-account/README.md | 69 ++++++++++++++++++ .../service-account}/init.tf | 0 .../service-account}/main.tf | 0 .../service-account}/outputs.tf | 0 .../service-account}/variables.tf | 0 8 files changed, 84 insertions(+), 70 deletions(-) delete mode 100644 modules/gcp-sa-provisioning/Makefile create mode 100644 modules/gcp/service-account/Makefile create mode 100644 modules/gcp/service-account/README.md rename modules/{gcp-sa-provisioning => gcp/service-account}/init.tf (100%) rename modules/{gcp-sa-provisioning => gcp/service-account}/main.tf (100%) rename modules/{gcp-sa-provisioning => gcp/service-account}/outputs.tf (100%) rename modules/{gcp-sa-provisioning => gcp/service-account}/variables.tf (100%) diff --git a/modules/gcp-sa-provisioning/Makefile b/modules/gcp-sa-provisioning/Makefile deleted file mode 100644 index 653039d..0000000 --- a/modules/gcp-sa-provisioning/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -.PHONY: docs test_docs - -docs: - terraform-docs -c ../../.terraform-docs.yml . - -test_docs: - terraform-docs -c ../../.terraform-docs.yml --output-check . diff --git a/modules/gcp-sa-provisioning/README.md b/modules/gcp-sa-provisioning/README.md index 75b57fc..1132953 100644 --- a/modules/gcp-sa-provisioning/README.md +++ b/modules/gcp-sa-provisioning/README.md @@ -1,69 +1,14 @@ -# Provisioning a Google Service Account that can be used to deploy Databricks workspace on GCP -========================= +# DEPRECATED — moved to `modules/gcp/service-account/` -In this template, we show how to deploy a service account that can be used to deploy gcp workspaces. +This module has been relocated to [`../gcp/service-account/`](../gcp/service-account/). -In this template, we create a [Service Account](https://cloud.google.com/iam/docs/service-account-overview) with minimal permissions that allow to provision a workspacce with both managed and user-provisionned vpc. +All variables, outputs, and resource addresses are unchanged. Update your +module `source` from: + source = "github.com/databricks/terraform-databricks-examples/modules/gcp-sa-provisioning" -## Requirements +to: -- Your user that you use to delegate from needs a set of permissions detailed [here](https://docs.gcp.databricks.com/administration-guide/cloud-configurations/gcp/permissions.html#required-user-permissions-or-service-account-permissions-to-create-a-workspace) + source = "github.com/databricks/terraform-databricks-examples/modules/gcp/service-account" -- The built-in roles of Kubernetes Admin and Compute Storage Admin needs to be available - -- you need to run `gcloud auth application-default login` and login with your google account - -## Run as an SA - -You can do the same thing by provisioning a service account that will have the same permissions - and associate the key associated to it. - - -## Run the tempalte - -- You need to fill in the variables.tf -- run `terraform init` -- run `teraform apply` - - -## Requirements - -No requirements. - -## Providers - -| Name | Version | -|------|---------| -| [google](#provider\_google) | n/a | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [google_project_iam_custom_role.workspace_creator](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_custom_role) | resource | -| [google_project_iam_member.sa2_can_create_workspaces](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource | -| [google_service_account.sa2](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource | -| [google_service_account_iam_policy.impersonatable](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_iam_policy) | resource | -| [google_client_config.current](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/client_config) | data source | -| [google_client_openid_userinfo.me](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/client_openid_userinfo) | data source | -| [google_iam_policy.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/iam_policy) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [delegate\_from](#input\_delegate\_from) | Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com) | `list(string)` | n/a | yes | -| [google\_project](#input\_google\_project) | Google project for VCP/workspace deployment | `string` | n/a | yes | -| [prefix](#input\_prefix) | Prefix to use in generated service account name | `string` | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| [custom\_role\_url](#output\_custom\_role\_url) | n/a | -| [service\_account](#output\_service\_account) | Add this email as a user in the Databricks account console | - +This stub will be removed in PR 6 of the GCP modules refactor. diff --git a/modules/gcp/service-account/Makefile b/modules/gcp/service-account/Makefile new file mode 100644 index 0000000..17b32ec --- /dev/null +++ b/modules/gcp/service-account/Makefile @@ -0,0 +1,7 @@ +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . diff --git a/modules/gcp/service-account/README.md b/modules/gcp/service-account/README.md new file mode 100644 index 0000000..75b57fc --- /dev/null +++ b/modules/gcp/service-account/README.md @@ -0,0 +1,69 @@ +# Provisioning a Google Service Account that can be used to deploy Databricks workspace on GCP +========================= + +In this template, we show how to deploy a service account that can be used to deploy gcp workspaces. + +In this template, we create a [Service Account](https://cloud.google.com/iam/docs/service-account-overview) with minimal permissions that allow to provision a workspacce with both managed and user-provisionned vpc. + + +## Requirements + +- Your user that you use to delegate from needs a set of permissions detailed [here](https://docs.gcp.databricks.com/administration-guide/cloud-configurations/gcp/permissions.html#required-user-permissions-or-service-account-permissions-to-create-a-workspace) + +- The built-in roles of Kubernetes Admin and Compute Storage Admin needs to be available + +- you need to run `gcloud auth application-default login` and login with your google account + +## Run as an SA + +You can do the same thing by provisioning a service account that will have the same permissions - and associate the key associated to it. + + +## Run the tempalte + +- You need to fill in the variables.tf +- run `terraform init` +- run `teraform apply` + + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [google](#provider\_google) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [google_project_iam_custom_role.workspace_creator](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_custom_role) | resource | +| [google_project_iam_member.sa2_can_create_workspaces](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource | +| [google_service_account.sa2](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource | +| [google_service_account_iam_policy.impersonatable](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_iam_policy) | resource | +| [google_client_config.current](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/client_config) | data source | +| [google_client_openid_userinfo.me](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/client_openid_userinfo) | data source | +| [google_iam_policy.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/iam_policy) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [delegate\_from](#input\_delegate\_from) | Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com) | `list(string)` | n/a | yes | +| [google\_project](#input\_google\_project) | Google project for VCP/workspace deployment | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix to use in generated service account name | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [custom\_role\_url](#output\_custom\_role\_url) | n/a | +| [service\_account](#output\_service\_account) | Add this email as a user in the Databricks account console | + diff --git a/modules/gcp-sa-provisioning/init.tf b/modules/gcp/service-account/init.tf similarity index 100% rename from modules/gcp-sa-provisioning/init.tf rename to modules/gcp/service-account/init.tf diff --git a/modules/gcp-sa-provisioning/main.tf b/modules/gcp/service-account/main.tf similarity index 100% rename from modules/gcp-sa-provisioning/main.tf rename to modules/gcp/service-account/main.tf diff --git a/modules/gcp-sa-provisioning/outputs.tf b/modules/gcp/service-account/outputs.tf similarity index 100% rename from modules/gcp-sa-provisioning/outputs.tf rename to modules/gcp/service-account/outputs.tf diff --git a/modules/gcp-sa-provisioning/variables.tf b/modules/gcp/service-account/variables.tf similarity index 100% rename from modules/gcp-sa-provisioning/variables.tf rename to modules/gcp/service-account/variables.tf From 085fa2b591d2888d12a809389ef8ade76f8c5fb9 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 15:26:46 +0200 Subject: [PATCH 10/14] refactor(gcp/unity-catalog): relocate from modules/gcp-unity-catalog git mv only; no functional changes. Old path has a deprecation README. Co-authored-by: Isaac --- modules/gcp-unity-catalog/Makefile | 7 ------- modules/gcp-unity-catalog/README.md | 14 ++++++++++++++ modules/gcp/unity-catalog/Makefile | 7 +++++++ .../unity-catalog}/databricks-cloud-resources.tf | 0 .../unity-catalog}/gcs.tf | 0 .../unity-catalog}/terraform.tf | 0 .../unity-catalog}/variables.tf | 0 7 files changed, 21 insertions(+), 7 deletions(-) delete mode 100644 modules/gcp-unity-catalog/Makefile create mode 100644 modules/gcp-unity-catalog/README.md create mode 100644 modules/gcp/unity-catalog/Makefile rename modules/{gcp-unity-catalog => gcp/unity-catalog}/databricks-cloud-resources.tf (100%) rename modules/{gcp-unity-catalog => gcp/unity-catalog}/gcs.tf (100%) rename modules/{gcp-unity-catalog => gcp/unity-catalog}/terraform.tf (100%) rename modules/{gcp-unity-catalog => gcp/unity-catalog}/variables.tf (100%) diff --git a/modules/gcp-unity-catalog/Makefile b/modules/gcp-unity-catalog/Makefile deleted file mode 100644 index 653039d..0000000 --- a/modules/gcp-unity-catalog/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -.PHONY: docs test_docs - -docs: - terraform-docs -c ../../.terraform-docs.yml . - -test_docs: - terraform-docs -c ../../.terraform-docs.yml --output-check . diff --git a/modules/gcp-unity-catalog/README.md b/modules/gcp-unity-catalog/README.md new file mode 100644 index 0000000..9987312 --- /dev/null +++ b/modules/gcp-unity-catalog/README.md @@ -0,0 +1,14 @@ +# DEPRECATED — moved to `modules/gcp/unity-catalog/` + +This module has been relocated to [`../gcp/unity-catalog/`](../gcp/unity-catalog/). + +All variables, outputs, and resource addresses are unchanged. Update your +module `source` from: + + source = "github.com/databricks/terraform-databricks-examples/modules/gcp-unity-catalog" + +to: + + source = "github.com/databricks/terraform-databricks-examples/modules/gcp/unity-catalog" + +This stub will be removed in PR 6 of the GCP modules refactor. diff --git a/modules/gcp/unity-catalog/Makefile b/modules/gcp/unity-catalog/Makefile new file mode 100644 index 0000000..17b32ec --- /dev/null +++ b/modules/gcp/unity-catalog/Makefile @@ -0,0 +1,7 @@ +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../../.terraform-docs.yml --output-check . diff --git a/modules/gcp-unity-catalog/databricks-cloud-resources.tf b/modules/gcp/unity-catalog/databricks-cloud-resources.tf similarity index 100% rename from modules/gcp-unity-catalog/databricks-cloud-resources.tf rename to modules/gcp/unity-catalog/databricks-cloud-resources.tf diff --git a/modules/gcp-unity-catalog/gcs.tf b/modules/gcp/unity-catalog/gcs.tf similarity index 100% rename from modules/gcp-unity-catalog/gcs.tf rename to modules/gcp/unity-catalog/gcs.tf diff --git a/modules/gcp-unity-catalog/terraform.tf b/modules/gcp/unity-catalog/terraform.tf similarity index 100% rename from modules/gcp-unity-catalog/terraform.tf rename to modules/gcp/unity-catalog/terraform.tf diff --git a/modules/gcp-unity-catalog/variables.tf b/modules/gcp/unity-catalog/variables.tf similarity index 100% rename from modules/gcp-unity-catalog/variables.tf rename to modules/gcp/unity-catalog/variables.tf From 4b617d8ecce4cd1514c94b2ef7788a7dc3fb2374 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Thu, 14 May 2026 15:27:04 +0200 Subject: [PATCH 11/14] docs(gcp): terraform-docs READMEs for new submodules Generated README content for network, private-connectivity, account, dns, databricks-workspace, service-account, and unity-catalog via `make -C modules/gcp docs`. Includes an added README placeholder for unity-catalog (the original modules/gcp-unity-catalog had no README). Co-authored-by: Isaac --- modules/gcp/account/README.md | 61 ++++++++++++++++++ modules/gcp/databricks-workspace/README.md | 75 ++++++++++++++++++++++ modules/gcp/dns/README.md | 59 +++++++++++++++++ modules/gcp/network/README.md | 71 ++++++++++++++++++++ modules/gcp/private-connectivity/README.md | 67 +++++++++++++++++++ modules/gcp/service-account/README.md | 2 +- modules/gcp/unity-catalog/README.md | 50 +++++++++++++++ 7 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 modules/gcp/unity-catalog/README.md diff --git a/modules/gcp/account/README.md b/modules/gcp/account/README.md index 2821cb8..a4d7408 100644 --- a/modules/gcp/account/README.md +++ b/modules/gcp/account/README.md @@ -3,4 +3,65 @@ All `databricks_mws_*` resources for the GCP composer: `mws_networks`, `mws_workspaces`, `mws_vpc_endpoint`, `mws_private_access_settings`. +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5 | +| [databricks](#requirement\_databricks) | >= 1.0 | + +## Providers + +| Name | Version | +|------|---------| +| [databricks](#provider\_databricks) | 1.114.2 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [databricks_mws_networks.this](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_networks) | resource | +| [databricks_mws_private_access_settings.this](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_private_access_settings) | resource | +| [databricks_mws_vpc_endpoint.backend](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_vpc_endpoint) | resource | +| [databricks_mws_vpc_endpoint.frontend](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_vpc_endpoint) | resource | +| [databricks_mws_vpc_endpoint.transit](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_vpc_endpoint) | resource | +| [databricks_mws_workspaces.this](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_workspaces) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [databricks\_account\_id](#input\_databricks\_account\_id) | n/a | `string` | n/a | yes | +| [google\_project](#input\_google\_project) | n/a | `string` | n/a | yes | +| [google\_region](#input\_google\_region) | n/a | `string` | n/a | yes | +| [prefix](#input\_prefix) | n/a | `string` | n/a | yes | +| [suffix](#input\_suffix) | n/a | `string` | n/a | yes | +| [vpc\_source](#input\_vpc\_source) | n/a | `string` | n/a | yes | +| [backend\_psc\_fr\_id](#input\_backend\_psc\_fr\_id) | n/a | `string` | `null` | no | +| [enable\_backend](#input\_enable\_backend) | n/a | `bool` | `false` | no | +| [enable\_frontend](#input\_enable\_frontend) | n/a | `bool` | `false` | no | +| [frontend\_psc\_fr\_id](#input\_frontend\_psc\_fr\_id) | Forwarding-rule names from private-connectivity module (gate vpc\_endpoint creation) | `string` | `null` | no | +| [hub\_frontend\_psc\_fr\_id](#input\_hub\_frontend\_psc\_fr\_id) | n/a | `string` | `null` | no | +| [hub\_vpc\_google\_project](#input\_hub\_vpc\_google\_project) | n/a | `string` | `null` | no | +| [nat\_dependency](#input\_nat\_dependency) | Opaque value used as depends\_on for the workspace to ensure NAT readiness | `any` | `null` | no | +| [private\_access\_only](#input\_private\_access\_only) | n/a | `bool` | `false` | no | +| [spoke\_subnet\_name](#input\_spoke\_subnet\_name) | n/a | `string` | `null` | no | +| [spoke\_vpc\_google\_project](#input\_spoke\_vpc\_google\_project) | n/a | `string` | `null` | no | +| [spoke\_vpc\_name](#input\_spoke\_vpc\_name) | n/a | `string` | `null` | no | +| [workspace\_name](#input\_workspace\_name) | n/a | `string` | `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [backend\_endpoint\_id](#output\_backend\_endpoint\_id) | Backend mws\_vpc\_endpoint ID (null when no PSC) | +| [frontend\_endpoint\_id](#output\_frontend\_endpoint\_id) | Frontend mws\_vpc\_endpoint ID (null when no PSC) | +| [network\_id](#output\_network\_id) | mws\_networks ID (null when databricks\_managed) | +| [transit\_endpoint\_id](#output\_transit\_endpoint\_id) | Hub-side mws\_vpc\_endpoint ID (null when no hub) | +| [workspace\_id](#output\_workspace\_id) | Databricks workspace ID | +| [workspace\_url](#output\_workspace\_url) | Databricks workspace URL | diff --git a/modules/gcp/databricks-workspace/README.md b/modules/gcp/databricks-workspace/README.md index cb78800..c39a4cc 100644 --- a/modules/gcp/databricks-workspace/README.md +++ b/modules/gcp/databricks-workspace/README.md @@ -12,3 +12,78 @@ See the examples in `tests/` for common scenarios. - **private_connectivity**: Private Service Connect (PSC) with optional frontend/backend - **account**: Databricks MWS resources and workspace - **dns**: Private DNS zones for restricted egress scenarios + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5 | +| [databricks](#requirement\_databricks) | >= 1.0 | +| [google](#requirement\_google) | >= 4.0 | +| [null](#requirement\_null) | >= 3.0 | +| [random](#requirement\_random) | >= 3.0 | + +## Providers + +| Name | Version | +|------|---------| +| [null](#provider\_null) | 3.2.4 | +| [random](#provider\_random) | 3.8.1 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [account](#module\_account) | ../account | n/a | +| [dns](#module\_dns) | ../dns | n/a | +| [network](#module\_network) | ../network | n/a | +| [private\_connectivity](#module\_private\_connectivity) | ../private-connectivity | n/a | + +## Resources + +| Name | Type | +|------|------| +| [null_resource.preconditions](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [databricks\_account\_id](#input\_databricks\_account\_id) | n/a | `string` | n/a | yes | +| [google\_project](#input\_google\_project) | n/a | `string` | n/a | yes | +| [google\_region](#input\_google\_region) | n/a | `string` | n/a | yes | +| [prefix](#input\_prefix) | === Identity =========================================================== | `string` | n/a | yes | +| [existing\_subnet\_name](#input\_existing\_subnet\_name) | n/a | `string` | `null` | no | +| [existing\_vpc\_name](#input\_existing\_vpc\_name) | When vpc\_source = "existing" | `string` | `null` | no | +| [hive\_metastore\_ip](#input\_hive\_metastore\_ip) | n/a | `string` | `null` | no | +| [hub\_vpc\_cidr](#input\_hub\_vpc\_cidr) | n/a | `string` | `null` | no | +| [hub\_vpc\_google\_project](#input\_hub\_vpc\_google\_project) | === Required when restricted\_egress = true ============================= | `string` | `null` | no | +| [is\_spoke\_vpc\_shared](#input\_is\_spoke\_vpc\_shared) | n/a | `bool` | `false` | no | +| [pod\_cidr](#input\_pod\_cidr) | n/a | `string` | `null` | no | +| [private\_access\_only](#input\_private\_access\_only) | n/a | `bool` | `false` | no | +| [private\_link\_backend](#input\_private\_link\_backend) | n/a | `bool` | `false` | no | +| [private\_link\_frontend](#input\_private\_link\_frontend) | === Connectivity feature flags ========================================= | `bool` | `false` | no | +| [psc\_subnet\_cidr](#input\_psc\_subnet\_cidr) | n/a | `string` | `null` | no | +| [restricted\_egress](#input\_restricted\_egress) | n/a | `bool` | `false` | no | +| [spoke\_vpc\_cidr](#input\_spoke\_vpc\_cidr) | When vpc\_source = "create" | `string` | `null` | no | +| [spoke\_vpc\_google\_project](#input\_spoke\_vpc\_google\_project) | n/a | `string` | `null` | no | +| [subnet\_cidr](#input\_subnet\_cidr) | n/a | `string` | `null` | no | +| [svc\_cidr](#input\_svc\_cidr) | n/a | `string` | `null` | no | +| [tags](#input\_tags) | n/a | `map(string)` | `{}` | no | +| [vpc\_source](#input\_vpc\_source) | One of: databricks\_managed, create, existing | `string` | `"databricks_managed"` | no | +| [workspace\_name](#input\_workspace\_name) | n/a | `string` | `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [hub\_vpc\_id](#output\_hub\_vpc\_id) | Hub VPC ID (null when not restricted\_egress) | +| [network\_id](#output\_network\_id) | mws\_networks ID (null when databricks\_managed) | +| [spoke\_vpc\_id](#output\_spoke\_vpc\_id) | Spoke VPC ID (null when databricks\_managed) | +| [suffix](#output\_suffix) | Random suffix used in resource names | +| [vpc\_id](#output\_vpc\_id) | Spoke VPC ID (null when databricks\_managed) | +| [workspace\_id](#output\_workspace\_id) | Databricks workspace ID | +| [workspace\_url](#output\_workspace\_url) | Databricks workspace URL | + \ No newline at end of file diff --git a/modules/gcp/dns/README.md b/modules/gcp/dns/README.md index bde4787..467f3d5 100644 --- a/modules/gcp/dns/README.md +++ b/modules/gcp/dns/README.md @@ -3,4 +3,63 @@ Private DNS zones (hub + spoke) used with restricted-egress workspaces. +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5 | +| [google](#requirement\_google) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [google](#provider\_google) | 7.31.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [google_dns_managed_zone.gcr](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | +| [google_dns_managed_zone.google_apis](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | +| [google_dns_managed_zone.hub_dbx](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | +| [google_dns_managed_zone.pkg_dev](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | +| [google_dns_managed_zone.spoke_dbx](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | +| [google_dns_record_set.gcr_a](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.gcr_cname](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.google_apis_a](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.google_apis_cname](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.hub_dp](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.hub_psc_auth](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.hub_workspace_url](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.pkg_dev_a](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.pkg_dev_cname](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.spoke_dp](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.spoke_tunnel](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | +| [google_dns_record_set.spoke_workspace_url](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [backend\_psc\_ip\_spoke](#input\_backend\_psc\_ip\_spoke) | n/a | `string` | n/a | yes | +| [frontend\_psc\_ip\_spoke](#input\_frontend\_psc\_ip\_spoke) | PSC IPs | `string` | n/a | yes | +| [google\_region](#input\_google\_region) | n/a | `string` | n/a | yes | +| [hub\_vpc\_google\_project](#input\_hub\_vpc\_google\_project) | n/a | `string` | n/a | yes | +| [hub\_vpc\_id](#input\_hub\_vpc\_id) | Hub | `string` | n/a | yes | +| [hub\_vpc\_self\_link](#input\_hub\_vpc\_self\_link) | n/a | `string` | n/a | yes | +| [prefix](#input\_prefix) | n/a | `string` | n/a | yes | +| [spoke\_vpc\_google\_project](#input\_spoke\_vpc\_google\_project) | n/a | `string` | n/a | yes | +| [spoke\_vpc\_id](#input\_spoke\_vpc\_id) | Spoke | `string` | n/a | yes | +| [spoke\_vpc\_self\_link](#input\_spoke\_vpc\_self\_link) | n/a | `string` | n/a | yes | +| [workspace\_url](#input\_workspace\_url) | Workspace | `string` | n/a | yes | +| [frontend\_psc\_ip\_hub](#input\_frontend\_psc\_ip\_hub) | n/a | `string` | `null` | no | + +## Outputs + +No outputs. diff --git a/modules/gcp/network/README.md b/modules/gcp/network/README.md index c27fc06..278c863 100644 --- a/modules/gcp/network/README.md +++ b/modules/gcp/network/README.md @@ -3,4 +3,75 @@ VPC, subnet, router, NAT, peering, and Shared-VPC binding for the Databricks GCP composer. +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5 | +| [google](#requirement\_google) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [google](#provider\_google) | 6.46.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [google_compute_network.hub_vpc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network) | resource | +| [google_compute_network.spoke_vpc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network) | resource | +| [google_compute_network_peering.hub_to_spoke](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network_peering) | resource | +| [google_compute_network_peering.spoke_to_hub](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network_peering) | resource | +| [google_compute_router.router](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_router) | resource | +| [google_compute_router_nat.nat](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_router_nat) | resource | +| [google_compute_shared_vpc_host_project.host](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_shared_vpc_host_project) | resource | +| [google_compute_shared_vpc_service_project.service](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_shared_vpc_service_project) | resource | +| [google_compute_subnetwork.hub_subnet](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork) | resource | +| [google_compute_subnetwork.spoke_subnet](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork) | resource | +| [google_compute_network.existing_spoke](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_network) | data source | +| [google_compute_subnetwork.existing_spoke_subnet](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_subnetwork) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [google\_region](#input\_google\_region) | GCP region for all network resources | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix for generated resource names | `string` | n/a | yes | +| [spoke\_vpc\_google\_project](#input\_spoke\_vpc\_google\_project) | GCP project hosting the spoke VPC | `string` | n/a | yes | +| [suffix](#input\_suffix) | Random suffix passed by the composer for uniqueness | `string` | n/a | yes | +| [vpc\_source](#input\_vpc\_source) | Either 'create' (Terraform creates a VPC) or 'existing' (data-source lookup) | `string` | n/a | yes | +| [create\_hub](#input\_create\_hub) | Create a hub VPC + subnet + peering with the spoke. Composer passes restricted\_egress here. | `bool` | `false` | no | +| [existing\_subnet\_name](#input\_existing\_subnet\_name) | Name of pre-existing subnet (required when vpc\_source=existing) | `string` | `null` | no | +| [existing\_vpc\_name](#input\_existing\_vpc\_name) | Name of pre-existing VPC (required when vpc\_source=existing) | `string` | `null` | no | +| [hub\_vpc\_cidr](#input\_hub\_vpc\_cidr) | CIDR for the hub subnet (required when create\_hub=true) | `string` | `null` | no | +| [hub\_vpc\_google\_project](#input\_hub\_vpc\_google\_project) | GCP project hosting the hub VPC (required when create\_hub=true) | `string` | `null` | no | +| [is\_spoke\_vpc\_shared](#input\_is\_spoke\_vpc\_shared) | If true, bind the spoke VPC's project as a Shared-VPC host and the workspace project as a service project | `bool` | `false` | no | +| [pod\_cidr](#input\_pod\_cidr) | GKE secondary range for pods (optional) | `string` | `null` | no | +| [spoke\_vpc\_cidr](#input\_spoke\_vpc\_cidr) | CIDR for the spoke subnet primary range (required when vpc\_source=create) | `string` | `null` | no | +| [subnet\_cidr](#input\_subnet\_cidr) | CIDR for the spoke subnet (required when vpc\_source=create) | `string` | `null` | no | +| [subnet\_name](#input\_subnet\_name) | Override for spoke subnet name (default: "{prefix}-subnet-{suffix}") | `string` | `null` | no | +| [svc\_cidr](#input\_svc\_cidr) | GKE secondary range for services (optional) | `string` | `null` | no | +| [workspace\_google\_project](#input\_workspace\_google\_project) | Workspace project (used for Shared-VPC service binding) | `string` | `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [hub\_subnet\_name](#output\_hub\_subnet\_name) | Name of the hub subnet (null when create\_hub=false) | +| [hub\_vpc\_id](#output\_hub\_vpc\_id) | ID of the hub VPC (null when create\_hub=false) | +| [hub\_vpc\_name](#output\_hub\_vpc\_name) | Name of the hub VPC (null when create\_hub=false) | +| [hub\_vpc\_self\_link](#output\_hub\_vpc\_self\_link) | Self-link of the hub VPC (null when create\_hub=false) | +| [nat\_id](#output\_nat\_id) | ID of the Cloud NAT (null when vpc\_source=existing) | +| [spoke\_subnet\_id](#output\_spoke\_subnet\_id) | ID of the spoke subnet | +| [spoke\_subnet\_name](#output\_spoke\_subnet\_name) | Name of the spoke subnet | +| [spoke\_subnet\_self\_link](#output\_spoke\_subnet\_self\_link) | Self-link of the spoke subnet | +| [spoke\_vpc\_id](#output\_spoke\_vpc\_id) | ID of the spoke VPC | +| [spoke\_vpc\_name](#output\_spoke\_vpc\_name) | Name of the spoke VPC | +| [spoke\_vpc\_self\_link](#output\_spoke\_vpc\_self\_link) | Self-link of the spoke VPC | diff --git a/modules/gcp/private-connectivity/README.md b/modules/gcp/private-connectivity/README.md index 0c37c8f..bbe06d3 100644 --- a/modules/gcp/private-connectivity/README.md +++ b/modules/gcp/private-connectivity/README.md @@ -3,4 +3,71 @@ GCP-side PSC endpoints + restricted-egress firewall for the Databricks GCP composer. +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5 | +| [google](#requirement\_google) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [google](#provider\_google) | 7.31.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [google_compute_address.backend_address](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_address) | resource | +| [google_compute_address.frontend_address_hub](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_address) | resource | +| [google_compute_address.frontend_address_spoke](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_address) | resource | +| [google_compute_firewall.hub_ingress](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | +| [google_compute_firewall.spoke_allow_ctl_plane](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | +| [google_compute_firewall.spoke_allow_google_apis](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | +| [google_compute_firewall.spoke_allow_hive](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | +| [google_compute_firewall.spoke_default_deny_egress](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | +| [google_compute_forwarding_rule.backend_fr](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_forwarding_rule) | resource | +| [google_compute_forwarding_rule.frontend_fr_hub](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_forwarding_rule) | resource | +| [google_compute_forwarding_rule.frontend_fr_spoke](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_forwarding_rule) | resource | +| [google_compute_subnetwork.psc_subnet](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [google\_region](#input\_google\_region) | n/a | `string` | n/a | yes | +| [prefix](#input\_prefix) | n/a | `string` | n/a | yes | +| [psc\_subnet\_cidr](#input\_psc\_subnet\_cidr) | CIDR for the dedicated PSC subnet in the spoke VPC | `string` | n/a | yes | +| [spoke\_vpc\_cidr](#input\_spoke\_vpc\_cidr) | n/a | `string` | n/a | yes | +| [spoke\_vpc\_google\_project](#input\_spoke\_vpc\_google\_project) | n/a | `string` | n/a | yes | +| [spoke\_vpc\_id](#input\_spoke\_vpc\_id) | Spoke network refs | `string` | n/a | yes | +| [spoke\_vpc\_self\_link](#input\_spoke\_vpc\_self\_link) | n/a | `string` | n/a | yes | +| [suffix](#input\_suffix) | n/a | `string` | n/a | yes | +| [enable\_backend](#input\_enable\_backend) | n/a | `bool` | `false` | no | +| [enable\_frontend](#input\_enable\_frontend) | Feature flags | `bool` | `false` | no | +| [hive\_metastore\_ip](#input\_hive\_metastore\_ip) | Regional Hive metastore IP (looked up via internal map if null) | `string` | `null` | no | +| [hub\_subnet\_name](#input\_hub\_subnet\_name) | n/a | `string` | `null` | no | +| [hub\_vpc\_cidr](#input\_hub\_vpc\_cidr) | n/a | `string` | `null` | no | +| [hub\_vpc\_google\_project](#input\_hub\_vpc\_google\_project) | n/a | `string` | `null` | no | +| [hub\_vpc\_id](#input\_hub\_vpc\_id) | Hub network refs (nullable when no hub) | `string` | `null` | no | +| [hub\_vpc\_self\_link](#input\_hub\_vpc\_self\_link) | n/a | `string` | `null` | no | +| [restrict\_egress](#input\_restrict\_egress) | n/a | `bool` | `false` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [backend\_psc\_fr\_id](#output\_backend\_psc\_fr\_id) | Name of the backend (SCC) PSC forwarding rule (null when enable\_backend=false) | +| [backend\_psc\_ip\_spoke](#output\_backend\_psc\_ip\_spoke) | IP address of the spoke-side backend PSC endpoint | +| [frontend\_psc\_fr\_id](#output\_frontend\_psc\_fr\_id) | Name of the frontend PSC forwarding rule (null when enable\_frontend=false) | +| [frontend\_psc\_ip\_hub](#output\_frontend\_psc\_ip\_hub) | IP address of the hub-side frontend PSC endpoint (null when no hub) | +| [frontend\_psc\_ip\_spoke](#output\_frontend\_psc\_ip\_spoke) | IP address of the spoke-side frontend PSC endpoint | +| [hub\_frontend\_psc\_fr\_id](#output\_hub\_frontend\_psc\_fr\_id) | Name of the hub-side frontend PSC forwarding rule (null when no hub or no frontend) | +| [psc\_subnet\_self\_link](#output\_psc\_subnet\_self\_link) | Self-link of the PSC subnet | diff --git a/modules/gcp/service-account/README.md b/modules/gcp/service-account/README.md index 75b57fc..35adbf9 100644 --- a/modules/gcp/service-account/README.md +++ b/modules/gcp/service-account/README.md @@ -34,7 +34,7 @@ No requirements. | Name | Version | |------|---------| -| [google](#provider\_google) | n/a | +| [google](#provider\_google) | 7.31.0 | ## Modules diff --git a/modules/gcp/unity-catalog/README.md b/modules/gcp/unity-catalog/README.md new file mode 100644 index 0000000..b5bdb04 --- /dev/null +++ b/modules/gcp/unity-catalog/README.md @@ -0,0 +1,50 @@ +# modules/gcp/unity-catalog + +Unity Catalog metastore, GCS bucket, storage credential, external location, and catalog for GCP Databricks workspaces. Called by examples after the workspace exists (uses workspace-scoped Databricks provider alias). + + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [databricks](#provider\_databricks) | 1.114.2 | +| [databricks.workspace](#provider\_databricks.workspace) | 1.114.2 | +| [google](#provider\_google) | 7.31.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [databricks_catalog.main](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/catalog) | resource | +| [databricks_external_location.this](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/external_location) | resource | +| [databricks_metastore.this](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/metastore) | resource | +| [databricks_metastore_assignment.this](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/metastore_assignment) | resource | +| [databricks_storage_credential.this](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/storage_credential) | resource | +| [google_storage_bucket.ext_bucket](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket) | resource | +| [google_storage_bucket_iam_member.unity_cred_admin](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_iam_member) | resource | +| [google_storage_bucket_iam_member.unity_cred_reader](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_iam_member) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [catalog\_name](#input\_catalog\_name) | Name to assign to default catalog | `string` | n/a | yes | +| [databricks\_workspace\_id](#input\_databricks\_workspace\_id) | The unique identifier of the Databricks workspace in which resources will be managed. | `any` | n/a | yes | +| [databricks\_workspace\_url](#input\_databricks\_workspace\_url) | The URL of the Databricks workspace to which resources will be deployed (e.g., https://.gcp.databricks.com). | `any` | n/a | yes | +| [google\_project](#input\_google\_project) | The Google Cloud project ID where the Databricks workspace and associated resources will be created. | `string` | n/a | yes | +| [google\_region](#input\_google\_region) | Google Cloud region where the resources will be created | `string` | n/a | yes | +| [metastore\_name](#input\_metastore\_name) | Name to assign to regional metastore | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix to use in generated resources name | `string` | n/a | yes | + +## Outputs + +No outputs. + From 9a07265d1064b047ccef1aa89c38f3a2ea218885 Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Fri, 22 May 2026 13:32:26 +0200 Subject: [PATCH 12/14] refactor(examples/gcp): migrate to new composer + add existing-vpc Migrates all four existing GCP examples to call modules/gcp/databricks-workspace: - gcp-basic: vpc_source="databricks_managed" - gcp-byovpc: vpc_source="create" with spoke_vpc_cidr + subnet_cidr - gcp-with-psc-exfiltration-protection: vpc_source="create" + all 4 PrivateLink/egress flags + Unity Catalog wired via modules/gcp/unity-catalog - gcp-sa-provisioning: source repointed from github URL to ../../modules/gcp/service-account Adds a NEW example examples/gcp-existing-vpc demonstrating vpc_source="existing" (data-source lookup of a pre-existing VPC + subnet). Variable name changes documented in each example's README migration table: subnet_ip_cidr_range -> subnet_cidr, pod/svc renames, subnet/router/nat_name dropped (composer derives), delegate_from dropped (SA-provisioning concern). State from old applies does NOT migrate cleanly because resource addresses differ. Re-apply on clean state. Co-authored-by: Isaac --- examples/gcp-basic/README.md | 51 ++++++------ examples/gcp-basic/main.tf | 10 ++- examples/gcp-basic/outputs.tf | 12 +-- examples/gcp-basic/terraform.tfvars | 7 ++ examples/gcp-basic/variables.tf | 18 ++-- examples/gcp-byovpc/README.md | 72 +++++++++------- examples/gcp-byovpc/main.tf | 20 ++--- examples/gcp-byovpc/outputs.tf | 21 +++-- examples/gcp-byovpc/terraform.tfvars | 11 +++ examples/gcp-byovpc/variables.tf | 42 ++++------ examples/gcp-existing-vpc/Makefile | 7 ++ examples/gcp-existing-vpc/README.md | 74 +++++++++++++++++ examples/gcp-existing-vpc/init.tf | 22 +++++ examples/gcp-existing-vpc/main.tf | 13 +++ examples/gcp-existing-vpc/outputs.tf | 14 ++++ examples/gcp-existing-vpc/terraform.tfvars | 9 ++ examples/gcp-existing-vpc/variables.tf | 44 ++++++++++ examples/gcp-sa-provisioning/main.tf | 2 +- .../README.md | 83 ++++++++++--------- .../main.tf | 30 ++++--- .../outputs.tf | 20 ++++- .../providers.tf | 2 +- .../terraform.tfvars | 2 + .../unity-catalog.tf | 11 +-- .../variables.tf | 31 ++++--- 25 files changed, 442 insertions(+), 186 deletions(-) create mode 100644 examples/gcp-basic/terraform.tfvars create mode 100644 examples/gcp-byovpc/terraform.tfvars create mode 100644 examples/gcp-existing-vpc/Makefile create mode 100644 examples/gcp-existing-vpc/README.md create mode 100644 examples/gcp-existing-vpc/init.tf create mode 100644 examples/gcp-existing-vpc/main.tf create mode 100644 examples/gcp-existing-vpc/outputs.tf create mode 100644 examples/gcp-existing-vpc/terraform.tfvars create mode 100644 examples/gcp-existing-vpc/variables.tf diff --git a/examples/gcp-basic/README.md b/examples/gcp-basic/README.md index 894b51e..b6df455 100644 --- a/examples/gcp-basic/README.md +++ b/examples/gcp-basic/README.md @@ -1,25 +1,27 @@ -# Provisioning Databricks workspace on GCP with managed VPC -========================= +# examples/gcp-basic — Databricks-managed VPC -In this template, we show how to deploy a workspace with managed VPC. +Calls `modules/gcp/databricks-workspace` with `vpc_source = "databricks_managed"`. +The Databricks platform provisions the workspace VPC; you provide only the GCP +project, region, and prefix. +## Prerequisites -## Requirements - -- You need to have run gcp-sa-provisionning and have a service account to fill in the variables. -- If you want to deploy to a new project, you will need to grant the custom role generated in that template to the service acount in the new project. -- The Service Account needs to be added as Databricks Admin in the account console - -## Run as an SA +- A GCP project with the Databricks platform onboarded +- A service account with workspace-creator role (see `examples/gcp-sa-provisioning`) +- Databricks account ID -You can do the same thing by provisionning a service account that will have the same permissions - and associate the key associated to it. +## Apply +```bash +terraform init +terraform apply +``` -## Run the tempalte +## Migrating from the old example -- You need to fill in the `variables.tf` -- run `terraform init` -- run `teraform apply` +This example previously called `modules/gcp-workspace-basic`. State from the +old apply does **not** migrate cleanly to the new composer because the +`databricks_mws_workspaces` resource address differs. Re-apply on clean state. ## Requirements @@ -34,7 +36,7 @@ No providers. | Name | Source | Version | |------|--------|---------| -| [gcp-basic](#module\_gcp-basic) | github.com/databricks/terraform-databricks-examples/modules/gcp-workspace-basic | n/a | +| [workspace](#module\_workspace) | ../../modules/gcp/databricks-workspace | n/a | ## Resources @@ -45,18 +47,17 @@ No resources. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [databricks\_account\_id](#input\_databricks\_account\_id) | Databricks Account ID | `string` | n/a | yes | -| [databricks\_google\_service\_account](#input\_databricks\_google\_service\_account) | Email of the service account used for deployment | `string` | n/a | yes | -| [delegate\_from](#input\_delegate\_from) | Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com) | `list(string)` | n/a | yes | -| [google\_project](#input\_google\_project) | Google project for VCP/workspace deployment | `string` | n/a | yes | -| [google\_region](#input\_google\_region) | Google region for VCP/workspace deployment | `string` | n/a | yes | -| [google\_zone](#input\_google\_zone) | Zone in GCP region | `string` | n/a | yes | -| [prefix](#input\_prefix) | Prefix to use in generated VPC name | `string` | n/a | yes | -| [workspace\_name](#input\_workspace\_name) | Name of the workspace to create | `string` | n/a | yes | +| [databricks\_google\_service\_account](#input\_databricks\_google\_service\_account) | Service account email used for Databricks provider authentication | `string` | n/a | yes | +| [google\_project](#input\_google\_project) | GCP project where the workspace will be created | `string` | n/a | yes | +| [google\_region](#input\_google\_region) | GCP region for workspace deployment | `string` | n/a | yes | +| [google\_zone](#input\_google\_zone) | GCP zone (used by the google provider) | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix used to name generated resources | `string` | n/a | yes | +| [workspace\_name](#input\_workspace\_name) | Workspace name | `string` | n/a | yes | ## Outputs | Name | Description | |------|-------------| -| [databricks\_host](#output\_databricks\_host) | n/a | -| [databricks\_token](#output\_databricks\_token) | n/a | +| [workspace\_id](#output\_workspace\_id) | Databricks workspace ID | +| [workspace\_url](#output\_workspace\_url) | Databricks workspace URL | diff --git a/examples/gcp-basic/main.tf b/examples/gcp-basic/main.tf index 372bbf9..3265569 100644 --- a/examples/gcp-basic/main.tf +++ b/examples/gcp-basic/main.tf @@ -1,9 +1,11 @@ -module "gcp-basic" { - source = "github.com/databricks/terraform-databricks-examples/modules/gcp-workspace-basic" +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix databricks_account_id = var.databricks_account_id google_project = var.google_project google_region = var.google_region - prefix = var.prefix workspace_name = var.workspace_name - delegate_from = var.delegate_from + + vpc_source = "databricks_managed" } diff --git a/examples/gcp-basic/outputs.tf b/examples/gcp-basic/outputs.tf index d6b170a..81a92ab 100644 --- a/examples/gcp-basic/outputs.tf +++ b/examples/gcp-basic/outputs.tf @@ -1,9 +1,9 @@ - -output "databricks_host" { - value = databricks_mws_workspaces.databricks_workspace.workspace_url +output "workspace_id" { + value = module.workspace.workspace_id + description = "Databricks workspace ID" } -output "databricks_token" { - value = databricks_mws_workspaces.databricks_workspace.token[0].token_value - sensitive = true +output "workspace_url" { + value = module.workspace.workspace_url + description = "Databricks workspace URL" } diff --git a/examples/gcp-basic/terraform.tfvars b/examples/gcp-basic/terraform.tfvars new file mode 100644 index 0000000..8405cca --- /dev/null +++ b/examples/gcp-basic/terraform.tfvars @@ -0,0 +1,7 @@ +databricks_account_id = "" +databricks_google_service_account = "" +google_project = "" +google_region = "" +google_zone = "" +prefix = "" +workspace_name = "" diff --git a/examples/gcp-basic/variables.tf b/examples/gcp-basic/variables.tf index 9805c04..4b02c04 100644 --- a/examples/gcp-basic/variables.tf +++ b/examples/gcp-basic/variables.tf @@ -4,38 +4,32 @@ variable "databricks_account_id" { } variable "databricks_google_service_account" { - description = "Email of the service account used for deployment" type = string + description = "Service account email used for Databricks provider authentication" } variable "google_project" { type = string - description = "Google project for VCP/workspace deployment" + description = "GCP project where the workspace will be created" } variable "google_region" { type = string - description = "Google region for VCP/workspace deployment" + description = "GCP region for workspace deployment" } variable "google_zone" { - description = "Zone in GCP region" type = string + description = "GCP zone (used by the google provider)" } variable "prefix" { type = string - description = "Prefix to use in generated VPC name" + description = "Prefix used to name generated resources" } variable "workspace_name" { - description = "Name of the workspace to create" type = string + description = "Workspace name" } -variable "delegate_from" { - description = "Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com)" - type = list(string) -} - - diff --git a/examples/gcp-byovpc/README.md b/examples/gcp-byovpc/README.md index 8dc13eb..3458782 100644 --- a/examples/gcp-byovpc/README.md +++ b/examples/gcp-byovpc/README.md @@ -1,25 +1,39 @@ -# Provisioning Databricks workspace on GCP with a custom VPC -========================= +# examples/gcp-byovpc — Customer-managed VPC -In this template, we show how to deploy a workspace with a custom vpc. +Calls `modules/gcp/databricks-workspace` with `vpc_source = "create"`. Terraform +creates the spoke VPC + subnet + Cloud Router + NAT, then registers the network +with the Databricks account and provisions a workspace inside it. +## Prerequisites -## Requirements +- A GCP project with the Databricks platform onboarded +- A service account with workspace-creator role (see `examples/gcp-sa-provisioning`) +- Databricks account ID +- CIDR ranges for the spoke VPC and subnet that don't overlap with existing networks -- You need to have run gcp-sa-provisionning and have a service account to fill in the variables. -- If you want to deploy to a new project, you will need to grant the custom role generated in that template to the service acount in the new project. -- The sizing of the custom vpc subnets needs to be appropriate for the usage of the workspace. [This documentation covers it](https://docs.gcp.databricks.com/administration-guide/cloud-configurations/gcp/network-sizing.html) +## Apply -## Run as an SA +```bash +terraform init +terraform apply +``` -You can do the same thing by provisionning a service account that will have the same permissions - and associate the key associated to it. +## Migrating from the old example +This example previously called `modules/gcp-workspace-byovpc`. Several variable +names changed to match the new composer API: -## Run the tempalte +| Old name | New name | +|----------|----------| +| `subnet_ip_cidr_range` | `subnet_cidr` | +| `pod_ip_cidr_range` | `pod_cidr` | +| `svc_ip_cidr_range` | `svc_cidr` | +| `subnet_name`, `router_name`, `nat_name` | (removed — composer derives from `prefix` + random suffix) | +| `delegate_from` | (removed — handled by `examples/gcp-sa-provisioning`) | +| _(new)_ | `spoke_vpc_cidr` (VPC primary CIDR, distinct from subnet CIDR) | -- You need to fill in the variables.tf -- run `terraform init` -- run `teraform apply` +State from the old apply does **not** migrate cleanly to the new composer +because resource addresses differ. Re-apply on clean state. ## Requirements @@ -30,13 +44,13 @@ No requirements. | Name | Version | |------|---------| -| [google](#provider\_google) | 4.63.1 | +| [google](#provider\_google) | 6.46.0 | ## Modules | Name | Source | Version | |------|--------|---------| -| [gcp-byovpc](#module\_gcp-byovpc) | github.com/databricks/terraform-databricks-examples/modules/gcp-workspace-byovpc | n/a | +| [workspace](#module\_workspace) | ../../modules/gcp/databricks-workspace | n/a | ## Resources @@ -50,23 +64,23 @@ No requirements. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [databricks\_account\_id](#input\_databricks\_account\_id) | Databricks Account ID | `string` | n/a | yes | -| [databricks\_google\_service\_account](#input\_databricks\_google\_service\_account) | Email of the service account used for deployment | `string` | n/a | yes | -| [delegate\_from](#input\_delegate\_from) | Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com) | `list(string)` | n/a | yes | -| [google\_project](#input\_google\_project) | Google project for VCP/workspace deployment | `string` | n/a | yes | -| [google\_region](#input\_google\_region) | Google region for VCP/workspace deployment | `string` | n/a | yes | -| [google\_zone](#input\_google\_zone) | Zone in GCP region | `string` | n/a | yes | -| [nat\_name](#input\_nat\_name) | Name of the NAT service in compute router | `string` | n/a | yes | -| [pod\_ip\_cidr\_range](#input\_pod\_ip\_cidr\_range) | IP Range for Pods subnet (secondary) | `string` | n/a | yes | -| [prefix](#input\_prefix) | Prefix to use in generated VPC name | `string` | n/a | yes | -| [router\_name](#input\_router\_name) | Name of the compute router to create | `string` | n/a | yes | -| [subnet\_ip\_cidr\_range](#input\_subnet\_ip\_cidr\_range) | IP Range for Nodes subnet (primary) | `string` | n/a | yes | -| [subnet\_name](#input\_subnet\_name) | Name of the subnet to create | `string` | n/a | yes | -| [svc\_ip\_cidr\_range](#input\_svc\_ip\_cidr\_range) | IP Range for Services subnet (secondary) | `string` | n/a | yes | +| [databricks\_google\_service\_account](#input\_databricks\_google\_service\_account) | Service account email used for Databricks provider authentication | `string` | n/a | yes | +| [google\_project](#input\_google\_project) | GCP project where the workspace VPC and resources will be created | `string` | n/a | yes | +| [google\_region](#input\_google\_region) | GCP region for workspace deployment | `string` | n/a | yes | +| [google\_zone](#input\_google\_zone) | GCP zone (used by the google provider) | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix used to name generated resources | `string` | n/a | yes | +| [spoke\_vpc\_cidr](#input\_spoke\_vpc\_cidr) | CIDR for the spoke VPC (e.g. 10.0.0.0/16) | `string` | n/a | yes | +| [subnet\_cidr](#input\_subnet\_cidr) | CIDR for the GKE nodes subnet primary range (e.g. 10.0.0.0/22) | `string` | n/a | yes | +| [workspace\_name](#input\_workspace\_name) | Workspace name | `string` | n/a | yes | +| [pod\_cidr](#input\_pod\_cidr) | Optional secondary range for GKE pods | `string` | `null` | no | +| [svc\_cidr](#input\_svc\_cidr) | Optional secondary range for GKE services | `string` | `null` | no | ## Outputs | Name | Description | |------|-------------| -| [databricks\_host](#output\_databricks\_host) | n/a | -| [databricks\_token](#output\_databricks\_token) | n/a | +| [network\_id](#output\_network\_id) | databricks\_mws\_networks ID | +| [vpc\_id](#output\_vpc\_id) | ID of the spoke VPC created by the module | +| [workspace\_id](#output\_workspace\_id) | Databricks workspace ID | +| [workspace\_url](#output\_workspace\_url) | Databricks workspace URL | diff --git a/examples/gcp-byovpc/main.tf b/examples/gcp-byovpc/main.tf index c1e82a0..5c9d7ec 100644 --- a/examples/gcp-byovpc/main.tf +++ b/examples/gcp-byovpc/main.tf @@ -1,15 +1,15 @@ -module "gcp-byovpc" { - source = "github.com/databricks/terraform-databricks-examples/modules/gcp-workspace-byovpc" +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix databricks_account_id = var.databricks_account_id google_project = var.google_project google_region = var.google_region - prefix = var.prefix - subnet_ip_cidr_range = var.subnet_ip_cidr_range - pod_ip_cidr_range = var.pod_ip_cidr_range - svc_ip_cidr_range = var.svc_ip_cidr_range - subnet_name = var.subnet_name - router_name = var.router_name - nat_name = var.nat_name workspace_name = var.workspace_name - delegate_from = var.delegate_from + + vpc_source = "create" + spoke_vpc_cidr = var.spoke_vpc_cidr + subnet_cidr = var.subnet_cidr + pod_cidr = var.pod_cidr + svc_cidr = var.svc_cidr } diff --git a/examples/gcp-byovpc/outputs.tf b/examples/gcp-byovpc/outputs.tf index f544b3b..3df898c 100644 --- a/examples/gcp-byovpc/outputs.tf +++ b/examples/gcp-byovpc/outputs.tf @@ -1,8 +1,19 @@ -output "databricks_host" { - value = databricks_mws_workspaces.databricks_workspace.workspace_url +output "workspace_id" { + value = module.workspace.workspace_id + description = "Databricks workspace ID" } -output "databricks_token" { - value = databricks_mws_workspaces.databricks_workspace.token[0].token_value - sensitive = true +output "workspace_url" { + value = module.workspace.workspace_url + description = "Databricks workspace URL" +} + +output "vpc_id" { + value = module.workspace.vpc_id + description = "ID of the spoke VPC created by the module" +} + +output "network_id" { + value = module.workspace.network_id + description = "databricks_mws_networks ID" } \ No newline at end of file diff --git a/examples/gcp-byovpc/terraform.tfvars b/examples/gcp-byovpc/terraform.tfvars new file mode 100644 index 0000000..6029b38 --- /dev/null +++ b/examples/gcp-byovpc/terraform.tfvars @@ -0,0 +1,11 @@ +databricks_account_id = "" +databricks_google_service_account = "" +google_project = "" +google_region = "" +google_zone = "" +prefix = "" +workspace_name = "" +spoke_vpc_cidr = "" +subnet_cidr = "" +pod_cidr = null +svc_cidr = null diff --git a/examples/gcp-byovpc/variables.tf b/examples/gcp-byovpc/variables.tf index e1c91f2..f9d9aef 100644 --- a/examples/gcp-byovpc/variables.tf +++ b/examples/gcp-byovpc/variables.tf @@ -4,61 +4,53 @@ variable "databricks_account_id" { } variable "databricks_google_service_account" { - description = "Email of the service account used for deployment" type = string + description = "Service account email used for Databricks provider authentication" } variable "google_project" { type = string - description = "Google project for VCP/workspace deployment" + description = "GCP project where the workspace VPC and resources will be created" } variable "google_region" { type = string - description = "Google region for VCP/workspace deployment" + description = "GCP region for workspace deployment" } variable "google_zone" { - description = "Zone in GCP region" type = string + description = "GCP zone (used by the google provider)" } variable "prefix" { type = string - description = "Prefix to use in generated VPC name" + description = "Prefix used to name generated resources" } -variable "subnet_ip_cidr_range" { +variable "workspace_name" { type = string - description = "IP Range for Nodes subnet (primary)" + description = "Workspace name" } -variable "pod_ip_cidr_range" { +variable "spoke_vpc_cidr" { type = string - description = "IP Range for Pods subnet (secondary)" + description = "CIDR for the spoke VPC (e.g. 10.0.0.0/16)" } -variable "svc_ip_cidr_range" { +variable "subnet_cidr" { type = string - description = "IP Range for Services subnet (secondary)" + description = "CIDR for the GKE nodes subnet primary range (e.g. 10.0.0.0/22)" } -variable "subnet_name" { +variable "pod_cidr" { type = string - description = "Name of the subnet to create" + default = null + description = "Optional secondary range for GKE pods" } -variable "router_name" { +variable "svc_cidr" { type = string - description = "Name of the compute router to create" -} - -variable "nat_name" { - type = string - description = "Name of the NAT service in compute router" -} - -variable "delegate_from" { - description = "Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com)" - type = list(string) + default = null + description = "Optional secondary range for GKE services" } diff --git a/examples/gcp-existing-vpc/Makefile b/examples/gcp-existing-vpc/Makefile new file mode 100644 index 0000000..653039d --- /dev/null +++ b/examples/gcp-existing-vpc/Makefile @@ -0,0 +1,7 @@ +.PHONY: docs test_docs + +docs: + terraform-docs -c ../../.terraform-docs.yml . + +test_docs: + terraform-docs -c ../../.terraform-docs.yml --output-check . diff --git a/examples/gcp-existing-vpc/README.md b/examples/gcp-existing-vpc/README.md new file mode 100644 index 0000000..48affa7 --- /dev/null +++ b/examples/gcp-existing-vpc/README.md @@ -0,0 +1,74 @@ +# examples/gcp-existing-vpc — Use a pre-existing VPC + +Calls `modules/gcp/databricks-workspace` with `vpc_source = "existing"`. Instead +of creating a VPC, the composer looks up the named VPC + subnet via Terraform +data sources and registers them with the Databricks account. + +This is the scenario for organizations that manage GCP networking out-of-band +(e.g. via a platform team) and just want Databricks to consume an existing +network. + +## Prerequisites + +- A GCP project with the Databricks platform onboarded +- A pre-existing VPC and subnet in that project. The subnet must be in `google_region`. +- A service account with workspace-creator role (see `examples/gcp-sa-provisioning`) +- Databricks account ID + +## Apply + +```bash +terraform init +terraform apply +``` + +## What the composer does NOT do in this mode + +- Does not create the VPC, subnet, router, or NAT — those must already exist +- Does not enforce that the subnet has Private Google Access enabled — verify in the console +- Does not configure egress firewalls or PrivateLink (those require `vpc_source = "create"`) + +To layer PrivateLink onto an existing network, the current composer requires +`vpc_source = "create"`. Future work may relax this. + + +## Requirements + +No requirements. + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [workspace](#module\_workspace) | ../../modules/gcp/databricks-workspace | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [databricks\_account\_id](#input\_databricks\_account\_id) | Databricks Account ID | `string` | n/a | yes | +| [databricks\_google\_service\_account](#input\_databricks\_google\_service\_account) | Service account email used for Databricks provider authentication | `string` | n/a | yes | +| [existing\_subnet\_name](#input\_existing\_subnet\_name) | Name of the pre-existing subnet inside the VPC (must be in google\_region) | `string` | n/a | yes | +| [existing\_vpc\_name](#input\_existing\_vpc\_name) | Name of the pre-existing GCP VPC to deploy the workspace into | `string` | n/a | yes | +| [google\_project](#input\_google\_project) | GCP project hosting the existing VPC and subnet (also the workspace project) | `string` | n/a | yes | +| [google\_region](#input\_google\_region) | GCP region for workspace deployment (must match the existing subnet's region) | `string` | n/a | yes | +| [google\_zone](#input\_google\_zone) | GCP zone (used by the google provider) | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix used to name Databricks-side resources (mws\_networks, mws\_workspaces) | `string` | n/a | yes | +| [workspace\_name](#input\_workspace\_name) | Workspace name | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [network\_id](#output\_network\_id) | databricks\_mws\_networks ID | +| [workspace\_id](#output\_workspace\_id) | Databricks workspace ID | +| [workspace\_url](#output\_workspace\_url) | Databricks workspace URL | + diff --git a/examples/gcp-existing-vpc/init.tf b/examples/gcp-existing-vpc/init.tf new file mode 100644 index 0000000..8fc08e7 --- /dev/null +++ b/examples/gcp-existing-vpc/init.tf @@ -0,0 +1,22 @@ +terraform { + required_providers { + databricks = { + source = "databricks/databricks" + } + google = { + source = "hashicorp/google" + } + } +} + +provider "google" { + project = var.google_project + region = var.google_region + zone = var.google_zone +} + +provider "databricks" { + host = "https://accounts.gcp.databricks.com" + google_service_account = var.databricks_google_service_account + account_id = var.databricks_account_id +} diff --git a/examples/gcp-existing-vpc/main.tf b/examples/gcp-existing-vpc/main.tf new file mode 100644 index 0000000..6b4173e --- /dev/null +++ b/examples/gcp-existing-vpc/main.tf @@ -0,0 +1,13 @@ +module "workspace" { + source = "../../modules/gcp/databricks-workspace" + + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.google_project + google_region = var.google_region + workspace_name = var.workspace_name + + vpc_source = "existing" + existing_vpc_name = var.existing_vpc_name + existing_subnet_name = var.existing_subnet_name +} diff --git a/examples/gcp-existing-vpc/outputs.tf b/examples/gcp-existing-vpc/outputs.tf new file mode 100644 index 0000000..469a66e --- /dev/null +++ b/examples/gcp-existing-vpc/outputs.tf @@ -0,0 +1,14 @@ +output "workspace_id" { + value = module.workspace.workspace_id + description = "Databricks workspace ID" +} + +output "workspace_url" { + value = module.workspace.workspace_url + description = "Databricks workspace URL" +} + +output "network_id" { + value = module.workspace.network_id + description = "databricks_mws_networks ID" +} diff --git a/examples/gcp-existing-vpc/terraform.tfvars b/examples/gcp-existing-vpc/terraform.tfvars new file mode 100644 index 0000000..a541640 --- /dev/null +++ b/examples/gcp-existing-vpc/terraform.tfvars @@ -0,0 +1,9 @@ +databricks_account_id = "" +databricks_google_service_account = "" +google_project = "" +google_region = "" +google_zone = "" +prefix = "" +workspace_name = "" +existing_vpc_name = "" +existing_subnet_name = "" diff --git a/examples/gcp-existing-vpc/variables.tf b/examples/gcp-existing-vpc/variables.tf new file mode 100644 index 0000000..f0518c8 --- /dev/null +++ b/examples/gcp-existing-vpc/variables.tf @@ -0,0 +1,44 @@ +variable "databricks_account_id" { + type = string + description = "Databricks Account ID" +} + +variable "databricks_google_service_account" { + type = string + description = "Service account email used for Databricks provider authentication" +} + +variable "google_project" { + type = string + description = "GCP project hosting the existing VPC and subnet (also the workspace project)" +} + +variable "google_region" { + type = string + description = "GCP region for workspace deployment (must match the existing subnet's region)" +} + +variable "google_zone" { + type = string + description = "GCP zone (used by the google provider)" +} + +variable "prefix" { + type = string + description = "Prefix used to name Databricks-side resources (mws_networks, mws_workspaces)" +} + +variable "workspace_name" { + type = string + description = "Workspace name" +} + +variable "existing_vpc_name" { + type = string + description = "Name of the pre-existing GCP VPC to deploy the workspace into" +} + +variable "existing_subnet_name" { + type = string + description = "Name of the pre-existing subnet inside the VPC (must be in google_region)" +} diff --git a/examples/gcp-sa-provisioning/main.tf b/examples/gcp-sa-provisioning/main.tf index 7b59653..109fd49 100644 --- a/examples/gcp-sa-provisioning/main.tf +++ b/examples/gcp-sa-provisioning/main.tf @@ -1,5 +1,5 @@ module "gcp-sa-provisioning" { - source = "github.com/databricks/terraform-databricks-examples/modules/gcp-sa-provisioning" + source = "../../modules/gcp/service-account" google_project = var.google_project prefix = var.prefix delegate_from = var.delegate_from diff --git a/examples/gcp-with-psc-exfiltration-protection/README.md b/examples/gcp-with-psc-exfiltration-protection/README.md index 64d676a..265c560 100644 --- a/examples/gcp-with-psc-exfiltration-protection/README.md +++ b/examples/gcp-with-psc-exfiltration-protection/README.md @@ -1,37 +1,42 @@ -# Provisioning Databricks on GCP workspace with a Hub & Spoke network architecture for data exfiltration protection +# examples/gcp-with-psc-exfiltration-protection — Workspace with PSC + private DNS + restricted egress -This example is using the [gcp-with-psc-exfiltration-protection](../../modules/gcp-with-psc-exfiltration-protection) module. +Calls `modules/gcp/databricks-workspace` with all PrivateLink and egress-control flags enabled: -This template provides an example deployment of: Hub-Spoke networking with egress firewall to control all outbound traffic from Databricks subnets. +- `vpc_source = "create"` — composer creates the spoke VPC + hub VPC + peering +- `private_link_frontend = true` — frontend PSC endpoint (workspace UI/API) +- `private_link_backend = true` — backend (SCC) PSC endpoint (data plane) +- `private_access_only = true` — `mws_private_access_settings.public_access_enabled = false` +- `restricted_egress = true` — hub VPC + deny-egress firewall + private DNS zones -With this setup, you can setup firewall rules to block / allow egress traffic from your Databricks clusters. You can also use firewall to block all access to storage accounts, and use private endpoint connection to bypass this firewall, such that you allow access only to specific storage accounts. +Optionally pairs with the `modules/gcp/unity-catalog` module to create a metastore, GCS bucket, storage credential, external location, and default catalog. +## Prerequisites -To find IP and FQDN for your deployment, go to: https://docs.gcp.databricks.com/en/resources/ip-domain-region.html +- Two (or three) GCP projects: workspace project, spoke VPC project, hub VPC project (can be the same) +- Service account with workspace-creator role (see `examples/gcp-sa-provisioning`) +- Databricks account ID +- CIDR ranges that don't overlap: `spoke_vpc_cidr`, `subnet_cidr` (subset of spoke), `hub_vpc_cidr`, `psc_subnet_cidr` +- Regional default Hive Metastore IP from [Databricks docs](https://docs.gcp.databricks.com/en/resources/ip-domain-region.html#addresses-for-default-metastore) -## Overall Architecture +## Apply -![alt text](../../modules/gcp-with-psc-exfiltration-protection/images/architecture.png) +```bash +terraform init +terraform apply +``` -Resources to be created: -* Hub VPC and its subnet -* Spoke VPC and its subnets -* Peering between Hub and Spoke VPC -* Private Service Connect (PSC) endpoints -* DNS private and peering zones -* Firewall rules for Hub and Spoke VPCs -* Databricks workspace with private link to control plane, user to webapp and private link to DBFS +## Migrating from the old example -## How to use +This example previously called `modules/gcp-with-psc-exfiltration-protection` and `modules/gcp-unity-catalog`. Key changes: -1. Reference this module using one of the different [module source types](https://developer.hashicorp.com/terraform/language/modules/sources) -2. Add `terraform.tfvars` with the information about service principals to be provisioned at account level. +| Old | New | +|-----|-----| +| `module.gcp_with_data_exfiltration_protection` | `module.workspace` | +| `modules/gcp-with-psc-exfiltration-protection` | `modules/gcp/databricks-workspace` with `vpc_source=create` + 4 PSC/egress flags | +| `modules/gcp-unity-catalog` | `modules/gcp/unity-catalog` (relocated, same interface) | +| `spoke_vpc_cidr` (legacy: was used as subnet CIDR AND firewall source ranges) | Split into `subnet_cidr` (subnet CIDR) and `spoke_vpc_cidr` (broader VPC CIDR for firewall source) | -## How to fill in variable values - -Variables have no default values in order to avoid misconfiguration - -Most values are related to resources managed by Databricks. The required values can be found at: https://docs.gcp.databricks.com/en/resources/ip-domain-region.html +State from the old apply does **not** migrate cleanly to the new composer because resource addresses differ. Re-apply on clean state. ## Requirements @@ -49,8 +54,8 @@ No providers. | Name | Source | Version | |------|--------|---------| -| [gcp\_with\_data\_exfiltration\_protection](#module\_gcp\_with\_data\_exfiltration\_protection) | ../../modules/gcp-with-psc-exfiltration-protection | n/a | -| [unity\_catalog](#module\_unity\_catalog) | ../../modules/gcp-unity-catalog | n/a | +| [unity\_catalog](#module\_unity\_catalog) | ../../modules/gcp/unity-catalog | n/a | +| [workspace](#module\_workspace) | ../../modules/gcp/databricks-workspace | n/a | ## Resources @@ -60,25 +65,29 @@ No resources. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [catalog\_name](#input\_catalog\_name) | Name to assign to default catalog | `string` | n/a | yes | +| [catalog\_name](#input\_catalog\_name) | Name to assign to default Unity Catalog catalog | `string` | n/a | yes | | [databricks\_account\_id](#input\_databricks\_account\_id) | Databricks Account ID | `string` | n/a | yes | | [google\_region](#input\_google\_region) | Google Cloud region where the resources will be created | `string` | n/a | yes | -| [hive\_metastore\_ip](#input\_hive\_metastore\_ip) | Value of regional default Hive Metastore IP | `string` | n/a | yes | -| [hub\_vpc\_cidr](#input\_hub\_vpc\_cidr) | CIDR for Hub VPC | `string` | n/a | yes | -| [hub\_vpc\_google\_project](#input\_hub\_vpc\_google\_project) | Google Cloud project ID related to Hub VPC | `string` | n/a | yes | -| [is\_spoke\_vpc\_shared](#input\_is\_spoke\_vpc\_shared) | Whether the Spoke VPC is a Shared or a dedicated VPC | `bool` | n/a | yes | -| [metastore\_name](#input\_metastore\_name) | Name to assign to regional metastore | `string` | n/a | yes | -| [prefix](#input\_prefix) | Prefix to use in generated resources name | `string` | n/a | yes | -| [psc\_subnet\_cidr](#input\_psc\_subnet\_cidr) | CIDR for Spoke VPC | `string` | n/a | yes | -| [spoke\_vpc\_cidr](#input\_spoke\_vpc\_cidr) | CIDR for Spoke VPC | `string` | n/a | yes | -| [spoke\_vpc\_google\_project](#input\_spoke\_vpc\_google\_project) | Google Cloud project ID related to Spoke VPC | `string` | n/a | yes | -| [workspace\_google\_project](#input\_workspace\_google\_project) | Google Cloud project ID related to Databricks workspace | `string` | n/a | yes | -| [tags](#input\_tags) | Map of tags to add to all resources | `map(string)` | `{}` | no | +| [hive\_metastore\_ip](#input\_hive\_metastore\_ip) | Regional default Hive Metastore IP (used by the spoke egress firewall to allow MySQL/3306) | `string` | n/a | yes | +| [hub\_vpc\_cidr](#input\_hub\_vpc\_cidr) | CIDR for the hub subnet | `string` | n/a | yes | +| [hub\_vpc\_google\_project](#input\_hub\_vpc\_google\_project) | Google Cloud project ID hosting the hub VPC | `string` | n/a | yes | +| [is\_spoke\_vpc\_shared](#input\_is\_spoke\_vpc\_shared) | Whether the spoke VPC project hosts a Shared VPC and the workspace project is bound as a service project | `bool` | n/a | yes | +| [metastore\_name](#input\_metastore\_name) | Name to assign to regional Unity Catalog metastore | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix used to name generated resources | `string` | n/a | yes | +| [psc\_subnet\_cidr](#input\_psc\_subnet\_cidr) | CIDR for the dedicated PSC subnet in the spoke VPC | `string` | n/a | yes | +| [spoke\_vpc\_cidr](#input\_spoke\_vpc\_cidr) | CIDR of the spoke VPC address space (used as source\_ranges for the hub ingress firewall) | `string` | n/a | yes | +| [spoke\_vpc\_google\_project](#input\_spoke\_vpc\_google\_project) | Google Cloud project ID hosting the spoke VPC (often the same as workspace project) | `string` | n/a | yes | +| [subnet\_cidr](#input\_subnet\_cidr) | CIDR for the spoke subnet (must be within spoke\_vpc\_cidr) | `string` | n/a | yes | +| [workspace\_google\_project](#input\_workspace\_google\_project) | Google Cloud project ID where the Databricks workspace lives | `string` | n/a | yes | +| [tags](#input\_tags) | Map of tags applied to the composer (the composer accepts this but does not currently propagate to all submodules) | `map(string)` | `{}` | no | ## Outputs | Name | Description | |------|-------------| +| [hub\_vpc\_id](#output\_hub\_vpc\_id) | ID of the hub VPC | +| [network\_id](#output\_network\_id) | databricks\_mws\_networks ID | +| [vpc\_id](#output\_vpc\_id) | ID of the spoke VPC | | [workspace\_id](#output\_workspace\_id) | The Databricks workspace ID | | [workspace\_url](#output\_workspace\_url) | The workspace URL which is of the format '{workspaceId}.{random}.gcp.databricks.com' | diff --git a/examples/gcp-with-psc-exfiltration-protection/main.tf b/examples/gcp-with-psc-exfiltration-protection/main.tf index c0b7fa9..54eff6a 100644 --- a/examples/gcp-with-psc-exfiltration-protection/main.tf +++ b/examples/gcp-with-psc-exfiltration-protection/main.tf @@ -1,16 +1,26 @@ -module "gcp_with_data_exfiltration_protection" { - source = "../../modules/gcp-with-psc-exfiltration-protection" +module "workspace" { + source = "../../modules/gcp/databricks-workspace" - databricks_account_id = var.databricks_account_id + prefix = var.prefix + databricks_account_id = var.databricks_account_id + google_project = var.workspace_google_project + google_region = var.google_region + + vpc_source = "create" + spoke_vpc_cidr = var.spoke_vpc_cidr + subnet_cidr = var.subnet_cidr + + private_link_frontend = true + private_link_backend = true + private_access_only = true + restricted_egress = true + + spoke_vpc_google_project = var.spoke_vpc_google_project hub_vpc_google_project = var.hub_vpc_google_project is_spoke_vpc_shared = var.is_spoke_vpc_shared - prefix = var.prefix - spoke_vpc_google_project = var.spoke_vpc_google_project - workspace_google_project = var.workspace_google_project - google_region = var.google_region - hive_metastore_ip = var.hive_metastore_ip hub_vpc_cidr = var.hub_vpc_cidr psc_subnet_cidr = var.psc_subnet_cidr - spoke_vpc_cidr = var.spoke_vpc_cidr - tags = var.tags + hive_metastore_ip = var.hive_metastore_ip + + tags = var.tags } \ No newline at end of file diff --git a/examples/gcp-with-psc-exfiltration-protection/outputs.tf b/examples/gcp-with-psc-exfiltration-protection/outputs.tf index 681fe5d..0fae6ec 100644 --- a/examples/gcp-with-psc-exfiltration-protection/outputs.tf +++ b/examples/gcp-with-psc-exfiltration-protection/outputs.tf @@ -1,10 +1,24 @@ - output "workspace_url" { - value = module.gcp_with_data_exfiltration_protection.workspace_url + value = module.workspace.workspace_url description = "The workspace URL which is of the format '{workspaceId}.{random}.gcp.databricks.com'" } output "workspace_id" { + value = module.workspace.workspace_id description = "The Databricks workspace ID" - value = module.gcp_with_data_exfiltration_protection.workspace_id +} + +output "vpc_id" { + value = module.workspace.vpc_id + description = "ID of the spoke VPC" +} + +output "hub_vpc_id" { + value = module.workspace.hub_vpc_id + description = "ID of the hub VPC" +} + +output "network_id" { + value = module.workspace.network_id + description = "databricks_mws_networks ID" } \ No newline at end of file diff --git a/examples/gcp-with-psc-exfiltration-protection/providers.tf b/examples/gcp-with-psc-exfiltration-protection/providers.tf index 489bf1e..f2881ff 100644 --- a/examples/gcp-with-psc-exfiltration-protection/providers.tf +++ b/examples/gcp-with-psc-exfiltration-protection/providers.tf @@ -6,7 +6,7 @@ provider "databricks" { provider "databricks" { alias = "workspace" - host = module.gcp_with_data_exfiltration_protection.workspace_url + host = module.workspace.workspace_url } provider "google" { diff --git a/examples/gcp-with-psc-exfiltration-protection/terraform.tfvars b/examples/gcp-with-psc-exfiltration-protection/terraform.tfvars index 8f09572..c9a603e 100644 --- a/examples/gcp-with-psc-exfiltration-protection/terraform.tfvars +++ b/examples/gcp-with-psc-exfiltration-protection/terraform.tfvars @@ -13,8 +13,10 @@ prefix = "" hive_metastore_ip = "" hub_vpc_cidr = "" spoke_vpc_cidr = "" +subnet_cidr = "" psc_subnet_cidr = "" metastore_name = "" catalog_name = "" +tags = {} diff --git a/examples/gcp-with-psc-exfiltration-protection/unity-catalog.tf b/examples/gcp-with-psc-exfiltration-protection/unity-catalog.tf index c6c0628..792862d 100644 --- a/examples/gcp-with-psc-exfiltration-protection/unity-catalog.tf +++ b/examples/gcp-with-psc-exfiltration-protection/unity-catalog.tf @@ -1,15 +1,16 @@ module "unity_catalog" { - source = "../../modules/gcp-unity-catalog" + source = "../../modules/gcp/unity-catalog" providers = { - databricks = databricks, + databricks = databricks databricks.workspace = databricks.workspace } - databricks_workspace_id = module.gcp_with_data_exfiltration_protection.workspace_id - databricks_workspace_url = module.gcp_with_data_exfiltration_protection.workspace_url + + databricks_workspace_id = module.workspace.workspace_id + databricks_workspace_url = module.workspace.workspace_url google_project = var.workspace_google_project google_region = var.google_region + prefix = var.prefix metastore_name = var.metastore_name catalog_name = var.catalog_name - prefix = var.prefix } \ No newline at end of file diff --git a/examples/gcp-with-psc-exfiltration-protection/variables.tf b/examples/gcp-with-psc-exfiltration-protection/variables.tf index 15365cc..4857831 100644 --- a/examples/gcp-with-psc-exfiltration-protection/variables.tf +++ b/examples/gcp-with-psc-exfiltration-protection/variables.tf @@ -10,64 +10,69 @@ variable "google_region" { variable "workspace_google_project" { type = string - description = "Google Cloud project ID related to Databricks workspace" + description = "Google Cloud project ID where the Databricks workspace lives" } variable "spoke_vpc_google_project" { type = string - description = "Google Cloud project ID related to Spoke VPC" + description = "Google Cloud project ID hosting the spoke VPC (often the same as workspace project)" } variable "hub_vpc_google_project" { type = string - description = "Google Cloud project ID related to Hub VPC" + description = "Google Cloud project ID hosting the hub VPC" } variable "is_spoke_vpc_shared" { type = bool - description = "Whether the Spoke VPC is a Shared or a dedicated VPC" + description = "Whether the spoke VPC project hosts a Shared VPC and the workspace project is bound as a service project" } variable "prefix" { type = string - description = "Prefix to use in generated resources name" + description = "Prefix used to name generated resources" } # For the value of the regional Hive Metastore IP, refer to the Databricks documentation -# Here - https://docs.gcp.databricks.com/en/resources/ip-domain-region.html#addresses-for-default-metastore +# https://docs.gcp.databricks.com/en/resources/ip-domain-region.html#addresses-for-default-metastore variable "hive_metastore_ip" { type = string - description = "Value of regional default Hive Metastore IP" + description = "Regional default Hive Metastore IP (used by the spoke egress firewall to allow MySQL/3306)" } variable "hub_vpc_cidr" { type = string - description = "CIDR for Hub VPC" + description = "CIDR for the hub subnet" } variable "spoke_vpc_cidr" { type = string - description = "CIDR for Spoke VPC" + description = "CIDR of the spoke VPC address space (used as source_ranges for the hub ingress firewall)" +} + +variable "subnet_cidr" { + type = string + description = "CIDR for the spoke subnet (must be within spoke_vpc_cidr)" } variable "psc_subnet_cidr" { type = string - description = "CIDR for Spoke VPC" + description = "CIDR for the dedicated PSC subnet in the spoke VPC" } variable "tags" { type = map(string) - description = "Map of tags to add to all resources" + description = "Map of tags applied to the composer (the composer accepts this but does not currently propagate to all submodules)" default = {} } variable "metastore_name" { type = string - description = "Name to assign to regional metastore" + description = "Name to assign to regional Unity Catalog metastore" } variable "catalog_name" { type = string - description = "Name to assign to default catalog" + description = "Name to assign to default Unity Catalog catalog" } \ No newline at end of file From 640d4efef6bbef8964a4bf19d868030c5b85e7eb Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Fri, 22 May 2026 13:49:40 +0200 Subject: [PATCH 13/14] chore(gcp): remove deprecated modules and junk directories Deletes: - modules/gcp-workspace-basic, modules/gcp-workspace-byovpc, modules/gcp-with-psc-exfiltration-protection (replaced by the composer + submodules under modules/gcp/) - modules/gcp-sa-provisioning, modules/gcp-unity-catalog deprecation stubs (relocated to modules/gcp/{service-account,unity-catalog} in PR 1's foundation commit) - examples/gcp-sa-provisionning (typo dir, only had a Makefile) - examples/gcp-test-modules (only contained terraform.tfstate files) All examples now point at modules/gcp/*. Stray terraform.tfstate* files inside remaining example dirs are gitignored and remain on disk untouched. Co-authored-by: Isaac --- examples/gcp-sa-provisionning/Makefile | 7 - modules/gcp-sa-provisioning/README.md | 14 -- modules/gcp-unity-catalog/README.md | 14 -- .../Makefile | 7 - .../README.md | 139 ------------ .../databricks-cloud-resources.tf | 86 ------- .../dns-hub.tf | 214 ------------------ .../dns-spoke.tf | 135 ----------- .../firewall-hub.tf | 21 -- .../firewall-spoke.tf | 112 --------- .../images/architecture.png | Bin 416969 -> 0 bytes .../main.tf | 63 ------ .../outputs.tf | 10 - .../psc.tf | 75 ------ .../terraform.tf | 13 -- .../variables.tf | 62 ----- .../vpc.tf | 90 -------- .../workspace.tf | 22 -- modules/gcp-workspace-basic/Makefile | 7 - modules/gcp-workspace-basic/README.md | 67 ------ modules/gcp-workspace-basic/init.tf | 24 -- modules/gcp-workspace-basic/outputs.tf | 9 - modules/gcp-workspace-basic/variables.tf | 29 --- modules/gcp-workspace-basic/workspace.tf | 14 -- modules/gcp-workspace-byovpc/Makefile | 7 - modules/gcp-workspace-byovpc/README.md | 75 ------ modules/gcp-workspace-byovpc/init.tf | 22 -- modules/gcp-workspace-byovpc/outputs.tf | 8 - modules/gcp-workspace-byovpc/variables.tf | 45 ---- modules/gcp-workspace-byovpc/vpc.tf | 40 ---- modules/gcp-workspace-byovpc/workspace.tf | 20 -- 31 files changed, 1451 deletions(-) delete mode 100644 examples/gcp-sa-provisionning/Makefile delete mode 100644 modules/gcp-sa-provisioning/README.md delete mode 100644 modules/gcp-unity-catalog/README.md delete mode 100644 modules/gcp-with-psc-exfiltration-protection/Makefile delete mode 100644 modules/gcp-with-psc-exfiltration-protection/README.md delete mode 100644 modules/gcp-with-psc-exfiltration-protection/databricks-cloud-resources.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/dns-hub.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/dns-spoke.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/firewall-hub.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/firewall-spoke.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/images/architecture.png delete mode 100644 modules/gcp-with-psc-exfiltration-protection/main.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/outputs.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/psc.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/terraform.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/variables.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/vpc.tf delete mode 100644 modules/gcp-with-psc-exfiltration-protection/workspace.tf delete mode 100644 modules/gcp-workspace-basic/Makefile delete mode 100644 modules/gcp-workspace-basic/README.md delete mode 100644 modules/gcp-workspace-basic/init.tf delete mode 100644 modules/gcp-workspace-basic/outputs.tf delete mode 100644 modules/gcp-workspace-basic/variables.tf delete mode 100644 modules/gcp-workspace-basic/workspace.tf delete mode 100644 modules/gcp-workspace-byovpc/Makefile delete mode 100644 modules/gcp-workspace-byovpc/README.md delete mode 100644 modules/gcp-workspace-byovpc/init.tf delete mode 100644 modules/gcp-workspace-byovpc/outputs.tf delete mode 100644 modules/gcp-workspace-byovpc/variables.tf delete mode 100644 modules/gcp-workspace-byovpc/vpc.tf delete mode 100644 modules/gcp-workspace-byovpc/workspace.tf diff --git a/examples/gcp-sa-provisionning/Makefile b/examples/gcp-sa-provisionning/Makefile deleted file mode 100644 index 653039d..0000000 --- a/examples/gcp-sa-provisionning/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -.PHONY: docs test_docs - -docs: - terraform-docs -c ../../.terraform-docs.yml . - -test_docs: - terraform-docs -c ../../.terraform-docs.yml --output-check . diff --git a/modules/gcp-sa-provisioning/README.md b/modules/gcp-sa-provisioning/README.md deleted file mode 100644 index 1132953..0000000 --- a/modules/gcp-sa-provisioning/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# DEPRECATED — moved to `modules/gcp/service-account/` - -This module has been relocated to [`../gcp/service-account/`](../gcp/service-account/). - -All variables, outputs, and resource addresses are unchanged. Update your -module `source` from: - - source = "github.com/databricks/terraform-databricks-examples/modules/gcp-sa-provisioning" - -to: - - source = "github.com/databricks/terraform-databricks-examples/modules/gcp/service-account" - -This stub will be removed in PR 6 of the GCP modules refactor. diff --git a/modules/gcp-unity-catalog/README.md b/modules/gcp-unity-catalog/README.md deleted file mode 100644 index 9987312..0000000 --- a/modules/gcp-unity-catalog/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# DEPRECATED — moved to `modules/gcp/unity-catalog/` - -This module has been relocated to [`../gcp/unity-catalog/`](../gcp/unity-catalog/). - -All variables, outputs, and resource addresses are unchanged. Update your -module `source` from: - - source = "github.com/databricks/terraform-databricks-examples/modules/gcp-unity-catalog" - -to: - - source = "github.com/databricks/terraform-databricks-examples/modules/gcp/unity-catalog" - -This stub will be removed in PR 6 of the GCP modules refactor. diff --git a/modules/gcp-with-psc-exfiltration-protection/Makefile b/modules/gcp-with-psc-exfiltration-protection/Makefile deleted file mode 100644 index 653039d..0000000 --- a/modules/gcp-with-psc-exfiltration-protection/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -.PHONY: docs test_docs - -docs: - terraform-docs -c ../../.terraform-docs.yml . - -test_docs: - terraform-docs -c ../../.terraform-docs.yml --output-check . diff --git a/modules/gcp-with-psc-exfiltration-protection/README.md b/modules/gcp-with-psc-exfiltration-protection/README.md deleted file mode 100644 index 6f9650d..0000000 --- a/modules/gcp-with-psc-exfiltration-protection/README.md +++ /dev/null @@ -1,139 +0,0 @@ -# Databricks on Google Cloud with Private Service Connect and Hub-Spoke network structure (data exfiltration protection). - -## ⚠️ Prerequisites -To **enable Private Service Connect for your Databricks workspace** on Google Cloud, you must contact your Databricks account team and provide: -- Databricks account ID -- VPC Host Project ID of the **compute plane VPC** for enabling back-end Private Service Connect -- VPC Host Project ID of the **transit VPC** for enabling front-end Private Service Connect -- Workspace region - -This configuration **cannot be completed independently** and requires coordination with your Databricks account team. - -## Overview - -The module includes: -1. Hub-Spoke networking with egress firewall to control all outbound traffic, e.g. to pypi.org. -2. Private Service Connect connection for backend traffic from data plane to control plane. -3. Private Service Connect connection from user client to webapp service. -4. Private Google Access from data plane to DBFS storage. -5. Private Service Connect connection for web-auth traffic. - -## Overall Architecture - -![alt text](images/architecture.png) - -With this deployment, traffic from user client to webapp (notebook UI), backend traffic from data plane to control plane will be through PSC endpoints. This terraform sample will create: -* Hub VPC and its subnet -* Spoke VPC and its subnets -* Peering between Hub and Spoke VPC -* Private Service Connect (PSC) endpoints -* DNS private and peering zones -* Firewall rules for Hub and Spoke VPCs -* Databricks workspace with private link to control plane, user to webapp and private link to DBFS - - -**Note that** the module does not contain the VPC SC implementation. This can be added to increase the security level in the Databricks deployment, providing detailed access level for ingress and egress traffic. -## How to use - -> **Note** -> You can customize this module by adding, deleting or updating the Google Cloud resources to adapt the module to your requirements. -> A deployment example using this module can be found in [examples/gcp-with-psc-exfiltration-protection](../../examples/gcp-with-psc-exfiltration-protection) - -1. Reference this module using one of the different [module source types](https://developer.hashicorp.com/terraform/language/modules/sources) -2. Add `terraform.tfvars` with the information about service principals to be provisioned at account level. - - -## Requirements - -No requirements. - -## Providers - -| Name | Version | -|------|---------| -| [databricks](#provider\_databricks) | n/a | -| [google](#provider\_google) | n/a | -| [random](#provider\_random) | n/a | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [databricks_mws_networks.databricks_network](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_networks) | resource | -| [databricks_mws_private_access_settings.pas](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_private_access_settings) | resource | -| [databricks_mws_vpc_endpoint.backend_endpoint](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_vpc_endpoint) | resource | -| [databricks_mws_vpc_endpoint.frontend_endpoint](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_vpc_endpoint) | resource | -| [databricks_mws_vpc_endpoint.transit_endpoint](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_vpc_endpoint) | resource | -| [databricks_mws_workspaces.databricks_workspace](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_workspaces) | resource | -| [google_compute_address.backend_pe_ip_address](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_address) | resource | -| [google_compute_address.hub_frontend_pe_ip_address](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_address) | resource | -| [google_compute_address.spoke_frontend_pe_ip_address](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_address) | resource | -| [google_compute_firewall.databricks_workspace_traffic](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | -| [google_compute_firewall.default_deny_egress](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | -| [google_compute_firewall.hub_net_traffic](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | -| [google_compute_firewall.to_databricks_compute_plane](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | -| [google_compute_firewall.to_databricks_control_plane](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | -| [google_compute_firewall.to_google_apis](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | -| [google_compute_firewall.to_managed_hive](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | -| [google_compute_forwarding_rule.backend_psc_ep](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_forwarding_rule) | resource | -| [google_compute_forwarding_rule.hub_frontend_psc_ep](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_forwarding_rule) | resource | -| [google_compute_forwarding_rule.spoke_frontend_psc_ep](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_forwarding_rule) | resource | -| [google_compute_network.hub_vpc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network) | resource | -| [google_compute_network.spoke_vpc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network) | resource | -| [google_compute_network_peering.hub_spoke_peering](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network_peering) | resource | -| [google_compute_network_peering.spoke_hub_peering](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network_peering) | resource | -| [google_compute_shared_vpc_host_project.host](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_shared_vpc_host_project) | resource | -| [google_compute_shared_vpc_service_project.service](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_shared_vpc_service_project) | resource | -| [google_compute_subnetwork.hub_subnetwork](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork) | resource | -| [google_compute_subnetwork.psc_subnetwork](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork) | resource | -| [google_compute_subnetwork.spoke_subnetwork](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork) | resource | -| [google_dns_managed_zone.gcr_peering_zone](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | -| [google_dns_managed_zone.gcr_private_zone](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | -| [google_dns_managed_zone.google_apis_peering_zone](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | -| [google_dns_managed_zone.google_apis_private_zone](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | -| [google_dns_managed_zone.hub_private_zone](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | -| [google_dns_managed_zone.pkg_dev_peering_zone](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | -| [google_dns_managed_zone.pkg_dev_private_zone](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | -| [google_dns_managed_zone.spoke_private_zone](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_managed_zone) | resource | -| [google_dns_record_set.gcr_a](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.gcr_cname](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.hub_workspace_dp](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.hub_workspace_psc_auth](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.hub_workspace_url](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.pkg_dev_a](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.pkg_dev_cname](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.restricted_apis_a](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.restricted_apis_cname](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.spoke_relay](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.spoke_workspace_dp](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [google_dns_record_set.spoke_workspace_url](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource | -| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [databricks\_account\_id](#input\_databricks\_account\_id) | Databricks Account ID | `string` | n/a | yes | -| [google\_region](#input\_google\_region) | Google Cloud region where the resources will be created | `string` | n/a | yes | -| [hive\_metastore\_ip](#input\_hive\_metastore\_ip) | Value of regional default Hive Metastore IP | `string` | n/a | yes | -| [hub\_vpc\_cidr](#input\_hub\_vpc\_cidr) | CIDR for Hub VPC | `string` | n/a | yes | -| [hub\_vpc\_google\_project](#input\_hub\_vpc\_google\_project) | Google Cloud project ID related to Hub VPC | `string` | n/a | yes | -| [is\_spoke\_vpc\_shared](#input\_is\_spoke\_vpc\_shared) | Whether the Spoke VPC is a Shared or a dedicated VPC | `bool` | n/a | yes | -| [prefix](#input\_prefix) | Prefix to use in generated resources name | `string` | n/a | yes | -| [psc\_subnet\_cidr](#input\_psc\_subnet\_cidr) | CIDR for Spoke VPC | `string` | n/a | yes | -| [spoke\_vpc\_cidr](#input\_spoke\_vpc\_cidr) | CIDR for Spoke VPC | `string` | n/a | yes | -| [spoke\_vpc\_google\_project](#input\_spoke\_vpc\_google\_project) | Google Cloud project ID related to Spoke VPC | `string` | n/a | yes | -| [tags](#input\_tags) | Map of tags to add to all resources | `map(string)` | n/a | yes | -| [workspace\_google\_project](#input\_workspace\_google\_project) | Google Cloud project ID related to Databricks workspace | `string` | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| [workspace\_id](#output\_workspace\_id) | The Databricks workspace ID | -| [workspace\_url](#output\_workspace\_url) | The workspace URL which is of the format '{workspaceId}.{random}.gcp.databricks.com' | - \ No newline at end of file diff --git a/modules/gcp-with-psc-exfiltration-protection/databricks-cloud-resources.tf b/modules/gcp-with-psc-exfiltration-protection/databricks-cloud-resources.tf deleted file mode 100644 index f0a355e..0000000 --- a/modules/gcp-with-psc-exfiltration-protection/databricks-cloud-resources.tf +++ /dev/null @@ -1,86 +0,0 @@ -################################################### -# Databricks VPC Endpoints & Network Configuration -################################################### - -# ================================================ -# Private Service Connect Endpoint Configurations -# ================================================ - -# Registers a transit VPC endpoint for hub network connectivity -resource "databricks_mws_vpc_endpoint" "transit_endpoint" { - depends_on = [google_compute_forwarding_rule.backend_psc_ep] - - vpc_endpoint_name = "${var.prefix}-hub-ep-${random_string.suffix.result}" - account_id = var.databricks_account_id - - # GCP-specific PSC configuration for hub network - gcp_vpc_endpoint_info { - project_id = var.hub_vpc_google_project - psc_endpoint_name = google_compute_forwarding_rule.hub_frontend_psc_ep.name - endpoint_region = var.google_region - } -} - -# Registers frontend workspace VPC endpoint for user-facing access -resource "databricks_mws_vpc_endpoint" "frontend_endpoint" { - depends_on = [google_compute_forwarding_rule.backend_psc_ep] - - vpc_endpoint_name = "${var.prefix}-ws-ep-${random_string.suffix.result}" - account_id = var.databricks_account_id - - # GCP-specific PSC configuration for spoke workspace - gcp_vpc_endpoint_info { - project_id = var.spoke_vpc_google_project - psc_endpoint_name = google_compute_forwarding_rule.spoke_frontend_psc_ep.name - endpoint_region = var.google_region - } -} - -# Registers backend SCC (Secure Cluster Connectivity) endpoint -resource "databricks_mws_vpc_endpoint" "backend_endpoint" { - depends_on = [google_compute_forwarding_rule.spoke_frontend_psc_ep] - - vpc_endpoint_name = "${var.prefix}-scc-ep-${random_string.suffix.result}" - account_id = var.databricks_account_id - - # GCP-specific PSC configuration for backend connectivity - gcp_vpc_endpoint_info { - project_id = var.spoke_vpc_google_project - psc_endpoint_name = google_compute_forwarding_rule.backend_psc_ep.name - endpoint_region = var.google_region - } -} - -# ================================================ -# Network Configuration for Databricks Workspace -# ================================================ - -resource "databricks_mws_networks" "databricks_network" { - network_name = "${var.prefix}-ntw-${random_string.suffix.result}" - account_id = var.databricks_account_id - - # GCP network infrastructure details - gcp_network_info { - network_project_id = var.spoke_vpc_google_project - vpc_id = google_compute_network.spoke_vpc.name - subnet_id = google_compute_subnetwork.spoke_subnetwork.name - subnet_region = var.google_region - } - - # PrivateLink endpoint associations - vpc_endpoints { - dataplane_relay = [databricks_mws_vpc_endpoint.backend_endpoint.vpc_endpoint_id] # SCC connectivity - rest_api = [databricks_mws_vpc_endpoint.frontend_endpoint.vpc_endpoint_id] # Workspace API access - } -} - -# ================================================ -# Private Access Configuration -# ================================================ - -resource "databricks_mws_private_access_settings" "pas" { - private_access_settings_name = "${var.prefix}-pas-${random_string.suffix.result}" - region = var.google_region - public_access_enabled = false # Block public internet access - private_access_level = "ACCOUNT" # Apply to entire Databricks account -} diff --git a/modules/gcp-with-psc-exfiltration-protection/dns-hub.tf b/modules/gcp-with-psc-exfiltration-protection/dns-hub.tf deleted file mode 100644 index b0eca33..0000000 --- a/modules/gcp-with-psc-exfiltration-protection/dns-hub.tf +++ /dev/null @@ -1,214 +0,0 @@ -######################################### -# Databricks Private DNS Configuration # -######################################### - -# Create a private DNS zone for Databricks PSC management -resource "google_dns_managed_zone" "hub_private_zone" { - name = "${var.prefix}-hub-gcp-databricks-com" - project = var.hub_vpc_google_project - dns_name = "gcp.databricks.com." - description = "Private DNS zone for Databricks PSC management" - visibility = "private" - - # Restrict visibility to the hub VPC network - private_visibility_config { - networks { - network_url = google_compute_network.hub_vpc.id - } - } -} - -# DNS A record for the Databricks workspace URL -resource "google_dns_record_set" "hub_workspace_url" { - name = "${local.workspace_dns_id}.${google_dns_managed_zone.hub_private_zone.dns_name}" - project = var.hub_vpc_google_project - managed_zone = google_dns_managed_zone.hub_private_zone.name - type = "A" - ttl = 300 - - # Points to the Databricks frontend Private Endpoint IP address - rrdatas = [ - google_compute_address.hub_frontend_pe_ip_address.address - ] -} - -# DNS A record for the Databricks PSC authentication endpoint -resource "google_dns_record_set" "hub_workspace_psc_auth" { - name = "${var.google_region}.psc-auth.${google_dns_managed_zone.hub_private_zone.dns_name}" - project = var.hub_vpc_google_project - managed_zone = google_dns_managed_zone.hub_private_zone.name - type = "A" - ttl = 300 - - # Points to the same frontend Private Endpoint IP - rrdatas = [ - google_compute_address.hub_frontend_pe_ip_address.address - ] -} - -# DNS A record for the Databricks dataplane endpoint -resource "google_dns_record_set" "hub_workspace_dp" { - name = "dp-${local.workspace_dns_id}.${google_dns_managed_zone.hub_private_zone.dns_name}" - project = var.hub_vpc_google_project - managed_zone = google_dns_managed_zone.hub_private_zone.name - type = "A" - ttl = 300 - - # Points to the same frontend Private Endpoint IP - rrdatas = [ - google_compute_address.hub_frontend_pe_ip_address.address - ] -} - -############################################# -# Google Container Registry Private DNS Zone # -############################################# - -# Create a private DNS zone for GCR (gcr.io) -resource "google_dns_managed_zone" "gcr_private_zone" { - name = "${var.prefix}-gcr-io" - project = var.hub_vpc_google_project - dns_name = "gcr.io." - description = "Private DNS zone for GCR private resolution" - visibility = "private" - - # Restrict visibility to the hub VPC network - private_visibility_config { - networks { - network_url = google_compute_network.hub_vpc.id - } - } -} - -# Wildcard CNAME record for all subdomains of gcr.io -resource "google_dns_record_set" "gcr_cname" { - name = "*.${google_dns_managed_zone.gcr_private_zone.dns_name}" - project = var.hub_vpc_google_project - managed_zone = google_dns_managed_zone.gcr_private_zone.name - type = "CNAME" - ttl = 300 - - # All subdomains point to gcr.io - rrdatas = [ - "gcr.io." - ] -} - -# A record for gcr.io pointing to Google IPs for private access -resource "google_dns_record_set" "gcr_a" { - name = google_dns_managed_zone.gcr_private_zone.dns_name - project = var.hub_vpc_google_project - managed_zone = google_dns_managed_zone.gcr_private_zone.name - type = "A" - ttl = 300 - - # Official Google IPs for gcr.io - rrdatas = [ - "199.36.153.8", - "199.36.153.9", - "199.36.153.10", - "199.36.153.11" - ] -} - -################################## -# Google APIs Private DNS Zone # -################################## - -# Create a private DNS zone for Google APIs (googleapis.com) -resource "google_dns_managed_zone" "google_apis_private_zone" { - name = "${var.prefix}-google-apis" - project = var.hub_vpc_google_project - dns_name = "googleapis.com." - description = "Private DNS zone for Google APIs resolution" - visibility = "private" - - # Restrict visibility to the hub VPC network - private_visibility_config { - networks { - network_url = google_compute_network.hub_vpc.id - } - } -} - -# Wildcard CNAME record for all subdomains of googleapis.com -resource "google_dns_record_set" "restricted_apis_cname" { - name = "*.${google_dns_managed_zone.google_apis_private_zone.dns_name}" - project = var.hub_vpc_google_project - managed_zone = google_dns_managed_zone.google_apis_private_zone.name - type = "CNAME" - ttl = 300 - - # All subdomains point to restricted.googleapis.com - rrdatas = [ - "restricted.googleapis.com." - ] -} - -# A record for restricted.googleapis.com pointing to Google IPs for private access -resource "google_dns_record_set" "restricted_apis_a" { - name = "restricted.${google_dns_managed_zone.google_apis_private_zone.dns_name}" - project = var.hub_vpc_google_project - managed_zone = google_dns_managed_zone.google_apis_private_zone.name - type = "A" - ttl = 300 - - # Official Google IPs for restricted.googleapis.com - rrdatas = [ - "199.36.153.4", - "199.36.153.5", - "199.36.153.6", - "199.36.153.7" - ] -} - -################################## -# Go Packages Private DNS Zone # -################################## - -# Create a private DNS zone for Go Packages (pkg.dev) -resource "google_dns_managed_zone" "pkg_dev_private_zone" { - name = "${var.prefix}-pkg-dev" - project = var.hub_vpc_google_project - dns_name = "pkg.dev." - description = "Private DNS zone for Go Packages resolution" - visibility = "private" - - # Restrict visibility to the hub VPC network - private_visibility_config { - networks { - network_url = google_compute_network.hub_vpc.id - } - } -} - -# Wildcard CNAME record for all subdomains of pkg.dev -resource "google_dns_record_set" "pkg_dev_cname" { - name = "*.${google_dns_managed_zone.pkg_dev_private_zone.dns_name}" - project = var.hub_vpc_google_project - managed_zone = google_dns_managed_zone.pkg_dev_private_zone.name - type = "CNAME" - ttl = 300 - - # All subdomains point to pkg.dev - rrdatas = [ - "pkg.dev." - ] -} - -# A record for pkg.dev pointing to Google IPs for private access -resource "google_dns_record_set" "pkg_dev_a" { - name = google_dns_managed_zone.pkg_dev_private_zone.dns_name - project = var.hub_vpc_google_project - managed_zone = google_dns_managed_zone.pkg_dev_private_zone.name - type = "A" - ttl = 300 - - # Official Google IPs for pkg.dev - rrdatas = [ - "199.36.153.8", - "199.36.153.9", - "199.36.153.10", - "199.36.153.11" - ] -} diff --git a/modules/gcp-with-psc-exfiltration-protection/dns-spoke.tf b/modules/gcp-with-psc-exfiltration-protection/dns-spoke.tf deleted file mode 100644 index 799cd81..0000000 --- a/modules/gcp-with-psc-exfiltration-protection/dns-spoke.tf +++ /dev/null @@ -1,135 +0,0 @@ -############################################# -# Databricks Private DNS Zone (Spoke VPC) # -############################################# - -# Creates a private DNS managed zone for Databricks PSC endpoints -# This zone is only visible within the spoke VPC network -resource "google_dns_managed_zone" "spoke_private_zone" { - name = "${var.prefix}-spoke-gcp-databricks-com" - project = var.spoke_vpc_google_project - dns_name = "gcp.databricks.com." - description = "Private DNS zone for Databricks PSC management" - visibility = "private" - - # Restricts DNS zone visibility to the spoke VPC - private_visibility_config { - networks { - network_url = google_compute_network.spoke_vpc.id - } - } -} - -# Creates an A record for the Databricks workspace endpoint in the spoke VPC -resource "google_dns_record_set" "spoke_workspace_url" { - name = "${local.workspace_dns_id}.${google_dns_managed_zone.spoke_private_zone.dns_name}" - project = var.spoke_vpc_google_project - managed_zone = google_dns_managed_zone.spoke_private_zone.name - type = "A" - ttl = 300 - - # Points to the Databricks frontend Private Endpoint IP in the spoke VPC - rrdatas = [ - google_compute_address.spoke_frontend_pe_ip_address.address - ] -} - -# Creates an A record for the Databricks dataplane endpoint in the spoke VPC -resource "google_dns_record_set" "spoke_workspace_dp" { - name = "dp-${local.workspace_dns_id}.${google_dns_managed_zone.spoke_private_zone.dns_name}" - project = var.spoke_vpc_google_project - managed_zone = google_dns_managed_zone.spoke_private_zone.name - type = "A" - ttl = 300 - - # Points to the Databricks frontend Private Endpoint IP in the spoke VPC - rrdatas = [ - google_compute_address.spoke_frontend_pe_ip_address.address - ] -} - -# Creates an A record for the Databricks relay/tunnel endpoint in the spoke VPC -resource "google_dns_record_set" "spoke_relay" { - name = "tunnel.${var.google_region}.${google_dns_managed_zone.spoke_private_zone.dns_name}" - project = var.spoke_vpc_google_project - managed_zone = google_dns_managed_zone.spoke_private_zone.name - type = "A" - ttl = 300 - - # Points to the backend Private Endpoint IP (used for relay/tunnel) - rrdatas = [ - google_compute_address.backend_pe_ip_address.address - ] -} - -########################################################## -# Peering DNS Zones for Hub-Spoke Shared Service Access # -########################################################## - -# The following managed zones provide private DNS for Google services (GCR, Google APIs, Go Packages) -# and are peered to the hub VPC for shared DNS resolution across VPCs. - -# Google Container Registry (GCR) private peering zone -resource "google_dns_managed_zone" "gcr_peering_zone" { - name = "${var.prefix}-peering-gcr" - project = var.spoke_vpc_google_project - dns_name = "gcr.io." - description = "Peering DNS zone for GCR private resolution" - visibility = "private" - - private_visibility_config { - networks { - network_url = google_compute_network.spoke_vpc.id - } - } - - # Peers this DNS zone with the hub VPC to allow DNS resolution from the hub - peering_config { - target_network { - network_url = google_compute_network.hub_vpc.id - } - } -} - -# Google APIs private peering zone -resource "google_dns_managed_zone" "google_apis_peering_zone" { - name = "${var.prefix}-peering-google-apis" - project = var.spoke_vpc_google_project - dns_name = "googleapis.com." - description = "Private DNS zone for Google APIs resolution" - visibility = "private" - - private_visibility_config { - networks { - network_url = google_compute_network.spoke_vpc.id - } - } - - # Peers this DNS zone with the hub VPC to allow DNS resolution from the hub - peering_config { - target_network { - network_url = google_compute_network.hub_vpc.id - } - } -} - -# Go Packages (pkg.dev) private peering zone -resource "google_dns_managed_zone" "pkg_dev_peering_zone" { - name = "${var.prefix}-peering-pkg-dev" - project = var.spoke_vpc_google_project - dns_name = "pkg.dev." - description = "Private DNS zone for Go Packages resolution" - visibility = "private" - - private_visibility_config { - networks { - network_url = google_compute_network.spoke_vpc.id - } - } - - # Peers this DNS zone with the hub VPC to allow DNS resolution from the hub - peering_config { - target_network { - network_url = google_compute_network.hub_vpc.id - } - } -} diff --git a/modules/gcp-with-psc-exfiltration-protection/firewall-hub.tf b/modules/gcp-with-psc-exfiltration-protection/firewall-hub.tf deleted file mode 100644 index a1563a2..0000000 --- a/modules/gcp-with-psc-exfiltration-protection/firewall-hub.tf +++ /dev/null @@ -1,21 +0,0 @@ -# ========================================================== -# Google Cloud VPC Firewall Rule: Hub Network Ingress Traffic -# ========================================================== - -resource "google_compute_firewall" "hub_net_traffic" { - name = "${google_compute_network.hub_vpc.name}-ingress" - - project = var.hub_vpc_google_project - network = google_compute_network.hub_vpc.self_link - - direction = "INGRESS" - priority = 1000 - destination_ranges = [] - # The source IP range(s) allowed by this rule (CIDR format) - # Only traffic originating from the spoke VPC's CIDR block will be allowed - source_ranges = [var.spoke_vpc_cidr] - - allow { - protocol = "all" - } -} diff --git a/modules/gcp-with-psc-exfiltration-protection/firewall-spoke.tf b/modules/gcp-with-psc-exfiltration-protection/firewall-spoke.tf deleted file mode 100644 index a44c69a..0000000 --- a/modules/gcp-with-psc-exfiltration-protection/firewall-spoke.tf +++ /dev/null @@ -1,112 +0,0 @@ -############################################################# -# Google Cloud Firewall Rules for Databricks Spoke Network # -############################################################# - -# ========================================================== -# Default Egress Deny Rule (Catch-All Block) -# ========================================================== - -resource "google_compute_firewall" "default_deny_egress" { - name = "${google_compute_network.spoke_vpc.name}-default-deny-egress" - project = var.spoke_vpc_google_project - network = google_compute_network.spoke_vpc.self_link - - direction = "EGRESS" - priority = 1100 # Higher priority than allow rules - destination_ranges = ["0.0.0.0/0"] # Block all external destinations - source_ranges = [] - - deny { protocol = "all" } # Explicit deny all outbound traffic -} - -# ========================================================== -# Essential Service Allow Rules -# ========================================================== - -# Allows outbound traffic to Google APIs and services -resource "google_compute_firewall" "to_google_apis" { - name = "${google_compute_network.spoke_vpc.name}-to-google-apis" - project = var.spoke_vpc_google_project - network = google_compute_network.spoke_vpc.self_link - - direction = "EGRESS" - priority = 1000 # Lower priority than deny rule - destination_ranges = [ - "199.36.153.4/30", # Restricted Google APIs - "199.36.153.8/30", # GCR/GCS endpoints - "34.126.0.0/18" # Additional Google service IPs - ] - - allow { protocol = "all" } # Full protocol access to these IPs -} - -# Allows control plane communication for Databricks -resource "google_compute_firewall" "to_databricks_control_plane" { - name = "${google_compute_network.spoke_vpc.name}-to-databricks-control-plane" - project = var.spoke_vpc_google_project - network = google_compute_network.spoke_vpc.self_link - - direction = "EGRESS" - priority = 1000 - destination_ranges = [ - "${google_compute_forwarding_rule.backend_psc_ep.ip_address}/32", # SCC endpoint - "${google_compute_forwarding_rule.spoke_frontend_psc_ep.ip_address}/32" # Frontend endpoint - ] - - allow { - protocol = "tcp" - ports = ["443"] # HTTPS only - } -} - -# ========================================================== -# Managed Hive Metastore Access (Conditional) -# ========================================================== - -resource "google_compute_firewall" "to_managed_hive" { - name = "${google_compute_network.spoke_vpc.name}-to-${var.google_region}-managed-hive" - project = var.spoke_vpc_google_project - network = google_compute_network.spoke_vpc.self_link - - direction = "EGRESS" - priority = 1000 - destination_ranges = ["${var.hive_metastore_ip}/32"] # Metastore-specific IP - - allow { - protocol = "tcp" - ports = ["3306"] # MySQL port - } -} - -# ========================================================== -# Internal Workspace Communication -# ========================================================== - -resource "google_compute_firewall" "databricks_workspace_traffic" { - name = "${google_compute_network.spoke_vpc.name}-${databricks_mws_workspaces.databricks_workspace.workspace_id}-ingress" - project = var.spoke_vpc_google_project - network = google_compute_network.spoke_vpc.self_link - - direction = "INGRESS" - priority = 1000 - source_ranges = [var.spoke_vpc_cidr] # Internal VPC traffic - target_tags = ["databricks-${databricks_mws_workspaces.databricks_workspace.workspace_id}"] # Workspace-specific instances - - allow { protocol = "all" } # Full internal access -} - -resource "google_compute_firewall" "to_databricks_compute_plane" { - name = "${google_compute_network.spoke_vpc.name}-to-databricks-compute-plane" - project = var.spoke_vpc_google_project - network = google_compute_network.spoke_vpc.self_link - - direction = "EGRESS" - priority = 1000 - destination_ranges = [ - var.spoke_vpc_cidr - ] - - allow { - protocol = "all" - } -} \ No newline at end of file diff --git a/modules/gcp-with-psc-exfiltration-protection/images/architecture.png b/modules/gcp-with-psc-exfiltration-protection/images/architecture.png deleted file mode 100644 index 9b2459046308adda5affef82d85303d1ece56cfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 416969 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

sG&ggg=1{VZ!Nz6h4*4f zkg=wIUR{6VXP*!E#r}nzU+V?;`=zdlp7eZ61crGZ6Lou-@J2x!A`0-stnCi*+lvKM z(YporgZtsfVWS|5a{c@UEbq7$x1(;fztbR<0F_?A+H={0uBf(8-SFQjv_%{KdNs@G z8<_%3vMk>>OTFacdUTuE#5m#>1|s%xp%cu6f1l)Lcc~@Fk-|)PIa-ug%zCUGZxEOs zWh{!^mJvH?uM(+F$it9-)0aymlgdhYC5^`hb(O$&wvN?xI&KZXC(`@?axyk0E-A}h z?f(O>+WTJy@)C>^lx}5YCv;(!qRNM0IvKOPMZdLV??f~hVb)zLSiaHl5D5Pphe`!G z@_^o5yTXe$x7LC35Wc=HNWHjbiGRQ4-M_9O4EO8H7y<+)zP*eV8M42N2yPwRh$h-4 zDzm`9F;*H)DcR+NeO+^WsRAIen*8kEaf~=N2)EmEQwaj>aQ=68oZn;PJr0i-C_iYL z8(^Hl{6B}7&C=5fzHO0UJP1_%?vjR zd_eE*`{7EPH#yl};|Ka*1xR0slxs&JjUnU$3jIuX4^OKuy|eT-P3?qI*FM=&vRH=t z9&vv?TW_Ozj&#E|z+O!8vDDBz8kg+z*0ml?yMK3&1&DgaN793)Cq~xUR zdhZh2?FL#-p=X_x%zW@HH%HT-m;Re2MSB4#15!njd~Fr`+Bc!uEIOCRw@)@fYNU;3 zsa1*PHH>#e_Vf22{~@e`p~w~P@&_4?SyyS-5(hzCkxeV|s#{DNsY#5>O{F+5^i8JClJ zwd)EhwW)Oq3Trr-VcSc=~GY zNpXC5NDd^-iSEqA$ZV=Yz;mEaAQI}SJzw|ow4F1@9kMRS@!O;Fz7Mdy>)F$s+eU^o z4sZi)a(gOoT5{82UZ08?JMF=v%6X~?ld=cO2Dkp?K)OZ)2{xIHc$bPUerm z3=jo1niwqXGVwBIn*^$Q)?mo;zY>bVz?3sqU(?e_W}tIT*Y+Tv8%v2>$=MDvI{Z`- zs}q6b_(M}D>WJ)lIKJBPljy=`sl%U!lCKd}h_%?%mNXpoP)%Y!HjjdhJEG?6tIsl# zw73ht!~c@>aAANgTy0DLP$hjZ;IUO6V7qfF?L+q3Q z;)}IE41?LeaAkP&Rh-iuVq>QhS#+Q7^1QNmOv!;SVgZ&(hlOZN>6=P*kQICE$`R~S zqbfFDtNJ#4hJITDsZ-_ZK*#=m#+1fF9Llxz%%#9`P~$--ek-7C6kkxI+MA};(6J7T zU1ll_z>P}e+kMe~t8ziZBWw+Zh0d8LD+1y=`_?^2#}_?oQgUP8`0gbUG1Sj$)A$hd zdRf#&<~K`Ob@GAO?7>oLYSQy`amFP^xo!9!bm+x=bPC$*gv&&HFD2#pJ?&jfv#U`c zZ0aR`zl`~NYQSUPNk(NAx^+O4ngjA^tjLTBV=rj}0-<1l`*F;0LLOa;Tk4x=!rc%p zF%1G@a__>9fSk;Nc8t|WA4Bg*V8`San6LyGi3qup=pbve*lelDDmc#cKs#3O#_E@@ z0>gMkFNn<6peZI4X)k=v{4L_XZamtmu53_T%_Ru13>nzEaDYtv>yxD8}WedO@t+=f`pBld)_yKot#x@Osg7LeJx1mg#E){yMvb6 z!&8NEo!gi)-CdnGB#qELnRMg2x726S|sR?!(JWh zL#O@-Ruo8Hks(`Cem8}zsYSpQGyv|J8|(z7R>|8T_jo~Oe0|cu+4vy9cCo|)^Hc}%>5(Ou^cUOibB>W}jY`s9A_(w>WvpM%_6ti@^nNCA z^!0DZ6pk-|Kl#U2*hf3}?ivVa;JNdHvgTKL1TRdBnUc%U#?ojwBYQ~_t}BOMUviF> zllpBIEI?LXs*>K$2&2NYAvGjhC7Ks?+PlcG)xUc7|SzEoPnsH3$ z>m!7ire}@%Ccb85O9(>EZTCEx=B_@}9I92Dsq{c>%b%%SOnp@@JR$OjRq&<< zz@08^`kdQwGbsV=giY&azp~R~2sZ$U8viL^5b8q9|5EB;NVumaBp}ops@Y_8&|b}Bv%T?!RFCjosyb=7E1 z*LKfN=@X+o%DUnS*WJDUu%OQj163w*aBemTRr>vmn>@LtoMWYzWMgnnrZmE&Tv^fF zNZ+I1aNpufz<5|B*8bMQKkcD97U@nTo{DTB%&7FM!=pF0d1}$mt@cTsyzc~Q)k;DT z@2p~c!2wnDeF4{IG~E%oNaAxHvIeV@0j{XA(-KxdBfIcCVe=09BgQZ8ITAVv#2|T1 znvvxs+T`^4ZX_*f%tg!2AS0btEriL(o~WV`M6p#0O2d_0dpPy2UB98*>8<7RiYBtvT!i3cX?{LiFx!3W)WNlteM?( z=s!S?ul0cag9so{-Gr;*l7f?8+ZrzwfS4$2gr$O9+`ZxCWoYbdkzxfxOA&N1GfbI= zVkFIcYsgE?ZM6@~KCh12bWu@hfo$UNLq@26dq_>;?sYf@0MhSBYnPr|i2o&3Wh z2WOI|-X>%K6c$2-sC~&bB(a*3Kh3IEpCNKJ6hmZUE(}(5Jr@9#Zcv}&8aJI)lu54! zfURE&po8Wfz;$@SdmRch6kTAtB`L>s{sG-Pdk(V$F^(d2G)Vw-jR?CUU9 zz2I+y^y?I{69?qkzvkg>P*Cs zIxB$v@zkbV%4OIN2i2^q5D1j;<@d+s?p0oXGsOn1KHumzN zi);mHoqE)H4O`;f=x7u6Xi>27vS~j0tNk^?X{?MaX^r~!p7}OVn&eR) zxhqPMm(S!V8}VMH^0hgm%Rk)X7O27y@FxCoV*)Dj;hG%OBzy zR-@EAeLEA^`5}@o8hM&Xr9vOGLXh$y_sI|h>JK;|gg%54$DiD$U|dCk#M9D`#3fRp zn&%rZO|y@hxV=4k;Jzt7+s?f_b3LA37RqI5bzw2jT3A$3slIVuT+pYqxZo(-vgPnP zt>M9F9z(8%OQqHDeuwAl&rxP(>OYBCRS-0ShcLS_fw)o)FMyn+TXI4n_auI(0&Rdp z!f{Otz3B>~f?C(pz^X*=UFaCyOf{(%laC$o!QNd@|0<*(n^j_gPr;@|TjRP26rzv! z%bVb40=(IMhsU=8S;1*Q9~UpNp53(bc9K?k!YhG@2}DljB&b_uaVIZ>2N&Pb-F{H!S>T^8x6R9 z8O5-E1P|S0c&d0VcY-Q&paPfJE?7kenqll;WKX%HaNRbAJ=*tr4;!k1Nbj2cpd?;W zwDS7Y|HBoLE)&&jC6Bp~vjT7xzv-3{iJq2>eafr@PL!kidNysOu_d1KI^LLT;~{5^ z0=j1wSo%Zu8Jas2p~Eys3X!6F2aj21Ig>N!Xo@%Ya>gD!v1DjiEv8WulXQqd=d`?` z{~MHLp%t73Wh5rvzwLYeKm)v+LQd5i{6L&c2Z8DC#*Y&}m71LgkIj(P5f-S8`*pmk zs3p)El?n$PMEWFJZNns9Ya}~W=n7f+YZmMT=C;f$Ni)A7z|lIrGBtKO!YVXj(KMx| zEZ7k-;xrFpv$k{uJt};xM{Cc#v*Muj-B zJyUR46E&~el)(GSj0YHbfwG_WD%)L7vj&IGmkAbh?S#`ifM0nCT?*Nm9Ll0K>JXd$WDdNm<%h5B| zE`9n%Yo-$2-t(C+BT?Jpvsr5`QJTw#mSvUEk6r6}!uQxmmWLkeg_<|AIabVI$c!Al zsK0EPHapFmNm={eYgBMYuPOhUQN1s}qI-G)GCJ)LA|fy-$)8W4xS4Z6iC!O%#%@lZ z4V4F+uQ~)?QNl!%@(?wmVk47b#4bECGSWuf%=kUy9qEY-SK?AQ)Wyci}>F;tT=kH3Zu!o%tg1~%BfVX?_ zWc|6E^LwP~6w((%Yew-`g67&|{Mk6gmDTGQv)M5{YwesS!O>T{DS^^m!SpnCmABgt(&aWsmya@)r3Fp%8h||BVL>gj}4ysk>VJlEP>kMk=bnC(2 z&LMJQX6Q7za!y`%Qe^HZ-mHRxyRDTyd3xC?E5=?xM^{M4_{#@=3~cOMG6NnNcY8rq zZM=$-{TZJu@KHeOg2*Bn^4;XUl6~*?LF>dgRTo6vX3rT~)H5af-Tu|!%gY7u&!v%U zTFzXk2ngNqw+*j*3fS-FhuLt&6w@0YOAR3(ktul|d)0QC72DY}mW|iC z=?;Bke~F~e3Fo9r;1QRa8<7R{p->6pQ^N3PdW6X?ugJH1B1OzYu4WtkPO2YMM~rF5 z)>L)wnh;@KpJ-OhcVL0L^uzD*mYQUV2%L-4*T;R2>x)+mukZHThd4=4Xlk8}7?N4{u3$rdtuYH>W zJsaM>@x}Efn1K?BuhJ}YCKZHK*4?d;>Fth7!bo4m9$;z@=ZPfheg%_20_L1On{}P-f{S< z7M}i&vzRpe7nJCqR~@O^BQ0h@?P($#V`7ekndNp~^kESt!zUB4T2B zQE#d?fqac!T*ayT;#Z8=4MSleG={%o#y^cLsUa*zeA)B{`AYx{?S*8iRPvt z(OFQ%vNtyLT<5q(rHW9?MH*51n7tuCMws@>+HlCByfXN%*1?3v*w83=lOZB(I<{`I z$N`;>XBq0CAp#=l-|0pK=dMI#YeLiW=gTHPRjC#Oh80(6wmkzrxOtRE(kKiOTHqIz z+Z=Q{O=Xg=x#?l{6lW#SowIKm_gc;T{XxP|lDTVEZH5y>!TRU*YxALvQUn zp@ZKT-YggP^ZQE}jkzb8mn$64GGIIJiyCU{$N)>5vSWS+f?dYvJI^369* z?HA%nx)1P##q!&u+gaGJAAtZRQQns92E83cibP~` zE6vNF;(PS*Z6#9~Z53r$h%e}D^ginP`0Vt#lptNh^B~kZ5przp3PHe^l51fSpm@SJ z%4+lh_j3MKKAys!y$nxejfxjD!)u6pW!XD9;pduaEtU|{LyRe?-JNy+qk5J>#@yA+ z!eZrQ`{3B>&-QnGspNpP&sDDjH=Qy;GJU{Lsl+s@4P7CoB7jR-E3-4=R{m43Tfvhb zAUwirTUT-Gn}PY;=4hO?(_L;-8KFwks7*(~!vddhI|lm1+wlmK#VP3ZW?n85H_W*H zIM(o3HCNI3{zOkJ;Vyd_o)dH77l@A^NW0EjbGu6e%$OY5I8U>!r);}x`ZL*XtFqji zV2GA>Z?_)%a$Yas8ZwSoWy1C1_{nvLhGduJ^~Q9rMh`LHPxq{gbkFLCW6;o>bG$4i zsao-Ut;x0Pdy(aChYo$+tdp;b2Q%fnAD`nb{{}AK0?hg3UGfhv))AX@XD}%l-xdbL zxP10Pg*<-#f)}iraZnr=VA&ATUco7O#mpyPD`{fcppss07?|rdOG8r*#z1z2%J3Te zG)Gdv{uKB@;3|BIuv)h9-G)ghOWKb%lcrq+_NIvxj+@ps^=uzG?OJ9ZAFRJU zUi>68^zu^gI%>xb-KbED*Zr>e=_%)Uf^kDOnos80Yc>oMeY%}UmbU28qzeA0BYd&i zZsi`9bP7-O!J;DqZuKI|9`SpZb`IG@POC=sWJ4|Ay8E<%h$pZ3BB+hPO%J2%tEpjQ z@uS*j)PI<)R00$rU5JeFQR9}$TB!vX;IdWzvlkXeT2Q2J2GPp#Da;3X_FGAcxUGl9 zL@vnTY!hY_U`pHUfU-A3*4R$4&kux|%^e`ZV&Z@a#k*FS1*$8 ztdVTK`@b-Os6bYOR zaS}s*RiDr}l)Y?@Ob@9xU6;(ViJb}toPGpLtexA*eH?vG3(0J6BmKi0 z-=&*)CgjHCBH*1a9u4XL1m7&RB1MGjTEN8Fi-#GhGV#!h@TVN$98 z&M;Z$>FQL+2;lDaheHtzJ^2xtDmmAiC{o;KyFoQWT2Q~uSBCYhbW1!8U2htD6Mgl5 zo8>IP`bN`l+>%GZR9Bd!KIsckhh=jcUJCj!9v)w*5+*y3=k=0ng{k~_t{|j{fiLGw zqMAT~6NuV$65P>yyoS{n?Fr3gEAqUC zX+x$vu|`PmP#_!U>qYLb7tLCBvjau$FrXmY#jgRWYC;``*)>dT<>z`R+8>pw)O>yN zx?%y@{meQyVpf*r8N+o){xT(>K z2A4w43O31~j~+H9;F4r+*08mWu3-lO55&}Gv|XhE>e^#mh>^2uSLFcdD2M2I3R#;f z_1|tu3t%)A)cSlT{zN^0CHeYciE!e1-DfaVNstkV?W=BH#PHQQi3(=Bk}0z?aiC{@ zp>>DcV3i0+rd(|*@blb+Sms!`?ABeAVTWT*M|>?Xbf_+q3Q2J zSW}Z?pp!+hD`J-QFJqED0!~KNzE>7+^{!LyT_wnzS5zvtw{x!AU&@?A)q)_$*($@e zz3l0WzD)t14W&iy*&yYgt70`rc*p%{OuL|7{ActywP5L zbVtGE+v!pEK9mT++A*fdBRI9~wkF&nzB55eMJv-Y;YUr(uExN8tHniAr}rVg+MZf# zBo=54&XV6UFUa>bO(`lCKJopoGYFRgh*&$k^w)xo8$#==0b!co3ohat>MkQy6ow!A zlo;BB+Y?1wak2|vZ7KuER$3n$btGo7iZc!WY4JQs{d+&1cQRvxvEl)1lDVEMvH{(n z7{4?ejd74S;f7VQNESVaFft0?xqtcMEY`HJN^GgY>yECZ$G#WxYCTrC ztnR*})vd2_^_dAfu_6q2^E^M>Rl=6xY~*$BWn7iW;5W9Td18LaZ&z*~8&)CNCUlBy zo6k`#1iBs#tp(PNUF`D)xaZ*6sxT!tB4%Dd*4d-N4d%$@>mK9=0OApSjn;m98i3_`ze^EJLlLl={rRw{+A2y?7Uno*|6@a{yQTP zT4?S&NYqXbx?w2@+s{@?)c(`cx>+_!lQH!t~wrp+f!y8V}!WXV6 zIRAJtuW1?_o#>Q3qVg40p`d71m$qnFD>p4D1>ba;yx3{%A*j7cK=>k?INZ}d2Ai0U zFtL=jDn2zx`&xx5TT&XRo>6dY&B88zom=RvJ3nKkNW=BBShs~p!o~NQY)xeHsvVx(+AE=mGw_%*44j_4 zFXOOT&M}<5_AaWe!pbH#9UH(s1|2kZ*dQ0aY`o54w(;tyn?>H~G()fi+vB@h^WQyU z*MelNdolnBk+Ni!qsSeGdllHR?O>0NvL8ZQsGKE66lhP~%acDB#;$$uvlGB5)CEAN zrb0B~p3Y;xW~ogERahxal!Yc>*zw5a)I|sRk4-j+E|axf8(niOezNZ9QYoINc!~oc zc%t)1*QY%DBq8P#i46>@Ev(5=XJQtP4f1E5O{PJW_OH+XOhR|VGG~b(!++%0mtj$n zY%lVS2ma}vQS%>pTW#v(K^s)rep({`$CGN-?GMu8(HPG>4i+X9948O4wM)Cva!)uX z6o5qf3y5%Kx>#N;8KD>RTsU<7N^U*~U(Fk5<^y}yNoINFX5UK3k@yii8?J7z#xaQ^ z^E_XJRzv6`P}Z_ZdpNw$r@p$O$${cZmhbk2DiS8<`m#&JwV}RLGBM3>C+qSCDYd^M zfq-mx#GT7G5a;oB&bI~8S6lS<;#1jAS!tF`C>dbDrA>EA8>fr(;PgoT+Q805 z>)_6h*1C*b^eDjRiI8|C^3{5_J@4s$+{_1~@UL3ZF$s|~$z)oEn1j=UBh5h~-s=hg1SzrWEtcbk4|7hS*IIKX2Ko;k$Rtyx@f|rPbxyCKd&eyu8*TB`d>`&u z6FYJozB4ekVn`ysd3~_|S~aSo+M0dFJ8rzivU0DVi?Cqmn{qzE_AHiy&4w}9-KeJF z`ynALNn#R{uox(FB~xA5m{R0z_UmvEdAA?edRgNFr#IKLh46?&ER?(p$UtgCE8>$0-9wy*oG*l$ zc%j^~6$rt(v)E&_wDz2u3@|7ty-0coeoWt_IX}onl!H`g!iT^GyYud&s|KlC=Zx?ntY$e&Q;)jp?WQtDEI5X$XeQN0w6MYKR96c^ zs7>se*mSWqoh}dsn|D>wgyp5KO${;Y%|Tk#N1>*mvoF%FcS4E@&vqX?p`uFEqeb`# zE1s+VTr%*pIVgEdOHLiz9!{Z2G~{G- zsAO>a8d@egy!N7}l@wM02vmSkrlc#bXMdA+8#wMQ{Su&z-SEI88&xR>8w0(U8w}W`nYW;lD{sVJ}{7P?m17$QX1!WSF4+sP|I0? zFu7(jSz#f+s*a^F1R){Jw4fj}1#y2W)a@izWZHG;a@*U(;(IS7eZ(vHyv^(v*98p$ zDX>!sUHIje{aG6G6jsWQyn`8!0JEiDcs9&h&2I@5)4+E2zdvNc)cL`4 z-@F}%)&>C{72Ulpjv->FJWTvVxzHi~4Wit&H?-G~2{kz7>D=;@sILX9*u_ObD(RmKd!y ziv}<@JCG+UN9~ACa*6QcIjeGgvhX%92*68>9nFH^wNh}2t)kZ2N-oPZ7Z`lt&jbLBPH3-!-`iZAX&@jttN-L2}a z#so^BysxWzdaKUUr>Cs~DZhRprFR^b9-7sfYH$(QmUdi%LEgS9G(E-b)NnX`35I`!Qwic;G z9I*#}rv&hN0O{4BtB=lfk7v&FJmUxQt|HLRcoPt)iC@{x)UyAG8K7r8s_1kIjs5XV zV%b>;-<%oXcCZ7WArnxl20|^QkKWRyZN?>W3Kl2rUm|M&?dS?gQH2}iwPco`jLs+t z#CcZa4G+I|ZL}4CfuyCv)K7n9ZPC5c}FiZfS-d{}=TAwGjvpHi&GI ze|`QF0xzQiG8?89i8lqYsdNb0BmTzl`*Zcsv*icqSVpEvkt{$8veNefuYz0as1X|K z%D9Mm<(2qVKozF5Ab{>yfe+$i#epzsFjmY!hIN2sI78cm3LRr6x7e2bnIc`|0AV)Q z3d*i!#YenYtU~?B^>!p}0I+!)QLSCMl~uT&5G(d&7yP~ht_g_$m$l!w~uaU8X4n7*6McaUT&}O3t;+W`Cd*s=y8B(Qtn%x9bHC! z+LicN`8iJQVH44NiKg``6;ld2<(m}>y<3}iKd6w5n<6kRd9b^^y?~ufCxaxKc1M0W zDJr&5V>wirc(Y2PAf{9KylaoV%PL%j*>-8#M*Y%5&3jF(!r#Y-pYKa+?M5F)G;{Ds z4J>MNTu#RFdfERba3Db7f)X2r+XS|u00_+4`}!u8lp2LVnEHX~RD#?RLXQq@{HkQJ z3t%Xl0aOx3#g}b7RwY-no0hz_enj&?HC&Z=V?;)~qRPN<=u^U&@pRrQo%!Rb=Wz{uGOKX6(y}pd5h-nvcNPVnN zud>e@@cQZ|QJ|JSHnMKK{&eWcHy)i2cY=NhkEd>c5q^Ii98U%_!hZ!G3V}SHaf~15&4SQg zH7hv<^bEy{^a)T6gw2dSqH3!ieTcfXdk@vDHc6%TsbxW=4s8}-Zw%s9DKF-6R7MB} zBx%0v;($ulmH6Ij?jpTIcJlvwshSM{cz{#~U*G)`-u_)71w`mw2(&pjChu#JAURn; zo-+37EHP@+PEA{5l3^vqo@H4Up4)CAGgw!!5kwZmVxyMhmpwXR)HH}ckQQ8Mv?>K*9&hMVV8g3U_S>a{}T#Z8oRswq(?asnTG z1KT)l7y{g{V@8g?=3SNLR53wqAWpPrc0LKZfqxw?9CoFxHgbvkuMc6g)qep55BRUR zKsUjI1#&u803i62kNFrQsL=i-hjZ+ z?A&cTxP6=&Ndbdva6|lW%8v?>(&Yn+!DPG0Gl7uoY6l98ZNP+rLr{ zMwW0rV1ZnBmThkD7bg11WB~j|`UElRwl1dH0xRTR?vVetUQ-$XrH(y3x}W@g)2VT= z{r)6@v-lTktYpFVyy~-Db!*Qqe!#YpcOTc@yS-t6-QJ76j@9GT@10Co!@#<&dzisP1BS4J)6wLiw+!9QM^)jE`!T-0Z!teme z&?8~s{9O_KYv_I=U=0U;Io*7>FHpdQjg0{4_1f3`_Ll1+0Ag4il*H5i+gu&Mkov)q zIRD_WE3m*?&gH+UBy1Yk{!f9aBCidb@88y*l?q@k*&%Dvf18REunmJDBu^A?Uyb{} z?sIxi!}6}2{PtUqZb?Na40~h_izNDmsQvBh6)#{B4|JGu|MnDMyb!GT+DOj#^fnJ+ z`&twNB#<$>_Rj64s{>$NBuGv6_AMJ2Enf8l5{P!T`;6;0)uFJ>R$*-a8>bGW0owBI z*V?Gwl9>h(U=2*p{XeK~F9K-j1z4t_ARqMGGu6cb+ub^Z#54R$C&9Nc{&qx^-`;XK z0k8(=H_Pd_mT>dY8D_7Iuts*NhXQXJw`rqyW3p&EYRx{=C{_Trpf`A>_VZLn- zV29L72uy<)6ql24&EygcO!Q>`X)V^@x9iP|;#R3sYCqz95C7-f+G}j@7VL+tHZ|;N z_F@m{3IZxw00^NJn2@pDa*GMbj8;>ZauWODL6;c~&XsXngN^OUOvNbrimA>CO!c%1d+ahR zS(vb!wvj1l>$iHN*s%h`PHlKI-`sNhA0@mC?q%k!=pryK-F0bUP+wj~;Ct2lpkcgt z3BSj2ZP8e9-=!K!YwufM`~SG{;JvlV@Banx2N_mA!DQgnn(go6CfJAlOyDc^^;1hB zMhE4%WcwW8HsC3i_#yFk)k@$>1cZYO$cY}0? zD2;S?cc=88x9fS{{VvwpKfdpm2hPbHV_bQj*EPmK`--qVE|Cvlg}oL0XV*;FBrJf+ z-Iufo{zs{QP0E`doP9Bf;GUiSwo?T3G*!X#&|u|)ErsWuP8B&YB?1J8rR_kjXNAfg zVQ|zK{jsI;OzQX$ccS*i%^%21FW!4+s8DVsU6W2}-?)?J1pdxt+4wc9Jr#&~)}8u| z90LL2H&0bhyZ{bY@x#83E$FNQlmWD3jPXL!TL*KOJx& zx6^}nke(SbMD^!pG8JBF3=He%sy>tUi+84cpQDpXLMA4tHooK7<2s?gLv+~)^$j*$ zk2=(ODuD^voV&}Y$&Wg$1J=rPS;K+)cdvQ@=?uWcXgHt&{`a^QsR~ityb+fXvMrVw z3CWNr9Oj*XB2q)-#K`Rsm%vCCIM6qqZzLVsAvtBj!T9{6v)}~ou3!G&g#Mp3$*Dm) ztWeBPk|FMwCrMjl_8P5p6*R^nmSO3G4k^mxw2IBvF@ji+Drs60%LLq8ZPsE*)%kvP zNZQ0DSk$osM?Uy?K+kl7wi5tot7%n8zyF!!(y)3bQFqhQiQz~iNxXqNg@hpCtcP#p zZ+GdKFtZWED^--LVv!hzrWtSS2vLKu@4Ori^>PIO+Ct6;e8$D2^tp~k4uNcE*DTuY z_iPeP#3w+A|9PK8krGGN^uCMGeWyw}uBxR6E_)7t-{;_bK@iTI{q zQ{gvY?7>YM&yb1HWz{laa}tbb&xupq)KVDM4BfpVFGU+1(TgFUu#5_ zOgf`$U@fpu!#ghP!Od*+>QZ_#UZQNSs)p$Z*Cc%T%X%cVn>5wepBQID37^!}7e7qB zY)Lc^B|M5Dqsj+Vhdm%nK?%uF-qZh&Xs`(8CD$&X!Dp9bWSRpiGaT$)wK=OP(&oYu zEui>jvTNhKAH!fUG`&1978my};?$+3>ah@=$vLphVB*{NcRe-r<9>2W_a^0T+{auu zpZcm|e@GV^RbNg;yF1K@z+M!Iapd0&HNa|uo=%vZU565?PPX%YEn`JIT9=^<*&S&P zxR}4RKJ{pGsCn&#Bexn?Hicl;TlnrRD*5J#Q10D|=M$iA(WeSmGfUj}*Y%x8v7%;b>aE$mQtX#|e&~4#-UF{q_ zvhuNMfO0=HTIY~QULsh~lmH`0WHW9yqgT?}vrt)|)RbWT{KlX&UVjrCvu?aWVt+t( zK(*gewv9GK17U=m$kd17?x3aYpzhG9ulc-gNo9p1v+QesaI57~b5et|8A_Dj1Y1Lf zPLp>+piafOZj|b6tUdRM4UnPxT3|v-zX2Cjw77>h{OQ^LZzuJq0Rs5v3>4;P;J>$p zpWfr+@O^3XGERGHyO@>xns8B>wF%?RS+rhBS7cGDo(A;f#AN-O>eZd7vu3Dk3|X$j zK&gT36hh~6;mx=;Ik|wBI}GjvlDTapq>{C*qgBevdgVzi_}>Pun7))qM>}%t57^&N zUh`&IMwz9$Z&FZHZH;&{8SwL|m&Td5XWYnalU=;a&0 zxX(%4B?=_lei79kl!?-_G_85Kog3?2Zm@jB3kn)c$F9>LPO=;y+lgY|o9L&aBpwIO z%U?ONrc5Rlp6I2&`tJds|G_$o;9eiQ`)8?Vm@HEf@+x9y)*@!CDCjcN*J$`b;>g9J ziAJlcd&RrzJRWZVYrldni@(FZet;$PtW%RngU)v?D4)OoXx{QuEujr?tl<5ms+H3T zj(Sm?!GYoD_O_~HKTE@$T9II#k_K{y{IoGl=A|gq_WSNx%kET<-;Ydga-PN+tu_x( zvI7a{p5NHJ295W^HJb%LM41dCE|5nsPd_etyK?nxn&-84NUClb+K72%1YNVxQNF

@)t{djwqwzjAr)&F?^X?$7p-!ynth~qIyR77a zcGYfT?Z!zfm6-hdodd~Qn>$5@0iXkT*kZWl&`>8Q0cxAS!qc{k^{eVBT< z5%-y#dVA3=Lb0fO;au61o6T7aG*yk`B)%OWg_n!HWE~l-fC>PJjyKOwa=w5h0AnFW z29f9K+8XfCS$B^q7!IIYo<-dPaXwyXkj!RK+88y8gSMq3A>Lg8+p%QQH z@mn598XCvR!7M4^IE($g3x^<3S|rtS5X+GGJZc%w1OtU7c6;B>1}S6IFt$in4d@g#R;?QgsfF|;6T`-#Lc9HR) zpELq=1>TECLJ#{~SGE8wvID@2dt^zmc`2HNw$KdcX@m4s!jG%ek=2mI4t{Pz$8cs6 zOxfzpzA#iC8rybF-7o}LNSmR`_E=uSydlQjQrh39MM_gFuu6kYn8on0HLv>iHirsy z;%05PXH#st(cyY~NubgWkG|=RVN=@aF@Sz0v*gq)_xG1HdMt2{Ookw|=I+QCwlA(t zK9*xRnF{=P`J4@)y$6uOZ~Xs>9Dhas3(Egae=nkiQ!>B(I>=rDQ?7BP;YY5ubC2NL z-NBt|YwX^3B9AGYreT?RaC|tjFgDw4R6ibIwjOejVbAr{Dd8FKXnQ@xt5QXy>#2&+ zVlbUl{Y+5ABbMMM+q`Jc&u937lSKFFtISFsrjsSU&Ev1@{knOgv%R2bB z3(I7o>v;`fVW`ys7D(R*I;v+#pFh(WT<5?Uj%t2Jp2cK49?{b}Bwawep&B&?jan`} zLPH}zu9At@yUUM(wRhR|aBO!sr>cC^pTei7da^OgmswupCi1u`Te!KP!3d+cgwfzJ z3;Pabm=f0A>!3d!6P+y=qL1{cXSf8zW_}>dJI!mD3@`jbT)pz@b^oXWkxcJ;^>%k)SGoOxSg(VF9Cq+E%}FMHOLp-0xL zxThUzl}-fyI9pn%*!&f-)!&(s&_vH+)635|X~|6(VeZ++Rt0REHMQ#UQ)>~w#+^2h zv=kDA5(sm&pl(U*2vLW#m?VH#ES&k+}YydDt+FCb>c4|O$)8du2=HDNAwWiv(K7KDaTN$wCoZlvL zOa$P(ZJlkSp4oT{^R}d=<3+&7Z29f6z(ZDn?lA4;*`aL-HYkQJk_d}-0kQkGd@dG& zgxPc4+^V`tn+O6Y#-v16*t?CHMZqfG*P!GMwP!#MC@GeM*=#bA=(9*-#{edao*-0H zc*B4I`N6eIm(66phkQkB=iRi3jUXIruAZa1!>y|0p?S`EYx9ncqwRdId1E5?Z^(Ev z!?Qy@{RfRYeQC1oYg#V%`FQ;#t_K?y+qS@oVoCWCGANoBjjX&woMeZOlb2uAD{&-uS@`RiKUsO+D{vcD4HfgG0iv z?$S+*f@+27Oz+U*eDjNUG*Qvnk@hAZFye?3%0A1Cw`XhhO9ZDLD$!J`X~{HisRU8s z3K8OkY6!`EaIz`R;#i(mSuykq(5};>dfVoJ!EoyIA8wISJd=ng&VF(hS;s;>_IdED z056A|U?d^~$J49N0?+-!lSP_3ux-!F?=^NIU1A1t5Whq4Mpg0&sA?mvFuA zbKSVQv!o{JVoq69bi&s6qzu*4=n(71Wt_1jGwLUg4UU_Gl$$9IA8)Vw%S7Ki9cO*b z++*w~TGge8MB7JzMyWU~TiF_OO-g9_a(cEMy(~Z(&&Tb!34{HD_zA$;7#FU&)!CK>h-z+6TT2ZBbkq&dPc&gs!Ki=D9R7JR&ra zPKVl4`@*e=71s}rvQu?L6Za172_9rM|?J%X$}E}V^LJTQCua?9B5>)JsLYLCv# zF+*2B%h;lz9A=k*c+F(}NZNShU65LTgq zTQ$57$j^&nmLR?Ty_AL>ugoe<7e?ruBNK3OZT6Y~l0jPGAAH`6Uj!V${Gwu>VFEi$ zF#L3uyKX&c8oYNZw>=9U!{9hPc}U%RH-z^~#mMcXi2J@@N}PNlD)`eJ#;%2B*o2$& z*00$!AWWw%<`aw(q2BoiP~URiDU`Wg{eo;1o+lB`4l8pGK*c5YNW}tX*L0>7*$_us30|un$(q} zFBNH6wknirJ3|A;y@`#p1HB}^8$W%i>fu`xofE$eQ0cfEqsGy(tBHoEH%vRr9r`23Jr~=UGM%=rfG52A=t`j`?r7o6RGxUU*C6i7BS@ z^f@U6?@Lt zI`-R*=C8I7t#!z~UmtgMmLJO%Rw-PEzcN2dg208kz%uOidwMpWVL;BeU<~M=@Uqih zx53o>VoTI^6NtVs{mFB&qTz&WAFt*-B&jA86jd3Fmp>OUU-_(~#&~QR4mlLR9v9g? zVI)idc84Tu`MZ-%UBb|J+yt^ z7mza@d75JNR)1Rjsikkbx68%NJ|gWxpE4;eZgH)r&7(S8(GoQHhn>U(u|JzL+D`M<^BOmJlepkg10Xv(+$y{Q9*)LtxG^##(@`tUuVWGXXfB^H0yl)wp)z6_9u{EHL zGm&BczbOL(`i%H5ZJ_=JdV??FXO@+L0~P%GZ=YAdTg3syZ0$0)K=bz}e*ir;FB1H# zIICe<(c#5L?Tr?Wey>4d{&irF1CXgpa`G!he(wH}dA0 zYQ6Za!Bv8kbhhhfPyWcwe;5+dpY9JqesyK5=kP4x!f!|eVq4m2d{*q_CxkvA8bxT? zfXI*}9f9~uZBQovxYHQu6#;lzS~kV?84Ud+w@zaS?@p{g_JVlF!N~Qu1+F~JHX}OG z1lK=!=zSs;qo&j)hNUABa})#aj=#=ZA6rBjTO@%TtAmF{{Q4%*^YyuFM&lq;v$5QyJoF7bYHL zpW%L%el7w%y*jx#Sa{w}qM2^1ZpA}q{%75;iwar5`2Tw(G(2d(6AZkG+|Xwa{=CND zK$;0R>VJY!t|~W2@a`{xFEp$rZ}>=Cf5i66Ba_?;by1H0YgzssRM-_GHz7JVv`}Xu zXgY3!nOOA5I@<;W;%ql!3*-D?^`gU)vS3(Fx2`_$5I5t5ReCoBH4xp)ZJ= z0HqvsirGR#z(L+xv#ud9B=4+|H@Hac(k`Q!xp4bhL#cfHr2gaFxx2~jAwn^-&#|K!{i04^ikryEUj(@bZ|VYikF-GljG^=KkYW~O z#=$5+oQ-C}ty?2K3Ip|%xPiRhd+lpep-q(!KQ!t4w8y z9ESg5-=uFKc)k+q_$9Q5ALTqfwh(^*+FyhOnGS!y>|V4}mjJq2K>ry0s3|Sh)?=+0 zIJTVOCRT6g9)5JlP5X7q+32DD=>vC+uOwq1{f94=^oTF{`se_w7Z3D`1Vz7e<){K| z{U-yYj50P!(@%yrg%sdtaui77w!~nDzMuq64?feQctz3{b0Acq$V?UhN`p{@DXXJ=H5I@Mno@US1wfIf(Ku zK~RwjN?<)9dmkvLsP)LmKy^-u_XPu!_{aiDc?#pltA_9I3dm=nBC|#Zc=I12iTDll z#Rn<3P%53}*7R~~ZQ&=!WvK5g0%wSjeYoCF9*+WP%RKIsBXqmWUAJ17%Vr09~gIF$F z8vI#A;Im_SOXk47>cGz8hcPukE?>E!qvv0I-q04{&<6(xSnrLCLCj9c_o^UZG5?|g zKQ16JB3#qFJw!DGhpvrDj;Bq-U{IY*TresYC^9hN;LmT7zlFTbLxMvelk^d!iv!G? zpPM+dEHv78PJp|EL>X8EKQys!vBvi~cr4M?H^4W0N+>O{{wZrTg=9c{yco9ckdZe zV!wnLQmRd8h$ndX)h<2}m6w%FRhf~mf!D>l;S+Nc&iv2iZv>rVPG|dz5P&s)Ak}^O zJGe>m14zKM8f^A35a&2=%Ki9AT7GW6jI8<>3ByCKf>+mh7%cAk1Drvjztql!r)Jyq z1TH9{1;&;BC0|a+`G{1~d!etj^4UKa;J{Ig5B_UY2$7dTJ=mXIp*t7Hq~RI*9HpKmbw_(`O(G-xYydx>J z48f_f;LlS-eq;3+lWds7MOdYfx5nm31pfDby!5}z;>RG%?9p5kQd+$|y0mlOrv^M! zM1s}H1tTK~AA~060yd!)Ry2EH$6hpe)JO}o_hn)E*SnDxLy|IovIw5{hVCZ%k}AIj z*b%b4yl!ZRyzG7B)k_ zh&BLY9fKhLo-fst&T$N>5&hWxDYV1#G7?}nL4v4cMy%MUC<8!?x1SJyi8ipp)%Wqq zK%)CQAo>dn{N@Kh)2Vm%1POct8L=O>qn1Tc_R)V+kPq4j!zVrSR|44DGRkQ1U!Yl5 zDYPuP1@S?q>A-~j&>`oh3bhZZC+bu--ZrgtkCk9JfD>eOEjlYg<1ai92p9sAy{yS$2DZdbk|svs+$t92MFn z%ncmxxgU5qP*mEm6S+`q(PuoBi_Ab5sIfg{zB1xlpwGiZQaqr4nOsboI;xzVHJk;m%#a&*6W zDYGJJ1;d21Oz$FH{uRCkK8jF!0O8I5Pt%btp1Rzf?= zPmioIWtXuF-EfUzvkmT>NZSRpYn{5E^veO=fBa#njS2cW^cFVNamZanyL%}2*zEV>?;d($;d=8eQ0c=QEkU-W!(?E%y`r~ks9l+-( zydka>+zaFtO@}KLL05Az);H}>IgkvzqQ3JiXA*Lw6o@P)vV0G>e672-&a(ZnEJ;)` z+XJVeqHVU=^a_*oP&{Lc7>^P2?4{a@#TqS<^G9E0Fgrsc20R}J241yVr1g{dVQ2q> zTh7j^P8iGZd^iloeH{!LH3iEA@$s;(4>B;j7-PP+=nto_u;V{+{VW4r>y>VN|$7`QucZh(yd z9vq#Z)!3`T6PtZh4msbr3A`iuOFU$sT{i6`z6{Wb+h9FCTv9!Z<$Q|9*L+D04pAW; zmN&r){hVb&_1m+!c@dM9_hzz0oFaQ8t+MxHy{nH68z^nsZkuU%5`fFuiux zI8j;-`*2Hz!=l4}8H=kfdwF(*uou98F?F;C^nT&QDTooTpbF3P!%GpQ@E zGK;$q$8wU!#tG^dQyG2Wx@LzN##%19c7DSVxyJsAy8M#N^$ls!x#y7ZE?TkETsv^2 z;9hsU(sk>tL;~k~aawJWcprWRvXL)ZgQ(>I?7x-hJq5>9cm^BPKX!!X3(o58n7CI& zLUMF*f1I6o3~h$d5_(ucI*q*G;kaIByy6EJRbS<1CfIMA;B%dBVVaulGe#^kYoq|n z1{jwJI36RLyqJ6#>O^+lulv-T`BZ4Mo6S;aPgarlKA*?s5pm9L}!y#9k9>{_+ zB_!*+43wK4!MiiTIM8PyACUoIbozzL!u|Y%C6Ard=2wHxfdtjIEL@)Kod~|Cnk?n; zC$Br!3Zd(TxUu-7x-a(+5k>RA)i2JUJgN`rA8hDx$MgK@=o&3s#%fZerzC*feTXR9 zeY2BL35FHUM0`t8-T|q3fCrLO0E3B3Tx`yOr62*nBNM|t%p(o(3dFqp8_R)O2W92- z=6TfISL*4PDST~ACcvNKxScFok>pq_(I^uABdExaD;4ce*_^G}O7nvLCpJ3ArzAmf zEZ`)Ff$GgC+bRy6ik1b&;U3%+1CN6Xzrz?`1!IH$*hNu(ZhLV%rEDiT7aD7Ja?zj^ zS2`frn5PnvwTFLKUn*izCFP@cqVZC-hqhLs$@Qu67PDp1?6NpA zvfQbCNfdhZ{wHheW9FTbQ-eu`LS=}&Hk8ZSx~;4@AHtU&e=E+5*EyBZ)|My->FU2S zNqyYGG<(fDY82#mHlHsdpI>7NSw>lU_HcQ<9NTQ=s+r<{0#4WfV|`$X5A$r`sxFK2 z1LO>l7JtAdqY<4s%E5sT7Z5wbV{oEh!q#LzoQ`+wUp5+w=5)PKKqBtc$a;?X$uFBl z8Ia)`p7$~vEmo(bn=kbx7z?TqXZEN8fW#@md~r#LMR(85zN72Mom{zb^$pJlcR!Fu*(g@GqhW9t-ksvv9hz~qyXwa(-S8`ka)@kEaJQ(-p^!r}UWx1u7-gxm#|N`(fkZ zSD{nF?<6n+bhQsA#t7g!Z-9VCz8*qTKnX}mfCPw}nJWrOK&^t?NI?)-ekV5Yg5Pm% zOgs`~A2|AvZWE&bnREOQl4m1gUvFJBb?m}MRCKaj&h~m zEHursePT`^T7mgW++kFOgy-X>r&8u8xZ4OA$|kl#fnT6z4#G#I=Io7%WtrEG)m%1H z&0n5A?QuDmOQg?K6U4c3)H6%|v_e|#>dDJ!upmggJ7W^A>XdwF%h_p_rB{2qIE?BW zh6y(gb-iew2UC64zWkJlQRXsgeUOsBJ{r1fGM@WJ{9RfA#pCZ^@FLQb^qUn=m)L2K zCoOu%?oZ)Ul-)16VN-5p>Y%N`_yA3x{H2#&vX4i_YpkJP&@uv{;T zMi@#AE2!hVyL@OnhH_dGn1z!P(>k{+QvAZD$Q;5{qSKwz?vV3qyRG44Li-Ja!17eyQ< zI&>!%SYL;EF#&(b8aNTIK)mjhJV_J>vnO~Ra58M0meHVxCpQM;>ePzzR4{Y@EeOQh zwh{3}_g}DKdG$*VkxaSb(yrUER%MNMJK}rXM#*LqD{NtfB1PFMP)|48I{n%Otj$UH z!b`LfrGqU@#)m4&4R&D?HgiO2k2~C~J6wW?coR+NleF4OWv+MDzYp?i-0ncNuKPgS zLM7$9=G0e7YycQY67KpX3@p2eZq8lTlT5UJvNw~<@}r}h33J|e9I3n>o(o2?TI~2S z1>gTxPHYrC6l1Utby&XoJEom{?Lyzj7-UnzZ+S_0#8FZjZQfV-T$W@TiO&2dCK`<_ zleEfyM-2~bwh0d1J!xz?F( z;i%#rI&}5#f7L5gzwA(APRM-P%SEJ+vKmvCOWK%T(APRwe8|%?G?Ht$zdKOTUU{`2 zr#TZ|EK%t*(dIxQ>@&Kn=J92dT3V;o|H{UBhLeMg6*He2t54R_Ohs4x8&_(xyOr;0 z!>1?o?p2g&hpZFJ`Xz}x(2%(O09MX+757#(??LL|wEI#3fePvu@X=ToYW`;YJ%!V%hNU-@J$RJ^|<7Rok-MJRrhHeggwJC%1q1;#2<@um>+~ z`ItzT&xaLe1)bMR_SB}y{1ViJs9?Y_eGrU**K|5maUB)bUHol%Wtriic^#0Vk$aiG zf~5E4en{k%c6&PP(iG2RWy~F^9kZG#w`SK4H T(x0Glsly$%)GT4DEz$0ZH{1+= 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

  • $WM{+V-zZ2lJ^$_ulyjNe_O5SusZvt9CI`g6 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%0AJGBjbH?3n=;7B#3a+srq2QE*;PXO<`KFL9mRr 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 eQvDy?gz -## Requirements - -No requirements. - -## Providers - -| Name | Version | -|------|---------| -| [databricks](#provider\_databricks) | n/a | -| [google](#provider\_google) | n/a | -| [random](#provider\_random) | n/a | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [databricks_mws_workspaces.databricks_workspace](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_workspaces) | resource | -| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | -| [google_client_config.current](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/client_config) | data source | -| [google_client_openid_userinfo.me](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/client_openid_userinfo) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [databricks\_account\_id](#input\_databricks\_account\_id) | Databricks Account ID | `string` | n/a | yes | -| [delegate\_from](#input\_delegate\_from) | Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com) | `list(string)` | n/a | yes | -| [google\_project](#input\_google\_project) | Google project for VCP/workspace deployment | `string` | n/a | yes | -| [google\_region](#input\_google\_region) | Google region for VCP/workspace deployment | `string` | n/a | yes | -| [prefix](#input\_prefix) | Prefix to use in generated VPC name | `string` | n/a | yes | -| [workspace\_name](#input\_workspace\_name) | Name of the workspace to create | `string` | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| [databricks\_host](#output\_databricks\_host) | n/a | -| [databricks\_token](#output\_databricks\_token) | n/a | - diff --git a/modules/gcp-workspace-basic/init.tf b/modules/gcp-workspace-basic/init.tf deleted file mode 100644 index b07d347..0000000 --- a/modules/gcp-workspace-basic/init.tf +++ /dev/null @@ -1,24 +0,0 @@ -terraform { - required_providers { - databricks = { - source = "databricks/databricks" - } - google = { - source = "hashicorp/google" - } - } -} - -data "google_client_openid_userinfo" "me" { -} - - -data "google_client_config" "current" { -} - - -resource "random_string" "suffix" { - special = false - upper = false - length = 6 -} diff --git a/modules/gcp-workspace-basic/outputs.tf b/modules/gcp-workspace-basic/outputs.tf deleted file mode 100644 index d6b170a..0000000 --- a/modules/gcp-workspace-basic/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ - -output "databricks_host" { - value = databricks_mws_workspaces.databricks_workspace.workspace_url -} - -output "databricks_token" { - value = databricks_mws_workspaces.databricks_workspace.token[0].token_value - sensitive = true -} diff --git a/modules/gcp-workspace-basic/variables.tf b/modules/gcp-workspace-basic/variables.tf deleted file mode 100644 index 5e94a56..0000000 --- a/modules/gcp-workspace-basic/variables.tf +++ /dev/null @@ -1,29 +0,0 @@ -variable "databricks_account_id" { - type = string - description = "Databricks Account ID" -} - -variable "google_project" { - type = string - description = "Google project for VCP/workspace deployment" -} - -variable "google_region" { - type = string - description = "Google region for VCP/workspace deployment" -} - -variable "prefix" { - type = string - description = "Prefix to use in generated VPC name" -} - -variable "workspace_name" { - type = string - description = "Name of the workspace to create" -} - -variable "delegate_from" { - description = "Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com)" - type = list(string) -} diff --git a/modules/gcp-workspace-basic/workspace.tf b/modules/gcp-workspace-basic/workspace.tf deleted file mode 100644 index 262d8a0..0000000 --- a/modules/gcp-workspace-basic/workspace.tf +++ /dev/null @@ -1,14 +0,0 @@ -resource "databricks_mws_workspaces" "databricks_workspace" { - account_id = var.databricks_account_id - workspace_name = var.workspace_name - - location = var.google_region - cloud_resource_container { - gcp { - project_id = var.google_project - } - } - token { - comment = "Terraform token" - } -} diff --git a/modules/gcp-workspace-byovpc/Makefile b/modules/gcp-workspace-byovpc/Makefile deleted file mode 100644 index 653039d..0000000 --- a/modules/gcp-workspace-byovpc/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -.PHONY: docs test_docs - -docs: - terraform-docs -c ../../.terraform-docs.yml . - -test_docs: - terraform-docs -c ../../.terraform-docs.yml --output-check . diff --git a/modules/gcp-workspace-byovpc/README.md b/modules/gcp-workspace-byovpc/README.md deleted file mode 100644 index 0fbaf40..0000000 --- a/modules/gcp-workspace-byovpc/README.md +++ /dev/null @@ -1,75 +0,0 @@ -gcp byovpc -========================= - -In this template, we show how to deploy a workspace with a custom VPC. - - -## Requirements - -- You need to have run `gcp-sa-provisionning` module and have a service account to fill in the variables. -- If you want to deploy to a new project, you will need to grant the custom role generated in that template to the service acount in the new project. -- The sizing of the custom vpc subnets needs to be appropriate for the usage of the workspace. [This documentation covers it](https://docs.gcp.databricks.com/administration-guide/cloud-configurations/gcp/network-sizing.html) - -## Run as an SA - -You can do the same thing by provisionning a service account that will have the same permissions - and associate the key associated to it. - - -## Run the tempalte - -- You need to fill in the `variables.tf` -- run `terraform init` -- run `teraform apply` - - -## Requirements - -No requirements. - -## Providers - -| Name | Version | -|------|---------| -| [databricks](#provider\_databricks) | n/a | -| [google](#provider\_google) | n/a | -| [random](#provider\_random) | n/a | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [databricks_mws_networks.databricks_network](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_networks) | resource | -| [databricks_mws_workspaces.databricks_workspace](https://registry.terraform.io/providers/databricks/databricks/latest/docs/resources/mws_workspaces) | resource | -| [google_compute_network.dbx_private_vpc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network) | resource | -| [google_compute_router.router](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_router) | resource | -| [google_compute_router_nat.nat](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_router_nat) | resource | -| [google_compute_subnetwork.network-with-private-secondary-ip-ranges](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork) | resource | -| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | -| [google_client_config.current](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/client_config) | data source | -| [google_client_openid_userinfo.me](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/client_openid_userinfo) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [databricks\_account\_id](#input\_databricks\_account\_id) | Databricks Account ID | `string` | n/a | yes | -| [delegate\_from](#input\_delegate\_from) | Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com) | `list(string)` | n/a | yes | -| [google\_project](#input\_google\_project) | Google project for VCP/workspace deployment | `string` | n/a | yes | -| [google\_region](#input\_google\_region) | Google region for VCP/workspace deployment | `string` | n/a | yes | -| [nat\_name](#input\_nat\_name) | Name of the NAT service in compute router | `string` | n/a | yes | -| [prefix](#input\_prefix) | Prefix to use in generated VPC name | `string` | n/a | yes | -| [router\_name](#input\_router\_name) | Name of the compute router to create | `string` | n/a | yes | -| [subnet\_ip\_cidr\_range](#input\_subnet\_ip\_cidr\_range) | IP Range for Nodes subnet (primary) | `string` | n/a | yes | -| [subnet\_name](#input\_subnet\_name) | Name of the subnet to create | `string` | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| [databricks\_host](#output\_databricks\_host) | n/a | -| [databricks\_token](#output\_databricks\_token) | n/a | - \ No newline at end of file diff --git a/modules/gcp-workspace-byovpc/init.tf b/modules/gcp-workspace-byovpc/init.tf deleted file mode 100644 index 103a33e..0000000 --- a/modules/gcp-workspace-byovpc/init.tf +++ /dev/null @@ -1,22 +0,0 @@ -terraform { - required_providers { - databricks = { - source = "databricks/databricks" - } - google = { - source = "hashicorp/google" - } - } -} - -data "google_client_openid_userinfo" "me" { -} - -data "google_client_config" "current" { -} - -resource "random_string" "suffix" { - special = false - upper = false - length = 6 -} diff --git a/modules/gcp-workspace-byovpc/outputs.tf b/modules/gcp-workspace-byovpc/outputs.tf deleted file mode 100644 index f544b3b..0000000 --- a/modules/gcp-workspace-byovpc/outputs.tf +++ /dev/null @@ -1,8 +0,0 @@ -output "databricks_host" { - value = databricks_mws_workspaces.databricks_workspace.workspace_url -} - -output "databricks_token" { - value = databricks_mws_workspaces.databricks_workspace.token[0].token_value - sensitive = true -} \ No newline at end of file diff --git a/modules/gcp-workspace-byovpc/variables.tf b/modules/gcp-workspace-byovpc/variables.tf deleted file mode 100644 index 4565008..0000000 --- a/modules/gcp-workspace-byovpc/variables.tf +++ /dev/null @@ -1,45 +0,0 @@ -variable "databricks_account_id" { - type = string - description = "Databricks Account ID" -} - -variable "google_project" { - type = string - description = "Google project for VCP/workspace deployment" -} - -variable "google_region" { - type = string - description = "Google region for VCP/workspace deployment" -} - -variable "prefix" { - type = string - description = "Prefix to use in generated VPC name" -} - -# These three ranges need to be computed based on the workspace size (cf documentation) -variable "subnet_ip_cidr_range" { - type = string - description = "IP Range for Nodes subnet (primary)" -} - -variable "subnet_name" { - type = string - description = "Name of the subnet to create" -} - -variable "router_name" { - type = string - description = "Name of the compute router to create" -} - -variable "nat_name" { - type = string - description = "Name of the NAT service in compute router" -} - -variable "delegate_from" { - description = "Identities to allow to impersonate created service account (in form of user:user.name@example.com, group:deployers@example.com or serviceAccount:sa1@project.iam.gserviceaccount.com)" - type = list(string) -} diff --git a/modules/gcp-workspace-byovpc/vpc.tf b/modules/gcp-workspace-byovpc/vpc.tf deleted file mode 100644 index 31e8e80..0000000 --- a/modules/gcp-workspace-byovpc/vpc.tf +++ /dev/null @@ -1,40 +0,0 @@ -resource "google_compute_network" "dbx_private_vpc" { - project = var.google_project - name = "${var.prefix}-${random_string.suffix.result}" - auto_create_subnetworks = false -} - -resource "google_compute_subnetwork" "network-with-private-secondary-ip-ranges" { - name = var.subnet_name - ip_cidr_range = var.subnet_ip_cidr_range - region = var.google_region - network = google_compute_network.dbx_private_vpc.id - private_ip_google_access = true -} - -resource "google_compute_router" "router" { - name = var.router_name - region = google_compute_subnetwork.network-with-private-secondary-ip-ranges.region - network = google_compute_network.dbx_private_vpc.id -} - -resource "google_compute_router_nat" "nat" { - name = var.nat_name - router = google_compute_router.router.name - region = google_compute_router.router.region - nat_ip_allocate_option = "AUTO_ONLY" - source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" -} - -resource "databricks_mws_networks" "databricks_network" { - account_id = var.databricks_account_id - - network_name = "${var.prefix}-${random_string.suffix.result}" - - gcp_network_info { - network_project_id = var.google_project - vpc_id = google_compute_network.dbx_private_vpc.name - subnet_id = google_compute_subnetwork.network-with-private-secondary-ip-ranges.name - subnet_region = google_compute_subnetwork.network-with-private-secondary-ip-ranges.region - } -} diff --git a/modules/gcp-workspace-byovpc/workspace.tf b/modules/gcp-workspace-byovpc/workspace.tf deleted file mode 100644 index 0f7c7a0..0000000 --- a/modules/gcp-workspace-byovpc/workspace.tf +++ /dev/null @@ -1,20 +0,0 @@ -resource "databricks_mws_workspaces" "databricks_workspace" { - account_id = var.databricks_account_id - workspace_name = "dbx-example-tf-deploy-${random_string.suffix.result}" - - location = var.google_region - cloud_resource_container { - gcp { - project_id = var.google_project - } - } - - network_id = databricks_mws_networks.databricks_network.network_id - - token { - comment = "Terraform token" - } - - # this makes sure that the NAT is created for outbound traffic before creating the workspace - depends_on = [google_compute_router_nat.nat] -} From 39954d962a45f932b25af6fd3079a5e45401dcdb Mon Sep 17 00:00:00 2001 From: micheledaddetta-databricks Date: Fri, 22 May 2026 13:51:41 +0200 Subject: [PATCH 14/14] docs: refresh GCP examples and modules in top-level README Examples section now lists all four migrated examples (gcp-basic, gcp-byovpc, gcp-with-psc-exfiltration-protection) plus the new gcp-existing-vpc and the corrected-spelling gcp-sa-provisioning. Modules section now lists the modules/gcp/ tree: the composer (databricks-workspace) and its five submodules (network, private-connectivity, account, dns, plus service-account and unity-catalog). Co-authored-by: Isaac --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1c66db1..ac63cad 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,11 @@ The folder `examples` contains the following Terraform implementation examples : | AWS | [aws-databricks-uc-bootstrap](examples/aws-databricks-uc-bootstrap/) | AWS UC | | AWS | [aws-remote-backend-infra](examples/aws-remote-backend-infra/) | Simple example on remote backend | | AWS | [aws-workspace-config](examples/aws-workspace-config/) | Configure workspace objects | -| GCP | [gcp-sa-provisionning](examples/gcp-sa-provisionning/) | Provisionning of the identity with the permissions required to deploy on GCP. | -| GCP | [gcp-basic](examples/gcp-basic/) | Workspace Deployment with managed vpc | -| GCP | [gcp-byovpc](examples/gcp-byovpc/) | Workspace Deployment with customer-managed vpc | +| GCP | [gcp-sa-provisioning](examples/gcp-sa-provisioning/) | Provisioning the identity (service account) with permissions required to deploy on GCP | +| GCP | [gcp-basic](examples/gcp-basic/) | Workspace deployment with Databricks-managed VPC | +| GCP | [gcp-byovpc](examples/gcp-byovpc/) | Workspace deployment with customer-managed VPC (Terraform creates the VPC) | +| GCP | [gcp-existing-vpc](examples/gcp-existing-vpc/) | Workspace deployment into a pre-existing VPC | +| GCP | [gcp-with-psc-exfiltration-protection](examples/gcp-with-psc-exfiltration-protection/) | Workspace with PrivateLink (PSC), private DNS, and restricted egress (hub-and-spoke topology) | ### Modules The folder `modules` contains the following Terraform modules : @@ -89,9 +91,13 @@ The folder `modules` contains the following Terraform modules : | AWS | [aws-workspace-with-firewall](modules/aws-workspace-with-firewall/) | Provisioning AWS Databricks E2 with an AWS Firewall | | AWS | [aws-exfiltration-protection](modules/aws-exfiltration-protection/) | An implementation of [Data Exfiltration Protection on AWS](https://www.databricks.com/blog/2021/02/02/data-exfiltration-protection-with-databricks-on-aws.html) | | AWS | aws-workspace-with-private-link | Coming soon | -| GCP | [gcp-sa-provisionning](modules/gcp-sa-provisionning/) | Provisions the identity (SA) with the correct permissions | -| GCP | [gcp-workspace-basic](modules/gcp-workspace-basic/) | Provisions a workspace with managed VPC | -| GCP | [gcp-workspace-byovpc](modules/gcp-workspace-byovpc/) | Workspace with customer-managed VPC. | +| GCP | [gcp/databricks-workspace](modules/gcp/databricks-workspace/) | Composer that orchestrates network, PSC, account, and DNS submodules based on scenario flags | +| GCP | [gcp/network](modules/gcp/network/) | VPC, subnet, router, NAT, peering, and shared-VPC binding (create or data-source lookup) | +| GCP | [gcp/private-connectivity](modules/gcp/private-connectivity/) | PSC endpoints (frontend, backend, hub-transit) and restricted-egress firewall rules | +| GCP | [gcp/account](modules/gcp/account/) | All databricks_mws_* resources: networks, workspaces, vpc_endpoint, private_access_settings | +| GCP | [gcp/dns](modules/gcp/dns/) | Private DNS zones (gcp.databricks.com, gcr.io, googleapis.com, pkg.dev) for restricted-egress workspaces | +| GCP | [gcp/service-account](modules/gcp/service-account/) | Service account with the IAM permissions required to provision Databricks workspaces | +| GCP | [gcp/unity-catalog](modules/gcp/unity-catalog/) | Metastore, GCS bucket, storage credential, external location, and default catalog | ### CI/CD pipelines The `cicd-pipelines` folder contains the following implementation examples of pipeline: