diff --git a/modules/cloudsql-instance/README.md b/modules/cloudsql-instance/README.md
index ab66fc2c1..8b2e9ffc7 100644
--- a/modules/cloudsql-instance/README.md
+++ b/modules/cloudsql-instance/README.md
@@ -413,7 +413,7 @@ module "db" {
| [network_config](variables.tf#L184) | Network configuration for the instance. Only one between private_network and psc_config can be used. | object({…}) | ✓ | |
| [project_id](variables.tf#L231) | The ID of the project where this instances will be created. | string | ✓ | |
| [region](variables.tf#L236) | Region of the primary instance. | string | ✓ | |
-| [tier](variables.tf#L287) | The machine type to use for the instances. | string | ✓ | |
+| [tier](variables.tf#L288) | The machine type to use for the instances. | string | ✓ | |
| [activation_policy](variables.tf#L16) | 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#L27) | Availability type for the primary replica. Either `ZONAL` or `REGIONAL`. | string | | "ZONAL" |
| [backup_configuration](variables.tf#L33) | Backup settings for primary instance. Will be automatically enabled if using MySQL with one or more replicas. | object({…}) | | {…} |
@@ -433,12 +433,12 @@ module "db" {
| [maintenance_config](variables.tf#L146) | Set maintenance window configuration and maintenance deny period (up to 90 days). Date format: 'yyyy-mm-dd'. | object({…}) | | {} |
| [password_validation_policy](variables.tf#L207) | Password validation policy configuration for instances. | object({…}) | | null |
| [prefix](variables.tf#L221) | Optional prefix used to generate instance names. | string | | null |
-| [replicas](variables.tf#L241) | Map of NAME=> {REGION, KMS_KEY} for additional read replicas. Set to null to disable replica creation. | map(object({…})) | | {} |
-| [root_password](variables.tf#L251) | Root password of the Cloud SQL instance, or flag to create a random password. Required for MS SQL Server. | object({…}) | | {} |
-| [ssl](variables.tf#L265) | Setting to enable SSL, set config and certificates. | object({…}) | | {} |
-| [terraform_deletion_protection](variables.tf#L280) | Prevent terraform from deleting instances. | bool | | true |
-| [time_zone](variables.tf#L292) | 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#L298) | 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({…})) | | {} |
+| [replicas](variables.tf#L241) | 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#L252) | Root password of the Cloud SQL instance, or flag to create a random password. Required for MS SQL Server. | object({…}) | | {} |
+| [ssl](variables.tf#L266) | Setting to enable SSL, set config and certificates. | object({…}) | | {} |
+| [terraform_deletion_protection](variables.tf#L281) | Prevent terraform from deleting instances. | bool | | true |
+| [time_zone](variables.tf#L293) | 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#L299) | 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 cafe9b56e..663d1172e 100644
--- a/modules/cloudsql-instance/main.tf
+++ b/modules/cloudsql-instance/main.tf
@@ -215,12 +215,12 @@ resource "google_sql_database_instance" "replicas" {
disk_autoresize_limit = var.disk_autoresize_limit
disk_size = var.disk_size
disk_type = var.disk_type
- # availability_type = var.availability_type
- user_labels = var.labels
- activation_policy = var.activation_policy
- collation = var.collation
- connector_enforcement = var.connector_enforcement
- time_zone = var.time_zone
+ availability_type = each.value.availability_type
+ user_labels = var.labels
+ activation_policy = var.activation_policy
+ collation = var.collation
+ connector_enforcement = var.connector_enforcement
+ time_zone = var.time_zone
ip_configuration {
diff --git a/modules/cloudsql-instance/variables.tf b/modules/cloudsql-instance/variables.tf
index 72a4fa5da..b566426d9 100644
--- a/modules/cloudsql-instance/variables.tf
+++ b/modules/cloudsql-instance/variables.tf
@@ -1,5 +1,5 @@
/**
- * Copyright 2024 Google LLC
+ * Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -239,10 +239,11 @@ variable "region" {
}
variable "replicas" {
- description = "Map of NAME=> {REGION, KMS_KEY} for additional read replicas. Set to null to disable replica creation."
+ description = "Map of NAME=> {REGION, KMS_KEY, AVAILABILITY_TYPE} for additional read replicas. Set to null to disable replica creation."
type = map(object({
region = string
encryption_key_name = optional(string)
+ availability_type = optional(string)
}))
default = {}
nullable = false
diff --git a/modules/net-firewall-policy/README.md b/modules/net-firewall-policy/README.md
index ac02f48b7..9f76a1013 100644
--- a/modules/net-firewall-policy/README.md
+++ b/modules/net-firewall-policy/README.md
@@ -15,6 +15,11 @@ The module also makes fewer assumptions about implicit defaults, only using one
- [Global Network policy](#global-network-policy)
- [Regional Network policy](#regional-network-policy)
- [Factory](#factory)
+ - [Firewall Rule Factory Schema](#firewall-rule-factory-schema)
+ - [Dynamic Rule Matching](#dynamic-rule-matching)
+ - [Ingress Rules (](#ingress-rules-)
+ - [Egress Rules (](#egress-rules-)
+ - [Rule-Level Mappings](#rule-level-mappings)
- [Variables](#variables)
- [Outputs](#outputs)
@@ -174,6 +179,7 @@ Factory configuration is via three optional attributes in the `rules_factory_con
- `ingress_rules_file_path` specifying the path to the ingress rules file
Factory rules are merged with rules declared in code, with the latter taking precedence where both use the same key.
+Also, the factory applies implicit defaults: `action` defaults to `deny` for egress and `allow` for ingress, while omitting `layer4_configs` makes the rule match all protocols.
This is an example of a simple factory:
@@ -309,7 +315,71 @@ http:
ports:
- 80
```
+
+#### Firewall Rule Factory Schema
+
+The following schema outlines all available fields for defining a rule within a factory YAML file. Use this as a reference, and note the inline comments for fields that apply only to specific policy types.
+
+```yaml
+rule-name:
+ priority:
+ action:
+ description:
+ disabled:
+ enable_logging:
+ security_profile_group: # Not for Regional policies
+ target_service_accounts: []
+ target_tags: [] # Not for Hierarchical policies
+ target_resources: [] # For Hierarchical policies only
+ tls_inspect: # Not for Regional policies
+ match:
+ source_ranges: []
+ destination_ranges: []
+ source_tags: [] # Not for Hierarchical policies
+ threat_intelligences: []
+ fqdns: []
+ address_groups: []
+ region_codes: []
+ layer4_configs:
+ - protocol:
+ ports: []
+```
+
+### Dynamic Rule Matching
+
+This module simplifies firewall rule creation by using generic, context-aware variables within the `match` block. Based on the rule's specified `direction` (`INGRESS` or `EGRESS`), the module maps these generic variables to the correct source- (`src_*`) or destination-specific (`dest_*`) arguments in the underlying resource.
+
+The tables below provide a complete reference for these dynamic mappings.
+
+#### Ingress Rules (`direction = "INGRESS"`)
+
+| Module Variable (`match.*`) | Mapped Resource Attribute |
+| :--- | :--- |
+| `address_groups` | `src_address_groups` |
+| `fqdns` | `src_fqdns` |
+| `region_codes` | `src_region_codes` |
+| `source_tags` | `src_secure_tags` |
+| `threat_intelligences` | `src_threat_intelligences` |
+
+#### Egress Rules (`direction = "EGRESS"`)
+
+| Module Variable (`match.*`) | Mapped Resource Attribute |
+| :--- | :--- |
+| `address_groups` | `dest_address_groups` |
+| `fqdns` | `dest_fqdns` |
+| `region_codes` | `dest_region_codes` |
+| `threat_intelligences` | `dest_threat_intelligences` |
+
+#### Rule-Level Mappings
+
+The following variable is defined at the top level of the rule (not within the `match` block) and is mapped directly, regardless of the rule's direction.
+
+| Module Variable | Mapped Resource Attribute |
+| :--- | :--- |
+| `target_tags` | `target_secure_tags` |
+
+
## Variables
| name | description | type | required | default |
diff --git a/modules/project/cmek.tf b/modules/project/cmek.tf
index 0d369d2c4..50b954aec 100644
--- a/modules/project/cmek.tf
+++ b/modules/project/cmek.tf
@@ -1,5 +1,5 @@
/**
- * Copyright 2024 Google LLC
+ * Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ locals {
"artifactregistry.googleapis.com" : ["artifactregistry"]
"bigtableadmin.googleapis.com" : ["bigtable"]
"bigquery.googleapis.com" : ["bigquery-encryption"]
- # the list for composer now track composer 3
+ # the list for composer now tracks composer 3
# https://cloud.google.com/composer/docs/composer-3/configure-cmek-encryption#grant-roles-permissions
"composer.googleapis.com" : ["composer", "storage"]
"compute.googleapis.com" : ["compute"]
@@ -49,20 +49,28 @@ locals {
"storage.googleapis.com" : ["storage"]
"run.googleapis.com" : ["cloudrun"]
}
- _cmek_members = merge(flatten([
+ _all_cmek_bindings = flatten([
for service, keys in var.service_encryption_key_ids : [
- # use the deps listed above, if the service does not appear
- # there, use all the service agents belonging to the service
- for dep in try(local._cmek_agents_by_service[service], [for x in local._service_agents_by_api[service] : x.name], [service]) : {
- # use index in map key, to allow specifying keys, that will be created in the same apply
- for index, key in keys :
- "key-${index}.${local._aliased_service_agents[dep].name}" => {
- key = key
- agent = local._aliased_service_agents[dep].iam_email
+ for dep in try(local._cmek_agents_by_service[service], [for x in local._service_agents_by_api[service] : x.name], [service]) : [
+ for key in keys : {
+ key_id = key
+ agent_name = local._aliased_service_agents[dep].name
+ agent_email = local._aliased_service_agents[dep].iam_email
}
- }
+ ]
]
- ])...)
+ ])
+ _cmek_bindings_grouped_by_agent = {
+ for binding in local._all_cmek_bindings : binding.agent_name => binding...
+ }
+ _cmek_members = merge([
+ for agent_name, bindings in local._cmek_bindings_grouped_by_agent : {
+ for i, binding in bindings : "key-${i}.${agent_name}" => {
+ key = binding.key_id
+ agent = binding.agent_email
+ }
+ }
+ ]...)
}
resource "google_kms_crypto_key_iam_member" "service_agent_cmek" {