diff --git a/modules/cloudsql-instance/README.md b/modules/cloudsql-instance/README.md
index 17d23186b..660a9972f 100644
--- a/modules/cloudsql-instance/README.md
+++ b/modules/cloudsql-instance/README.md
@@ -13,6 +13,7 @@ Note that this module assumes that some options are the same for both the primar
- [Custom flags, databases and users](#custom-flags-databases-and-users)
- [CMEK encryption](#cmek-encryption)
- [Instance with PSC enabled](#instance-with-psc-enabled)
+ - [Instance with PSC auto connections](#instance-with-psc-auto-connections)
- [Enable public IP](#enable-public-ip)
- [Query Insights](#query-insights)
- [Maintenance Config](#maintenance-config)
@@ -246,7 +247,9 @@ module "db" {
project_id = var.project_id
network_config = {
connectivity = {
- psc_allowed_consumer_projects = [var.project_id]
+ psc_config = {
+ allowed_consumer_projects = [var.project_id]
+ }
}
}
prefix = "myprefix"
@@ -262,6 +265,39 @@ module "db" {
# tftest modules=1 resources=1 inventory=psc.yaml e2e
```
+### Instance with PSC auto connections
+
+```hcl
+module "db" {
+ source = "./fabric/modules/cloudsql-instance"
+ project_id = var.project_id
+ context = {
+ networks = { myvpc = "https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa" }
+ }
+ network_config = {
+ connectivity = {
+ psc_config = {
+ allowed_consumer_projects = [var.project_id]
+ psc_auto_connections = [{
+ consumer_network = "$networks:myvpc"
+ consumer_service_project_id = var.project_id
+ }]
+ }
+ }
+ }
+ prefix = "myprefix"
+ name = "db"
+ region = var.region
+ availability_type = "REGIONAL"
+ database_version = "POSTGRES_13"
+ tier = "db-g1-small"
+
+ gcp_deletion_protection = false
+ terraform_deletion_protection = false
+}
+# tftest modules=1 resources=1 inventory=psc-auto.yaml e2e
+```
+
### Enable public IP
Use `public_ipv4` to create instances with a public IP.
@@ -417,9 +453,9 @@ module "db" {
| [database_version](variables.tf#L96) | Database type and version to create. | string | ✓ | |
| [name](variables.tf#L213) | Name of primary instance. | string | ✓ | |
| [network_config](variables.tf#L218) | Network configuration for the instance. Only one between private_network and psc_config can be used. | object({…}) | ✓ | |
-| [project_id](variables.tf#L261) | The ID of the project where this instances will be created. | string | ✓ | |
-| [region](variables.tf#L266) | Region of the primary instance. | string | ✓ | |
-| [tier](variables.tf#L318) | The machine type to use for the instances. | string | ✓ | |
+| [project_id](variables.tf#L278) | The ID of the project where this instances will be created. | string | ✓ | |
+| [region](variables.tf#L283) | Region of the primary instance. | string | ✓ | |
+| [tier](variables.tf#L335) | The machine type to use for the instances. | string | ✓ | |
| [activation_policy](variables.tf#L17) | This variable specifies when the instance should be active. Can be either ALWAYS, NEVER or ON_DEMAND. Default is ALWAYS. | string | | "ALWAYS" |
| [availability_type](variables.tf#L28) | Availability type for the primary replica. Either `ZONAL` or `REGIONAL`. | string | | "ZONAL" |
| [backup_configuration](variables.tf#L34) | Backup settings for primary instance. Set to null to leave existing GCP backup settings unmanaged. When set, all fields are managed by Terraform including disabling backups when enabled=false. | object({…}) | | null |
@@ -440,14 +476,14 @@ module "db" {
| [labels](variables.tf#L162) | Labels to be attached to all instances. | map(string) | | null |
| [maintenance_config](variables.tf#L168) | Set maintenance window configuration and maintenance deny period (up to 90 days). Date format: 'yyyy-mm-dd'. | object({…}) | | {} |
| [managed_connection_pooling_config](variables.tf#L203) | Configuration for Managed Connection Pooling. NOTE: This feature is only available for PostgreSQL on Enterprise Plus edition instances. | object({…}) | | {} |
-| [password_validation_policy](variables.tf#L237) | Password validation policy configuration for instances. | object({…}) | | null |
-| [prefix](variables.tf#L251) | Optional prefix used to generate instance names. | string | | null |
-| [replicas](variables.tf#L271) | Map of NAME=> {REGION, KMS_KEY, AVAILABILITY_TYPE} for additional read replicas. Set to null to disable replica creation. | map(object({…})) | | {} |
-| [root_password](variables.tf#L282) | Root password of the Cloud SQL instance, or flag to create a random password. Required for MS SQL Server. | object({…}) | | {} |
-| [ssl](variables.tf#L296) | Setting to enable SSL, set config and certificates. | object({…}) | | {} |
-| [terraform_deletion_protection](variables.tf#L311) | Prevent terraform from deleting instances. | bool | | true |
-| [time_zone](variables.tf#L323) | The time_zone to be used by the database engine (supported only for SQL Server), in SQL Server timezone format. | string | | null |
-| [users](variables.tf#L329) | Map of users to create in the primary instance (and replicated to other replicas). For MySQL, anything after the first `@` (if present) will be used as the user's host. Set PASSWORD to null if you want to get an autogenerated password. The user types available are: 'BUILT_IN', 'CLOUD_IAM_USER' or 'CLOUD_IAM_SERVICE_ACCOUNT'. | map(object({…})) | | {} |
+| [password_validation_policy](variables.tf#L254) | Password validation policy configuration for instances. | object({…}) | | null |
+| [prefix](variables.tf#L268) | Optional prefix used to generate instance names. | string | | null |
+| [replicas](variables.tf#L288) | Map of NAME=> {REGION, KMS_KEY, AVAILABILITY_TYPE} for additional read replicas. Set to null to disable replica creation. | map(object({…})) | | {} |
+| [root_password](variables.tf#L299) | Root password of the Cloud SQL instance, or flag to create a random password. Required for MS SQL Server. | object({…}) | | {} |
+| [ssl](variables.tf#L313) | Setting to enable SSL, set config and certificates. | object({…}) | | {} |
+| [terraform_deletion_protection](variables.tf#L328) | Prevent terraform from deleting instances. | bool | | true |
+| [time_zone](variables.tf#L340) | The time_zone to be used by the database engine (supported only for SQL Server), in SQL Server timezone format. | string | | null |
+| [users](variables.tf#L346) | Map of users to create in the primary instance (and replicated to other replicas). For MySQL, anything after the first `@` (if present) will be used as the user's host. Set PASSWORD to null if you want to get an autogenerated password. The user types available are: 'BUILT_IN', 'CLOUD_IAM_USER' or 'CLOUD_IAM_SERVICE_ACCOUNT'. | map(object({…})) | | {} |
## Outputs
diff --git a/modules/cloudsql-instance/main.tf b/modules/cloudsql-instance/main.tf
index d1fc9d9f3..5df33cd29 100644
--- a/modules/cloudsql-instance/main.tf
+++ b/modules/cloudsql-instance/main.tf
@@ -43,6 +43,29 @@ locals {
project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
region = lookup(local.ctx.locations, var.region, var.region)
+ ### PSC locals
+ psc_allowed_consumer_projects = (
+ try(var.network_config.connectivity.psc_config.allowed_consumer_projects, null) == null
+ ? null
+ : [
+ for p in try(var.network_config.connectivity.psc_config.allowed_consumer_projects, []) :
+ lookup(local.ctx.project_ids, p, p)
+ ]
+ )
+ psc_auto_connections = [
+ for c in coalesce(
+ try(var.network_config.connectivity.psc_config.psc_auto_connections, []),
+ []
+ ) : {
+ consumer_network = lookup(local.ctx.networks, c.consumer_network, c.consumer_network)
+ consumer_service_project_id = (
+ c.consumer_service_project_id == null
+ ? null
+ : lookup(local.ctx.project_ids, c.consumer_service_project_id, c.consumer_service_project_id)
+ )
+ }
+ ]
+
users = {
for k, v in var.users : k =>
local.is_mysql
@@ -113,16 +136,22 @@ resource "google_sql_database_instance" "primary" {
}
dynamic "psc_config" {
for_each = (
- var.network_config.connectivity.psc_allowed_consumer_projects != null
+ try(var.network_config.connectivity.psc_config, null) != null
? [""]
: []
)
content {
- psc_enabled = true
- allowed_consumer_projects = [
- for p in var.network_config.connectivity.psc_allowed_consumer_projects :
- lookup(local.ctx.project_ids, p, p)
- ]
+ psc_enabled = true
+ allowed_consumer_projects = local.psc_allowed_consumer_projects
+ network_attachment_uri = try(var.network_config.connectivity.psc_config.network_attachment_uri, null)
+
+ dynamic "psc_auto_connections" {
+ for_each = local.psc_auto_connections
+ content {
+ consumer_network = psc_auto_connections.value.consumer_network
+ consumer_service_project_id = psc_auto_connections.value.consumer_service_project_id
+ }
+ }
}
}
}
@@ -314,16 +343,26 @@ resource "google_sql_database_instance" "replicas" {
}
dynamic "psc_config" {
for_each = (
- var.network_config.connectivity.psc_allowed_consumer_projects != null
+ try(var.network_config.connectivity.psc_config, null) != null
? [""]
: []
)
content {
- psc_enabled = true
- allowed_consumer_projects = [
- for p in var.network_config.connectivity.psc_allowed_consumer_projects :
- lookup(local.ctx.project_ids, p, p)
- ]
+ psc_enabled = true
+ allowed_consumer_projects = local.psc_allowed_consumer_projects
+ network_attachment_uri = try(var.network_config.connectivity.psc_config.network_attachment_uri, null)
+
+ dynamic "psc_auto_connections" {
+ for_each = (
+ try(var.network_config.connectivity.psc_config.psc_auto_connections, null) != null
+ ? [""]
+ : []
+ )
+ content {
+ consumer_network = local.psc_consumer_network
+ consumer_service_project_id = local.psc_consumer_service_project_id
+ }
+ }
}
}
}
diff --git a/modules/cloudsql-instance/variables.tf b/modules/cloudsql-instance/variables.tf
index 82e6257a9..cb5008599 100644
--- a/modules/cloudsql-instance/variables.tf
+++ b/modules/cloudsql-instance/variables.tf
@@ -228,10 +228,27 @@ variable "network_config" {
replica = optional(string)
}))
}))
- psc_allowed_consumer_projects = optional(list(string))
+ psc_allowed_consumer_projects = optional(list(string)) # OBSOLETE. See validation below.
+ psc_config = optional(object({
+ allowed_consumer_projects = optional(list(string))
+ network_attachment_uri = optional(string)
+ psc_auto_connections = optional(list(object({
+ consumer_network = string
+ consumer_service_project_id = optional(string)
+ })))
+ }))
enable_private_path_for_services = optional(bool, false)
})
})
+ validation {
+ condition = (
+ try(var.network_config.connectivity, null) == null ? true : (
+ var.network_config.connectivity.psc_allowed_consumer_projects == null ||
+ length(var.network_config.connectivity.psc_allowed_consumer_projects) == 0
+ )
+ )
+ error_message = "network_config.connectivity.psc_allowed_consumer_projects is obsolete. Use network_config.connectivity.psc_config.allowed_consumer_projects instead."
+ }
}
variable "password_validation_policy" {
diff --git a/tests/fixtures/cloudsql-instance.tf b/tests/fixtures/cloudsql-instance.tf
index 6fed8d6ad..b8baf23a1 100644
--- a/tests/fixtures/cloudsql-instance.tf
+++ b/tests/fixtures/cloudsql-instance.tf
@@ -19,7 +19,9 @@ module "cloudsql-instance" {
project_id = var.project_id
network_config = {
connectivity = {
- psc_allowed_consumer_projects = [var.project_id]
+ psc_config = {
+ allowed_consumer_projects = [var.project_id]
+ }
}
}
## define a consumer project with an endpoint within the project
diff --git a/tests/modules/cloudsql_instance/context.tfvars b/tests/modules/cloudsql_instance/context.tfvars
index b0b115231..f2a80c232 100644
--- a/tests/modules/cloudsql_instance/context.tfvars
+++ b/tests/modules/cloudsql_instance/context.tfvars
@@ -11,10 +11,12 @@ database_version = "POSTGRES_13"
tier = "db-g1-small"
network_config = {
connectivity = {
- psc_allowed_consumer_projects = ["$project_ids:myprj"]
psa_config = {
private_network = "$networks:myvpc"
}
+ psc_config = {
+ allowed_consumer_projects = ["$project_ids:myprj"]
+ }
}
}
encryption_key_name = "$kms_keys:mykey"
diff --git a/tests/modules/cloudsql_instance/examples/custom.yaml b/tests/modules/cloudsql_instance/examples/custom.yaml
index 5d028b89c..e57dd18af 100644
--- a/tests/modules/cloudsql_instance/examples/custom.yaml
+++ b/tests/modules/cloudsql_instance/examples/custom.yaml
@@ -1,10 +1,10 @@
-# Copyright 2023 Google LLC
+# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,36 +14,100 @@
values:
module.db.google_sql_database.databases["departments"]:
- deletion_policy: DELETE
instance: db
name: departments
project: project-id
+ timeouts: null
module.db.google_sql_database.databases["people"]:
- deletion_policy: DELETE
instance: db
name: people
project: project-id
- module.db.google_sql_database_instance.primary: {}
+ timeouts: null
+ module.db.google_sql_database_instance.primary:
+ backupdr_backup: null
+ clone: []
+ database_version: MYSQL_8_0
+ deletion_protection: false
+ final_backup_description: null
+ name: db
+ point_in_time_restore_context: []
+ project: project-id
+ region: europe-west8
+ restore_backup_context: []
+ root_password: null
+ root_password_wo: null
+ root_password_wo_version: null
+ settings:
+ - activation_policy: ALWAYS
+ active_directory_config: []
+ advanced_machine_features: []
+ auto_upgrade_enabled: null
+ availability_type: ZONAL
+ collation: null
+ database_flags:
+ - name: cloudsql_iam_authentication
+ value: 'on'
+ - name: disconnect_on_expired_password
+ value: 'on'
+ deletion_protection_enabled: false
+ deny_maintenance_period: []
+ disk_autoresize: true
+ disk_autoresize_limit: 0
+ disk_type: PD_SSD
+ edition: ENTERPRISE
+ enable_dataplex_integration: null
+ enable_google_ml_integration: null
+ entraid_config: []
+ final_backup_config: []
+ ip_configuration:
+ - allocated_ip_range: null
+ authorized_networks: []
+ custom_subject_alternative_names: null
+ enable_private_path_for_google_cloud_services: false
+ ipv4_enabled: false
+ private_network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ psc_config: []
+ server_ca_pool: null
+ server_certificate_rotation_mode: null
+ maintenance_window: []
+ password_validation_policy: []
+ pricing_plan: PER_USE
+ retain_backups_on_delete: null
+ sql_server_audit_config: []
+ tier: db-g1-small
+ time_zone: null
+ timeouts: null
module.db.google_sql_user.users["fixture-service-account@project-id.iam.gserviceaccount.com"]:
+ database_roles: null
instance: db
name: fixture-service-account@project-id.iam.gserviceaccount.com
password: null
password_policy: []
+ password_wo: null
+ password_wo_version: null
project: project-id
+ timeouts: null
type: CLOUD_IAM_SERVICE_ACCOUNT
module.db.google_sql_user.users["user1"]:
- deletion_policy: null
+ database_roles: null
instance: db
name: user1
password_policy: []
+ password_wo: null
+ password_wo_version: null
project: project-id
+ timeouts: null
type: null
module.db.google_sql_user.users["user2"]:
- deletion_policy: null
+ database_roles: null
instance: db
name: user2
+ password: mypassword
password_policy: []
+ password_wo: null
+ password_wo_version: null
project: project-id
+ timeouts: null
type: null
module.db.random_password.passwords["user1"]:
keepers: null
@@ -58,9 +122,24 @@ values:
override_special: null
special: true
upper: true
+ module.iam-service-account.google_service_account.service_account[0]:
+ account_id: fixture-service-account
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: Terraform-managed.
+ email: fixture-service-account@project-id.iam.gserviceaccount.com
+ member: serviceAccount:fixture-service-account@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
counts:
+ google_service_account: 1
google_sql_database: 2
google_sql_database_instance: 1
google_sql_user: 3
modules: 2
+ random_password: 1
+ resources: 8
+
+outputs: {}
diff --git a/tests/modules/cloudsql_instance/examples/psc-auto.yaml b/tests/modules/cloudsql_instance/examples/psc-auto.yaml
new file mode 100644
index 000000000..0f18a6f12
--- /dev/null
+++ b/tests/modules/cloudsql_instance/examples/psc-auto.yaml
@@ -0,0 +1,78 @@
+# Copyright 2026 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ module.db.google_sql_database_instance.primary:
+ backupdr_backup: null
+ clone: []
+ database_version: POSTGRES_13
+ final_backup_description: null
+ name: myprefix-db
+ point_in_time_restore_context: []
+ project: project-id
+ region: europe-west8
+ restore_backup_context: []
+ root_password: null
+ root_password_wo: null
+ root_password_wo_version: null
+ settings:
+ - activation_policy: ALWAYS
+ active_directory_config: []
+ advanced_machine_features: []
+ auto_upgrade_enabled: null
+ availability_type: REGIONAL
+ collation: null
+ database_flags: []
+ deletion_protection_enabled: false
+ deny_maintenance_period: []
+ disk_autoresize: true
+ disk_autoresize_limit: 0
+ disk_type: PD_SSD
+ edition: ENTERPRISE
+ enable_dataplex_integration: null
+ enable_google_ml_integration: null
+ entraid_config: []
+ final_backup_config: []
+ ip_configuration:
+ - allocated_ip_range: null
+ authorized_networks: []
+ custom_subject_alternative_names: null
+ enable_private_path_for_google_cloud_services: false
+ ipv4_enabled: false
+ private_network: null
+ psc_config:
+ - allowed_consumer_projects:
+ - project-id
+ network_attachment_uri: ''
+ psc_auto_connections:
+ - consumer_network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ consumer_service_project_id: project-id
+ psc_enabled: true
+ server_ca_pool: null
+ server_certificate_rotation_mode: null
+ maintenance_window: []
+ password_validation_policy: []
+ pricing_plan: PER_USE
+ retain_backups_on_delete: null
+ sql_server_audit_config: []
+ tier: db-g1-small
+ time_zone: null
+ timeouts: null
+
+counts:
+ google_sql_database_instance: 1
+ modules: 1
+ resources: 1
+
+outputs: {}