Skip to content

feat(gcp): composer + submodules refactor, hardening fixes, cross-cloud contract, serverless egress & CMEK - #233

Draft
micheledaddetta-databricks wants to merge 47 commits into
mainfrom
feature/gcp-modules-refactor
Draft

feat(gcp): composer + submodules refactor, hardening fixes, cross-cloud contract, serverless egress & CMEK#233
micheledaddetta-databricks wants to merge 47 commits into
mainfrom
feature/gcp-modules-refactor

Conversation

@micheledaddetta-databricks

@micheledaddetta-databricks micheledaddetta-databricks commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Full GCP modules refactor (design specs under docs/superpowers/specs/). The repo previously shipped three duplicated GCP workspace modules; a change to a shared piece had to land in 2–3 places. This PR replaces that structure with a unified composer at modules/gcp/databricks-workspace that takes scenario flags and conditionally instantiates focused submodules — and establishes the cross-cloud module contract (docs/cross-cloud-module-contract.md) that future Azure/AWS refactors follow, with the GCP tree as reference implementation.

Module layout

  • modules/gcp/network — VPC + subnet + router/NAT + peering + Shared-VPC; both topology halves sourced independently (vpc_source = { spoke = databricks_managed|create|existing, hub = create|existing }), hub-spoke peering optional (enable_hub_spoke_peering) for Shared-VPC/existing-transit designs
  • modules/gcp/private-connectivity — PSC subnet + endpoints + egress firewall stack
  • modules/gcp/workspace — everything registering the workspace with the Databricks control plane (databricks_mws_*, serverless egress policy, CMEK)
  • modules/gcp/dns — private DNS zones for hub + spoke (gcp.databricks.com, googleapis.com, gcr.io, pkg.dev, incl. spoke peering zones)
  • modules/gcp/databricks-workspace — the composer
  • modules/databricks/security-settings — NEW cloud-neutral namespace: Compliance Security Profile, Enhanced Security Monitoring, automatic cluster update, IP access lists (workspace-level provider)
  • modules/gcp/service-account, modules/gcp/unity-catalog — relocated, no functional changes

Dependency graph: network → private-connectivity → workspace → dns.

Hardening fixes (vs. the first iteration of this PR)

A deep review of the initial composer found 3 blockers and 10 further defects; all fixed here:

  • Plan-time static counts — the PSC scenario previously failed terraform plan with 4× "Invalid count argument" (counts depended on apply-time values). All counts now gate on static booleans.
  • Spoke peering DNS restored — googleapis.com / gcr.io / pkg.dev are resolvable from the spoke via peering zones (private zones don't propagate over VPC peering); without this, restricted-egress clusters could not launch.
  • Intra-VPC firewall allows restored — egress-to-spoke-CIDR + ingress-from-spoke-CIDR; without them node-to-node Spark traffic was blocked in both directions.
  • PSC flag equality enforced at plan time — GCP's mws_networks.vpc_endpoints requires both dataplane_relay and rest_api; single-sided configs now fail plan with a clear message (flags stay independent in the cross-cloud contract for clouds that support single-sided).
  • No Cloud NAT under restricted_egress — the exfiltration-protection topology again has no internet egress path.
  • Shared-VPC binding works without a hub (BYOVPC + Shared VPC); honest hive_metastore_ip contract (explicit opt-in, no phantom internal lookup); dead variables removed; module-created PAT removed; terraform_data preconditions (null provider dropped); provider-constraint policy (modules: floors google >= 6.0, databricks >= 1.81.1; examples: pins ~> 6.17, ~> 1.81).

New features

Feature Interface (contract-frozen)
Serverless egress control serverless_egress_mode = unmanaged|full|restricted, serverless_allowed_internet_destinations, serverless_allowed_storage_destinations, serverless_egress_enforcement = enforced|dry_rundatabricks_account_network_policy + databricks_workspace_network_option. The PSC example defaults to restricted, closing its serverless exfiltration gap. Enterprise tier.
CMEK cmek_managed_services_key_id, cmek_storage_key_id (Cloud KMS IDs) → databricks_mws_customer_managed_keys wired into the workspace. Terraform principal needs cloudkms.cryptoKeys.get/setIamPolicy on the key. Enterprise tier; creation-time only.
Security settings modules/databricks/security-settings: CSP (irreversible — precondition-gated on ESM), ESM, automatic cluster update, IP access lists.

Reserved contract slot: serverless-connectivity (NCC) — interface frozen from AWS/Azure semantics; GCP implementation lands when the feature reaches Public Preview.

Breaking changes

  • The composer's vpc_source is now an object carrying both topology halves: { spoke = databricks_managed|create|existing, hub = create|existing } (hub optional, only settable with a customer-managed spoke; defaults preserve prior behavior). String-form vpc_source = "create" configs must switch to vpc_source = { spoke = "create" }.
  • private_link_frontend and private_link_backend must be enabled together on GCP (previously single-sided configs failed at apply/runtime; now fail at plan).
  • pod_cidr / svc_cidr removed — the GCP data plane is GCE; BYOVPC needs exactly one subnet, no secondary ranges.
  • The module no longer creates a PAT (token {} removed); create tokens explicitly if needed.
  • modules/gcp/accountmodules/gcp/workspace; composer submodule addresses change. State from old applies does not migrate cleanly — re-apply on clean state (each example README documents this).
  • modules/gcp/service-account: the provisioning SA is now named <prefix>-ws-creator (email changes on re-apply), and the workspace-creator custom role is trimmed to the currently documented permission set — the GKE-era container.*/storage.* permissions are gone (the data plane is GCE; Databricks service agents provision workspace infrastructure). The module output is now service_account_email.

Composer preconditions

Rule Reason
restricted_egressvpc_source.spoke="create" Hub-spoke + egress firewall + private DNS require owning the spoke VPC
restricted_egress ⇒ at least one private_link_* flag Egress-restricted workspace without PSC is unreachable
private_link_frontend == private_link_backend GCP mws_networks.vpc_endpoints requires both endpoint references
any private_link_*psc_subnet_cidr set PSC subnet needed for endpoints
restricted_egresshub_vpc_google_project set Hub topology needs a project
restricted_egress + vpc_source.hub="create"hub_vpc_cidr set Created hub needs a subnet CIDR
vpc_source.hub="existing"existing_hub_vpc_name + existing_hub_subnet_name set Need names to look up
vpc_source.spoke="create"spoke_vpc_cidr + subnet_cidr set Need CIDRs
vpc_source.spoke="existing"existing_vpc_name + existing_subnet_name set Need names to look up
vpc_source.spoke="databricks_managed" ⇒ all PrivateLink/egress flags false Can't attach PSC to a VPC we don't own
vpc_source.hub settable only when vpc_source.spoke is customer-managed (variable validation) A hub is meaningless without customer networking
PSC/egress ⇒ region in the 14-region PSC list Regional service-attachment maps
destination lists ⇒ serverless_egress_mode="restricted" Destinations are meaningless otherwise

Examples

Example Scenario
examples/gcp-basic vpc_source="databricks_managed"
examples/gcp-byovpc vpc_source="create" + CIDRs
examples/gcp-existing-vpc vpc_source="existing"
examples/gcp-with-psc-exfiltration-protection full isolation: 4 connectivity flags + serverless egress restricted + optional CMEK + security-settings + UC

Test plan

  • terraform validate passes for every module and example (unity-catalog validates via its example call site — configuration_aliases prevents standalone validate)
  • Positive fixtures plan offline for every scenario (13/15; the 2 exceptions do live data-source lookups and require real GCP credentials, documented)
  • 7 negative fixtures fail terraform plan with the expected precondition messages
  • terraform-docs regenerated for all modules; regen verified byte-reproducible
  • Sandbox terraform apply: gcp-basic — workspace created, login page HTTP 200, destroyed
  • Sandbox terraform apply: gcp-byovpc — workspace on customer VPC, Databricks auto-created db-<subnet>-ingress, destroyed
  • Sandbox terraform apply: PSC example (full topology, 54 resources) — verified in-cloud: workspace/dp/tunnel DNS → PSC IPs on both spoke and hub, googleapis.com → restricted range via peering zones, full firewall stack (deny-1100 + allows-1000 incl. intra-VPC), no Cloud NAT, PAS enforced ("Unauthorized network access" from public internet post-SSO), serverless egress policy RESTRICTED/ENFORCED with allow-listed destination honored and other destinations denied, cluster launched to RUNNING under restricted egress (validates the spoke DNS + intra-VPC firewall fixes end-to-end), destroyed

Phase B surfaced one runtime-only bug that terraform validate cannot catch, fixed in this PR: databricks_mws_vpc_endpoint builds its API path from the resource-level account_id attribute, so the attribute is required despite being schema-optional (omitting it fails with a misleading "Unable to load OAuth Config" error).

Docs

  • Cross-cloud contract (the normative design record for Azure/AWS refactors): docs/cross-cloud-module-contract.md

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
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
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
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
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
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
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
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
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
git mv only; no functional changes. Old path has a deprecation README.

Co-authored-by: Isaac
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
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
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 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
@micheledaddetta-databricks micheledaddetta-databricks changed the title feat(gcp): add modules/gcp/ composer + submodules (PR 1 of 6) feat(gcp): refactor GCP modules into composer + submodules (full) May 22, 2026
Plans a polish pass on the GCP modules and examples landed in PR #233:
- fill ~70 missing variable descriptions
- split large main.tf files by concern
- standardize versions.tf placement
- expand composer outputs, drop redundancy, rename misnomers
- add critical-only region validations
- add README usage examples

Co-authored-by: Isaac
8 tasks corresponding to the 8 commits planned in the spec.
Each task is self-contained with exact file paths, code blocks,
validation commands, and commit messages.

Co-authored-by: Isaac
… outputs

Reorganizes network, account, dns, and databricks-workspace modules
so each .tf file has one clear responsibility, and fixes the
*_psc_fr_id misnomer for outputs/inputs that hold forwarding-rule
names (not IDs).

File splits (no behavioral change):
- network/main.tf -> vpc.tf, subnets.tf, nat.tf, peering.tf,
  shared-vpc.tf, data.tf, locals.tf
- account/main.tf -> workspace.tf, networks.tf, locals.tf
- dns/hub.tf: workspace_dns_id local extracted to dns/locals.tf
- databricks-workspace/main.tf -> main.tf (module blocks only),
  locals.tf, preconditions.tf, random.tf

Renames (coordinated across outputs, inputs, wiring, and fixture):
- private-connectivity outputs: frontend_psc_fr_id -> frontend_forwarding_rule_name,
  backend_psc_fr_id -> backend_forwarding_rule_name,
  hub_frontend_psc_fr_id -> hub_frontend_forwarding_rule_name
- account inputs renamed to match
- composer wiring and psc-with-pas fixture updated accordingly

Co-authored-by: Isaac
…ion validations

Three layered improvements to module surface area and documentation:

Composer outputs (drop redundancy, add downstream-friendly outputs):
- Drop vpc_id (was an alias of spoke_vpc_id)
- Add 14 outputs: private_access_settings_id, frontend/backend/transit
  _endpoint_id, spoke_vpc_self_link, spoke_subnet_id/self_link,
  hub_vpc_self_link, nat_id, frontend/backend_psc_ip_spoke,
  frontend_psc_ip_hub, google_region
- Replace try(module.network[0].*) with explicit local.databricks_managed
  / var.restricted_egress ternaries (same behavior, intent visible)
- Add private_access_settings_id output on the account module
- Update gcp-byovpc and gcp-with-psc examples to consume spoke_vpc_id
  instead of the removed vpc_id

Variable descriptions (~70 added):
- Every variable in private-connectivity, account, dns, and the
  databricks-workspace composer now has a description attribute
  describing purpose, format, nullability semantics, and required-when
  context
- Gap-fill on service-account / unity-catalog where missing

Region validation (clearer error messages):
- private-connectivity validates google_region unconditionally against
  the 14-region list backing the regional PSC service-attachment maps
- composer adds the same validation as a precondition, but gated on
  any private_link_* flag or restricted_egress being true; bad regions
  now fail plan with a clear message instead of a map-lookup error

Co-authored-by: Isaac
File organization (versions.tf everywhere, no provider configs in modules):
- service-account module: init.tf -> versions.tf + data.tf
  (no provider "google" {} block — caller's responsibility)
- unity-catalog module: terraform.tf -> versions.tf
- examples gcp-basic, gcp-byovpc, gcp-existing-vpc, gcp-sa-provisioning:
  init.tf -> versions.tf + providers.tf (+ data.tf where the original
  carried google_client_openid_userinfo / google_client_config)
- examples gcp-with-psc-exfiltration-protection: terraform.tf -> versions.tf
  (providers.tf already existed)

README usage sections (terraform-google-modules convention):
- Each module README gets a ## Usage HCL block above the terraform-docs
  marker showing a minimal calling example
- Submodule READMEs note "Typically called by modules/gcp/databricks-
  workspace (the composer)"
- The composer README points to the four scenario examples

terraform-docs regen:
- Auto-generated README sections refreshed to reflect Task-2 renames,
  Task-3 new outputs, Task-4 ~70 variable descriptions, and Task-5
  region validation block

Co-authored-by: Isaac
…c with verified PSC/CMEK/provider-floor facts

Co-authored-by: Isaac
The psc-isolated composer fixture failed 'terraform plan' with 4x Invalid
count argument: counts depended on forwarding-rule names (which embed the
random suffix) and on hub_vpc_id (a resource attribute). Counts now gate on
enable_frontend/enable_backend/create_hub booleans.

Co-authored-by: Isaac
Private zones attached only to the hub are invisible to the spoke across
VPC peering, so restricted-egress spokes resolved Google APIs to public IPs
that the deny-egress rule blocks - breaking cluster launch, GCS and GCR.
Parity restore from the deleted module's dns-spoke.tf.

Co-authored-by: Isaac
…stricted egress

Parity restore from the deleted module's firewall-spoke.tf: EGRESS allow to
the spoke CIDR (beats the 1100 deny) and INGRESS allow from the spoke CIDR
(GCP ingress is implied-deny). Without both, node-to-node Spark traffic is
blocked and clusters never form.

Co-authored-by: Isaac
GCP's mws_networks.vpc_endpoints requires both dataplane_relay and rest_api,
so single-sided PSC configs previously passed preconditions and broke at
apply (null DNS rrdatas, missing control-plane allow rule, silently
unattached endpoints). psc_subnet_cidr is now required for any PSC scenario,
matching its documented contract.

Co-authored-by: Isaac
The variable promised an internal regional-default lookup backed by an
empty map, so omitting hive_metastore_ip silently dropped the legacy HMS
allow rule. The rule is now explicitly opt-in and the docs say so; UC
workspaces (the default) do not need it.

Co-authored-by: Isaac
The exfiltration-protection design deliberately has no internet egress
path; the refactor had reintroduced NAT unconditionally for created VPCs.

Co-authored-by: Isaac
BYOVPC + Shared VPC (no restricted egress) was expressible in the legacy
modules but the refactor gated the binding on create_hub.

Co-authored-by: Isaac
… PAT, resource-level account_id

- network.spoke_vpc_cidr, private-connectivity.hub_vpc_cidr and the dns
  self-link inputs were wired through but never consumed
- the PSC example passed tags, implying an effect the composer doesn't have
- the workspace module created a PAT the legacy module never did; tokens
  don't belong in module state
- account_id on mws_* resources is deprecated in favor of the provider block

databricks/databricks v1.120.0 still requires account_id on
databricks_mws_workspaces and databricks_mws_networks; restored there only.
Also dropped spoke_vpc_cidr from the network create-shared fixture, which
referenced the removed variable but wasn't in the original file list.

Co-authored-by: Isaac
Modules declare floors (google >= 6.0, databricks >= 1.81.1); examples pin
pessimistically (~> 6.17, ~> 1.81). Preconditions and the NAT dependency
bridge use the built-in terraform_data instead of null_resource, dropping
the null provider. Removes no-op firewall attributes.

Co-authored-by: Isaac
The contract defines the slot by responsibility - everything that registers
the workspace with the Databricks control plane (mws_* here, ARM on Azure).
'account' does not port to Azure, which has no account API layer.

Co-authored-by: Isaac
…licies

Classic-compute egress was hardened while serverless SQL in the same
workspace could egress freely. serverless_egress_mode=restricted creates a
deny-by-default databricks_account_network_policy bound to the workspace;
the PSC example defaults to restricted. Requires Enterprise tier and
databricks provider >= 1.81.1.

Co-authored-by: Isaac
… storage

Cloud KMS keys wire through databricks_mws_customer_managed_keys into the
workspace at creation. No Terraform-side IAM grants: Databricks sets the
key IAM policy during workspace creation, which is why the Terraform
principal needs cloudkms.cryptoKeys.get/setIamPolicy on the key.

Co-authored-by: Isaac
The GCP data plane runs on GCE; BYOVPC requires exactly one subnet and no
secondary ranges. BREAKING: GKE-era configs must remove pod_cidr/svc_cidr.

Co-authored-by: Isaac
…s module

Cloud-neutral module (CSP, ESM, automatic cluster update, IP access lists)
under the new modules/databricks/ namespace; wired into the PSC example via
the workspace-level provider alias. CSP is irreversible and precondition-
gated on ESM.

Co-authored-by: Isaac
Freezes the module slots, composer interface, two-tier naming rule, file
shape and testing standard that Azure/AWS refactors must follow; the GCP
tree is the reference implementation.

Co-authored-by: Isaac
@micheledaddetta-databricks micheledaddetta-databricks changed the title feat(gcp): refactor GCP modules into composer + submodules (full) feat(gcp): composer + submodules refactor, hardening fixes, cross-cloud contract, serverless egress & CMEK Jul 9, 2026
…, cleaner precondition errors

- PSC example's hive_metastore_ip is now optional (default null, module's
  exact description); dropped the stale required-var/empty-string tfvars
  wiring that rendered an invalid /32 CIDR at apply. Added a matching
  validation block to private-connectivity and databricks-workspace
  variables.tf (byte-identical) as a belt-and-braces guard.
- Regenerated terraform-docs for all five GCP examples plus the two
  touched submodules; hand-fixed gcp-byovpc's migration table (pod/svc
  CIDR rows) and added prose to the PSC example README on the
  serverless_egress_mode default and optional CMEK/security-settings vars.
- Guarded module.network[0]/module.private_connectivity[0] references in
  the databricks-workspace composer (main.tf, outputs.tf, new locals) so
  invalid vpc_source=databricks_managed + PSC/restricted_egress
  combinations surface only the precondition message instead of a raw
  "Invalid index" error.
- Loosened the cross-cloud contract's fixture-coverage wording (§5) to
  match actual negative-fixture coverage.
- Documented that CMEK keys must exist before plan in all three variable
  descriptions (byte-identical).
- Added a "Running this example" section to the PSC example README
  covering ADC auth, PSC network reachability, and Enterprise-tier
  requirements.

Co-authored-by: Isaac
… to documented permissions

- service-account: drop the sa2 label and SA name (now <prefix>-ws-creator),
  remove the personal name from the IAM role title, delete the unused data
  sources and the broken custom_role_url output
- BREAKING: the custom role now carries only the currently documented
  workspace-creator permissions (GKE-era container.*/storage.* removed;
  the data plane is GCE and Databricks service agents provision workspace
  infrastructure); the service account email changes
- rename cryptic _fr / _dbx resource labels; unity-catalog bucket label
  reflects its role

Co-authored-by: Isaac
Phase B sandbox finding: the provider builds this resource's API path from
the resource-level account_id attribute (mws/resource_mws_vpc_endpoint.go),
not the provider config. Without it, apply fails with a misleading
'Unable to load OAuth Config' error. validate cannot catch this - the
attribute is schema-optional.

Co-authored-by: Isaac
Design specs and implementation plans were session working documents;
the durable design record is docs/cross-cloud-module-contract.md.

Co-authored-by: Isaac
…oke/use_existing_spoke

The hub side of the topology was explicit (create_hub) while the spoke
side hid behind a vague create_vpc local. The pair now mirrors the
contract's hub/spoke vocabulary; both flags gate more than a VPC
(spoke: VPC+subnet+NAT, hub: VPC+subnet+peering). Locals only - no
interface or state-address changes.

Co-authored-by: Isaac
- enable_hub_spoke_peering (default true) gates the two VPC peerings in
  AND with the hub flag, for topologies where hub-spoke connectivity is
  provided by Shared VPC or an existing transit
- hub_vpc_source = create|existing mirrors the spoke's vpc_source: the
  hub VPC/subnet can now be looked up instead of created
- create_hub renamed to enable_hub across the submodules: with lookup
  support the flag enables the hub, it does not always create it
- BREAKING: composer vpc_source is now an object
  { spoke = databricks_managed|create|existing, hub = create|existing }
  carrying both topology halves in one variable

Co-authored-by: Isaac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant