diff --git a/FACTORIES.md b/FACTORIES.md
new file mode 100644
index 000000000..d8e1a7f2e
--- /dev/null
+++ b/FACTORIES.md
@@ -0,0 +1,118 @@
+# Factories Overview
+
+- [Modules](#modules)
+- [FAST Stages](#fast-stages)
+- [Maintenance Guide](#maintenance-guide)
+
+## Modules
+
+The following table provides a granular overview of modules that implement factory patterns. Each row represents a specific **factory configuration key** found within the `factories_config` variable.
+
+* **Primary Module Resource**: The main resource the module is designed to manage (e.g., a Project for the `project` module, or an Access Policy for `vpc-sc`). "N/A" indicates the module is a "Pure Factory" designed primarily to create multiple top-level resources.
+* **Factory Key**: The key in `factories_config` used to load external data.
+* **Factory-Managed Resources**: The specific resources created by iterating over the loaded factory data.
+* **Dependencies**: Module-level variables used by the factory resources (e.g., `project_id` injected into factory resources).
+
+| Module | Primary Module Resource | Factory Key | Factory-Managed Resources | Dependencies (Module Variables) |
+| :--- | :--- | :--- | :--- | :--- |
+| **analytics-hub** | Analytics Hub Exchange | `listings` | Analytics Hub Listings | `project_id`, `region` |
+| **billing-account** | Billing Account (Config) | `budgets_data_path` | Billing Budgets | `id` (Billing Account ID) |
+| **data-catalog-policy-tag** | Data Catalog Taxonomy | `taxonomy` | Policy Tags | `project_id`, `location`, `name` (Taxonomy Name) |
+| **data-catalog-tag** | N/A | `tags` | Data Catalog Tags | `tags` (Merged with factory data) |
+| **data-catalog-tag-template** | N/A | `tag_templates` | Tag Templates | `project_id`, `region` |
+| **dataplex-aspect-types** | N/A | `aspect_types` | Aspect Types | `project_id`, `location` |
+| **dataplex-datascan** | DataScan | `data_quality_spec` | Data Quality Rules | `project_id`, `location` |
+| **dns-response-policy** | DNS Response Policy | `rules` | Response Policy Rules | `project_id` |
+| **folder** | Folder | `org_policies` | Organization Policies | `folder` (ID/Name) |
+| **folder** | Folder | `pam_entitlements` | PAM Entitlements | `folder` (ID/Name) |
+| **folder** | Folder | `scc_mute_configs` | SCC Mute Configs | `folder` (ID/Name) |
+| **folder** | Folder | `scc_sha_custom_modules` | SCC SHA Custom Modules | `folder` (ID/Name) |
+| **net-firewall-policy** | Firewall Policy | `egress_mirroring_rules_file_path` | Egress Packet Mirroring Rules | `name` (Policy Name) |
+| **net-firewall-policy** | Firewall Policy | `egress_rules_file_path` | Egress Firewall Rules | `name` (Policy Name) |
+| **net-firewall-policy** | Firewall Policy | `ingress_mirroring_rules_file_path` | Ingress Packet Mirroring Rules | `name` (Policy Name) |
+| **net-firewall-policy** | Firewall Policy | `ingress_rules_file_path` | Ingress Firewall Rules | `name` (Policy Name) |
+| **net-swp** | Secure Web Proxy | `policy_rules` | Proxy Policy Rules | `project_id`, `region` |
+| **net-swp** | Secure Web Proxy | `url_lists` | Proxy URL Lists | `project_id`, `region` |
+| **net-vpc** | VPC Network | `internal_ranges_folder` | Internal Ranges | `project_id`, `name` (Network Name) |
+| **net-vpc** | VPC Network | `subnets_folder` | Subnets | `project_id`, `region` (Default), `name` (Network Name) |
+| **net-vpc-factory** | N/A | `vpcs` | VPCs (and associated resources) | `context`, `data_defaults`, `data_overrides` |
+| **net-vpc-firewall** | N/A | `rules_folder` | Firewall Rules | `project_id`, `network` |
+| **organization** | Organization (Existing) | `custom_roles` | Custom IAM Roles | `organization_id` |
+| **organization** | Organization (Existing) | `org_policies` | Organization Policies | `organization_id` |
+| **organization** | Organization (Existing) | `org_policy_custom_constraints` | Org Policy Custom Constraints | `organization_id` |
+| **organization** | Organization (Existing) | `pam_entitlements` | PAM Entitlements | `organization_id` |
+| **organization** | Organization (Existing) | `scc_mute_configs` | SCC Mute Configs | `organization_id` |
+| **organization** | Organization (Existing) | `scc_sha_custom_modules` | SCC SHA Custom Modules | `organization_id` |
+| **organization** | Organization (Existing) | `tags` | ResourceManager Tags | `organization_id` |
+| **project** | Project | `custom_roles` | Custom IAM Roles | `project.project_id` |
+| **project** | Project | `observability` | Observability (Alerts, Metrics) | `project.project_id` |
+| **project** | Project | `org_policies` | Organization Policies | `project.project_id` |
+| **project** | Project | `pam_entitlements` | PAM Entitlements | `project.project_id` |
+| **project** | Project | `quotas` | Service Quotas | `project.project_id` |
+| **project** | Project | `scc_mute_configs` | SCC Mute Configs | `project.project_id` |
+| **project** | Project | `scc_sha_custom_modules` | SCC SHA Custom Modules | `project.project_id` |
+| **project** | Project | `tags` | ResourceManager Tags | `project.project_id` |
+| **project-factory** | N/A | `budgets` | Budgets | `billing_account` (from defaults) |
+| **project-factory** | N/A | `folders` | Folders | `context` (Folder IDs) |
+| **project-factory** | N/A | `projects` | Projects | `context`, `data_defaults`, `data_overrides` |
+| **secops-rules** | N/A | `reference_lists` | SecOps Reference Lists | `project_id`, `tenant_config` |
+| **secops-rules** | N/A | `rules` | SecOps Detection Rules | `project_id`, `tenant_config` |
+| **vpc-sc** | Access Policy | `access_levels` | Access Levels | `access_policy`, `context` (for Project Numbers) |
+| **vpc-sc** | Access Policy | `egress_policies` | Egress Policies | `access_policy`, `context` |
+| **vpc-sc** | Access Policy | `ingress_policies` | Ingress Policies | `access_policy`, `context` |
+| **vpc-sc** | Access Policy | `perimeters` | Service Perimeters | `access_policy`, `context` |
+| **workstation-cluster** | Workstation Cluster | `workstation_configs` | Workstation Configurations | `project_id`, `location`, `network_config` |
+
+## FAST Stages
+
+The following table details how FAST stages implement factory patterns.
+
+* **Implementation Type**:
+ * `Module-Backed (Factory)`: The stage passes the `factories_config` path to a module which has internal logic to load and iterate over the data (e.g., `project-factory`).
+ * `Stage-Implemented (Module)`: The stage explicitly loads the YAML data (usually in `locals`) and iterates over a standard module (e.g., `dns` module).
+ * `Stage-Implemented (Resource)`: The stage explicitly loads the YAML data and iterates over raw Terraform resources.
+ * `Native (Complex)`: The stage implements complex factory logic combining multiple modules and resources.
+
+| Stage | Factory (Key/Feature) | Implementation Type | Underlying Module/Resource |
+| :--- | :--- | :--- | :--- |
+| **0-org-setup** | `projects`, `folders`, `budgets` | Module-Backed (Factory) | `project-factory` |
+| **1-vpcsc** | `access_levels`, `perimeters`, `policies` | Module-Backed (Factory) | `vpc-sc` |
+| **2-networking** | `vpcs` | Module-Backed (Factory) | `net-vpc-factory` |
+| **2-networking** | `projects` | Module-Backed (Factory) | `project-factory` |
+| **2-networking** | `dns` (Zones) | Stage-Implemented (Module) | `dns` |
+| **2-networking** | `dns_response_policies` | Stage-Implemented (Module) | `dns-response-policy` |
+| **2-networking** | `firewall_policies` | Stage-Implemented (Module) | `net-firewall-policy` |
+| **2-networking** | `vpns` | Stage-Implemented (Module) | `net-vpn-ha` |
+| **2-networking** | `ncc_hubs` | Stage-Implemented (Resource) | `google_network_connectivity_hub` |
+| **2-networking** | `ncc_groups` | Stage-Implemented (Resource) | `google_network_connectivity_group` |
+| **2-networking** | `nvas` | Native (Complex) | `compute-vm`, `net-lb-int` |
+| **2-project-factory** | `projects`, `folders`, `budgets` | Module-Backed (Factory) | `project-factory` |
+| **2-project-factory** | `vpcs` | Module-Backed (Factory) | `net-vpc-factory` |
+| **2-security** | `projects` | Module-Backed (Factory) | `project-factory` |
+| **2-security** | `certificate_authorities` | Stage-Implemented (Module) | `certificate-authority-service` |
+| **2-security** | `keyrings` (KMS) | Stage-Implemented (Module) | `kms` |
+| **3-secops-dev** | `rules`, `reference_lists` | Module-Backed (Factory) | `secops-rules` |
+
+## Maintenance Guide
+
+This documentation is maintained to track factory patterns across the `modules` and `fast/stages` directories.
+
+### To Update
+
+#### 1. Modules Analysis
+
+1. **Identify Configuration:** Search for `variable "factories_config"` in typically `modules/your-module/variables.tf`.
+2. **Determine Keys:** Inspect the `factories_config` type (e.g., `object({ ... })`) to identify the keys like `rules`, `vpcs`, `projects`.
+3. **Find Usage:** Search for `var.factories_config.KEY` in the module's `main.tf` or `factory.tf` to see how the data is used.
+4. **Classify Resources:** Determine whether the factory logic creates module resources (e.g., `google_project`) or iterates a sub-module.
+5. **List Dependencies:** Note any module-level variables (e.g., `project_id`, `name`) that are injected into the factory-created resources.
+
+#### 2. FAST Stages Analysis
+
+1. **Identify Configuration:** Search for `variable "factories_config"` in `fast/stages/your-stage/variables.tf`.
+2. **Find Usage:** Search for `var.factories_config.KEY` in the stage's implementation (often in `factory*.tf`).
+3. **Classify Implementation**:
+ * **Module-Backed (Factory)**: The `factories_config` path is passed directly to an underlying module (e.g., `project-factory`).
+ * **Stage-Implemented (Module)**: The stage explicitly loads the YAML/files and iterates over a standard module (e.g., `dns` module).
+ * **Stage-Implemented (Resource)**: The stage explicitly loads the YAML/files and iterates over raw Terraform resources (e.g., `google_network_connectivity_hub`).
+ * **Native (Complex)**: The stage implements complex logic combining multiple modules/resources (e.g., combining `compute-vm` and `net-lb-int` for NVAs).
diff --git a/fast/stages/0-org-setup/schemas/budget.schema.json b/fast/stages/0-org-setup/schemas/budget.schema.json
index 61a97730a..af145dcc1 100644
--- a/fast/stages/0-org-setup/schemas/budget.schema.json
+++ b/fast/stages/0-org-setup/schemas/budget.schema.json
@@ -42,7 +42,17 @@
"include_specified": {
"type": "array",
"items": {
- "type": "string"
+ "type": "string",
+ "enum": [
+ "COMMITTED_USAGE_DISCOUNT",
+ "COMMITTED_USAGE_DISCOUNT_DOLLAR_BASE",
+ "DISCOUNT",
+ "FREE_TIER",
+ "PROMOTION",
+ "RESELLER_MARGIN",
+ "SUBSCRIPTION_BENEFIT",
+ "SUSTAINED_USAGE_DISCOUNT"
+ ]
}
}
}
diff --git a/modules/billing-account/README.md b/modules/billing-account/README.md
index a9d9788e5..563370fac 100644
--- a/modules/billing-account/README.md
+++ b/modules/billing-account/README.md
@@ -281,17 +281,17 @@ update_rules:
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [id](variables.tf#L148) | Billing account id. | string | ✓ | |
+| [id](variables.tf#L165) | Billing account id. | string | ✓ | |
| [budget_notification_channels](variables.tf#L17) | Notification channels used by budget alerts. | map(object({…})) | | {} |
| [budgets](variables.tf#L47) | Billing budgets. Notification channels are either keys in corresponding variable, or external ids. | map(object({…})) | | {} |
-| [context](variables.tf#L122) | Context-specific interpolations. | object({…}) | | {} |
-| [factories_config](variables.tf#L139) | Path to folder containing budget alerts data files. | object({…}) | | {} |
+| [context](variables.tf#L139) | Context-specific interpolations. | object({…}) | | {} |
+| [factories_config](variables.tf#L156) | Path to folder containing budget alerts data files. | object({…}) | | {} |
| [iam](variables-iam.tf#L17) | IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} |
| [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} |
| [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} |
| [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} |
-| [logging_sinks](variables.tf#L153) | Logging sinks to create for the billing account. | map(object({…})) | | {} |
-| [projects](variables.tf#L186) | Projects associated with this billing account. | list(string) | | [] |
+| [logging_sinks](variables.tf#L170) | Logging sinks to create for the billing account. | map(object({…})) | | {} |
+| [projects](variables.tf#L203) | Projects associated with this billing account. | list(string) | | [] |
## Outputs
diff --git a/modules/billing-account/budgets.tf b/modules/billing-account/budgets.tf
index 6e7324c79..f227919b6 100644
--- a/modules/billing-account/budgets.tf
+++ b/modules/billing-account/budgets.tf
@@ -89,6 +89,7 @@ resource "google_billing_budget" "default" {
: "INCLUDE_ALL_CREDITS"
)
)
+ credit_types = try(each.value.filter.credit_types_treatment.include_specified, null)
labels = each.value.filter.label == null ? null : {
(each.value.filter.label.key) = each.value.filter.label.value
}
diff --git a/modules/billing-account/factory.tf b/modules/billing-account/factory.tf
index 3ee896db6..789b6f736 100644
--- a/modules/billing-account/factory.tf
+++ b/modules/billing-account/factory.tf
@@ -102,4 +102,21 @@ check "factory_budgets" {
]))
error_message = "Notification rules need either a pubsub topic or monitoring channels defined."
}
+ assert {
+ condition = alltrue(flatten([
+ for k, v in local.factory_budgets : [
+ for c in try(v.filter.credit_types_treatment.include_specified, []) : contains([
+ "COMMITTED_USAGE_DISCOUNT",
+ "COMMITTED_USAGE_DISCOUNT_DOLLAR_BASE",
+ "DISCOUNT",
+ "FREE_TIER",
+ "PROMOTION",
+ "RESELLER_MARGIN",
+ "SUBSCRIPTION_BENEFIT",
+ "SUSTAINED_USAGE_DISCOUNT"
+ ], c) if c != null
+ ]
+ ]))
+ error_message = "Budget filter credit types must be one of COMMITTED_USAGE_DISCOUNT, COMMITTED_USAGE_DISCOUNT_DOLLAR_BASE, DISCOUNT, FREE_TIER, PROMOTION, RESELLER_MARGIN, SUBSCRIPTION_BENEFIT, SUSTAINED_USAGE_DISCOUNT."
+ }
}
diff --git a/modules/billing-account/schemas/budget.schema.json b/modules/billing-account/schemas/budget.schema.json
index 61a97730a..af145dcc1 100644
--- a/modules/billing-account/schemas/budget.schema.json
+++ b/modules/billing-account/schemas/budget.schema.json
@@ -42,7 +42,17 @@
"include_specified": {
"type": "array",
"items": {
- "type": "string"
+ "type": "string",
+ "enum": [
+ "COMMITTED_USAGE_DISCOUNT",
+ "COMMITTED_USAGE_DISCOUNT_DOLLAR_BASE",
+ "DISCOUNT",
+ "FREE_TIER",
+ "PROMOTION",
+ "RESELLER_MARGIN",
+ "SUBSCRIPTION_BENEFIT",
+ "SUSTAINED_USAGE_DISCOUNT"
+ ]
}
}
}
diff --git a/modules/billing-account/variables.tf b/modules/billing-account/variables.tf
index 6da7c29b6..96b0087c2 100644
--- a/modules/billing-account/variables.tf
+++ b/modules/billing-account/variables.tf
@@ -117,6 +117,23 @@ variable "budgets" {
]))
error_message = "Budget notification rules need either a pubsub topic or monitoring channels defined."
}
+ validation {
+ condition = alltrue(flatten([
+ for k, v in var.budgets : [
+ for c in try(v.filter.credit_types_treatment.include_specified, []) : contains([
+ "COMMITTED_USAGE_DISCOUNT",
+ "COMMITTED_USAGE_DISCOUNT_DOLLAR_BASE",
+ "DISCOUNT",
+ "FREE_TIER",
+ "PROMOTION",
+ "RESELLER_MARGIN",
+ "SUBSCRIPTION_BENEFIT",
+ "SUSTAINED_USAGE_DISCOUNT"
+ ], c) if c != null
+ ]
+ ]))
+ error_message = "Budget filter credit types must be one of COMMITTED_USAGE_DISCOUNT, COMMITTED_USAGE_DISCOUNT_DOLLAR_BASE, DISCOUNT, FREE_TIER, PROMOTION, RESELLER_MARGIN, SUBSCRIPTION_BENEFIT, SUSTAINED_USAGE_DISCOUNT."
+ }
}
variable "context" {
diff --git a/modules/gke-nodepool/README.md b/modules/gke-nodepool/README.md
index 665b1555f..632332e2a 100644
--- a/modules/gke-nodepool/README.md
+++ b/modules/gke-nodepool/README.md
@@ -187,6 +187,31 @@ module "cluster-1-nodepool-dws" {
}
# tftest modules=1 resources=2 inventory=dws.yaml
```
+### Hyperdisk Balanced
+
+This example shows how to configure Hyperdisk Balanced with provisioned IOPS and throughput.
+
+```hcl
+module "cluster-1-nodepool-hyperdisk" {
+ source = "./fabric/modules/gke-nodepool"
+ project_id = "myproject"
+ cluster_name = "cluster-1"
+ location = "europe-west4-a"
+ name = "nodepool-hyperdisk"
+ node_config = {
+ machine_type = "c3-standard-4"
+ boot_disk = {
+ image_type = "COS_CONTAINERD"
+ type = "hyperdisk-balanced"
+ size_gb = 100
+ provisioned_iops = 3000
+ provisioned_throughput = 140
+ }
+ }
+}
+# tftest modules=1 resources=1 inventory=hyperdisk.yaml
+```
+
## Variables
@@ -194,7 +219,7 @@ module "cluster-1-nodepool-dws" {
|---|---|:---:|:---:|:---:|
| [cluster_name](variables.tf#L23) | Cluster name. | string | ✓ | |
| [location](variables.tf#L48) | Cluster location. | string | ✓ | |
-| [project_id](variables.tf#L216) | Cluster project id. | string | ✓ | |
+| [project_id](variables.tf#L223) | Cluster project id. | string | ✓ | |
| [cluster_id](variables.tf#L17) | Cluster id. Optional, but providing cluster_id is recommended to prevent cluster misconfiguration in some of the edge cases. | string | | null |
| [gke_version](variables.tf#L28) | Kubernetes nodes version. Ignored if auto_upgrade is set in management_config. | string | | null |
| [k8s_labels](variables.tf#L34) | Kubernetes labels applied to each node. | map(string) | | {} |
@@ -202,16 +227,16 @@ module "cluster-1-nodepool-dws" {
| [max_pods_per_node](variables.tf#L53) | Maximum number of pods per node. | number | | null |
| [name](variables.tf#L59) | Optional nodepool name. | string | | null |
| [network_config](variables.tf#L65) | Network configuration. | object({…}) | | null |
-| [node_config](variables.tf#L89) | Node-level configuration. | object({…}) | | {} |
-| [node_count](variables.tf#L162) | Number of nodes per instance group. Initial value can only be changed by recreation, current is ignored when autoscaling is used. | object({…}) | | {…} |
-| [node_locations](variables.tf#L174) | Node locations. | list(string) | | null |
-| [nodepool_config](variables.tf#L180) | Nodepool-level configuration. | object({…}) | | null |
-| [reservation_affinity](variables.tf#L221) | Configuration of the desired reservation which instances could take capacity from. | object({…}) | | null |
-| [resource_manager_tags](variables.tf#L231) | A map of resource manager tag keys and values to be attached to the nodes for managing Compute Engine firewalls using Network Firewall Policies. | map(string) | | null |
-| [service_account](variables.tf#L237) | Nodepool service account. If this variable is set to null, the default GCE service account will be used. If set and email is null, a service account will be created. If scopes are null a default will be used. | object({…}) | | {} |
-| [sole_tenant_nodegroup](variables.tf#L249) | Sole tenant node group. | string | | null |
-| [tags](variables.tf#L255) | Network tags applied to nodes. | list(string) | | null |
-| [taints](variables.tf#L261) | Kubernetes taints applied to all nodes. | map(object({…})) | | {} |
+| [node_config](variables.tf#L89) | Node-level configuration. | object({…}) | | {} |
+| [node_count](variables.tf#L169) | Number of nodes per instance group. Initial value can only be changed by recreation, current is ignored when autoscaling is used. | object({…}) | | {…} |
+| [node_locations](variables.tf#L181) | Node locations. | list(string) | | null |
+| [nodepool_config](variables.tf#L187) | Nodepool-level configuration. | object({…}) | | null |
+| [reservation_affinity](variables.tf#L228) | Configuration of the desired reservation which instances could take capacity from. | object({…}) | | null |
+| [resource_manager_tags](variables.tf#L238) | A map of resource manager tag keys and values to be attached to the nodes for managing Compute Engine firewalls using Network Firewall Policies. | map(string) | | null |
+| [service_account](variables.tf#L244) | Nodepool service account. If this variable is set to null, the default GCE service account will be used. If set and email is null, a service account will be created. If scopes are null a default will be used. | object({…}) | | {} |
+| [sole_tenant_nodegroup](variables.tf#L256) | Sole tenant node group. | string | | null |
+| [tags](variables.tf#L262) | Network tags applied to nodes. | list(string) | | null |
+| [taints](variables.tf#L268) | Kubernetes taints applied to all nodes. | map(object({…})) | | {} |
## Outputs
diff --git a/modules/gke-nodepool/main.tf b/modules/gke-nodepool/main.tf
index b2ab2021a..19c167547 100644
--- a/modules/gke-nodepool/main.tf
+++ b/modules/gke-nodepool/main.tf
@@ -192,20 +192,28 @@ resource "google_container_node_pool" "nodepool" {
}
node_config {
- boot_disk_kms_key = var.node_config.boot_disk_kms_key
- disk_size_gb = var.node_config.disk_size_gb
- disk_type = var.node_config.disk_type
- image_type = var.node_config.image_type
- labels = var.k8s_labels
- resource_labels = var.labels
- local_ssd_count = var.node_config.local_ssd_count
- machine_type = var.node_config.machine_type
- metadata = local.node_metadata
- min_cpu_platform = var.node_config.min_cpu_platform
- node_group = var.sole_tenant_nodegroup
- oauth_scopes = local.service_account_scopes
- preemptible = var.node_config.preemptible
- service_account = local.service_account_email
+ boot_disk_kms_key = try(var.node_config.boot_disk.kms_key, var.node_config.boot_disk_kms_key)
+ boot_disk {
+ size_gb = try(var.node_config.boot_disk.size_gb, var.node_config.disk_size_gb)
+ disk_type = try(var.node_config.boot_disk.type, var.node_config.disk_type)
+ provisioned_iops = try(
+ var.node_config.boot_disk.provisioned_iops, null
+ )
+ provisioned_throughput = try(
+ var.node_config.boot_disk.provisioned_throughput, null
+ )
+ }
+ image_type = var.node_config.image_type
+ labels = var.k8s_labels
+ resource_labels = var.labels
+ local_ssd_count = var.node_config.local_ssd_count
+ machine_type = var.node_config.machine_type
+ metadata = local.node_metadata
+ min_cpu_platform = var.node_config.min_cpu_platform
+ node_group = var.sole_tenant_nodegroup
+ oauth_scopes = local.service_account_scopes
+ preemptible = var.node_config.preemptible
+ service_account = local.service_account_email
spot = (
var.node_config.spot == true && var.node_config.preemptible != true
)
diff --git a/modules/gke-nodepool/variables.tf b/modules/gke-nodepool/variables.tf
index afdd6cab2..08ca1d09a 100644
--- a/modules/gke-nodepool/variables.tf
+++ b/modules/gke-nodepool/variables.tf
@@ -89,9 +89,16 @@ variable "network_config" {
variable "node_config" {
description = "Node-level configuration."
type = object({
- boot_disk_kms_key = optional(string)
- disk_size_gb = optional(number)
- disk_type = optional(string, "pd-balanced")
+ boot_disk = optional(object({
+ kms_key = optional(string)
+ size_gb = optional(number)
+ type = optional(string)
+ provisioned_iops = optional(number)
+ provisioned_throughput = optional(number)
+ }))
+ boot_disk_kms_key = optional(string) # usage of this is discouraged
+ disk_size_gb = optional(number) # usage of this is discouraged
+ disk_type = optional(string, "pd-balanced") # usage of this is discouraged
ephemeral_ssd_count = optional(number)
gcfs = optional(bool, false)
guest_accelerator = optional(object({
diff --git a/modules/net-lb-app-ext-regional/README.md b/modules/net-lb-app-ext-regional/README.md
index eb685f9ea..77b6e96a9 100644
--- a/modules/net-lb-app-ext-regional/README.md
+++ b/modules/net-lb-app-ext-regional/README.md
@@ -15,6 +15,7 @@ The variable space of this module closely mirrors that of [net-lb-app-ext](../n
- [HTTP backends](#http-backends)
- [HTTPS backends](#https-backends)
- [HTTP to HTTPS redirect](#http-to-https-redirect)
+ - [Backend Authenticated TLS](#backend-authenticated-tls)
- [Health Checks](#health-checks)
- [Backend Types and Management](#backend-types-and-management)
- [Instance Groups](#instance-groups)
@@ -218,6 +219,32 @@ module "ralb-test-0" {
# tftest modules=5 resources=16 fixtures=fixtures/ssl-certificate.tf,fixtures/compute-vm-group-bc.tf e2e
```
+### Backend Authenticated TLS
+
+This example shows how to configure Backend Authenticated TLS using the `tls_settings` block.
+
+```hcl
+module "ralb-0" {
+ source = "./fabric/modules/net-lb-app-ext-regional"
+ project_id = var.project_id
+ name = "ralb-test-0"
+ vpc = var.vpc.self_link
+ region = var.region
+ backend_service_configs = {
+ default = {
+ backends = [
+ { backend = module.compute-vm-group-b.group.id },
+ ]
+ tls_settings = {
+ sni = "backend.example.com"
+ subject_alt_names = ["backend.example.com"]
+ }
+ }
+ }
+}
+# tftest modules=3 resources=9 fixtures=fixtures/compute-vm-group-bc.tf inventory=tls-settings.yaml
+```
+
### Health Checks
You can leverage externally defined health checks for backend services, or have the module create them for you.
@@ -808,7 +835,7 @@ For deploying changes to load balancer configuration please refer to [net-lb-app
| [region](variables.tf#L217) | Region where the load balancer is created. | string | ✓ | |
| [vpc](variables.tf#L237) | VPC-level configuration. | string | ✓ | |
| [address](variables.tf#L17) | Optional IP address used for the forwarding rule. | string | | null |
-| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} |
+| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} |
| [description](variables.tf#L23) | Optional description used for resources. | string | | "Terraform managed." |
| [group_configs](variables.tf#L29) | Optional unmanaged groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} |
| [health_check_configs](variables-health-check.tf#L19) | Optional auto-created health check configurations, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | map(object({…})) | | {…} |
diff --git a/modules/net-lb-app-ext-regional/backend-service.tf b/modules/net-lb-app-ext-regional/backend-service.tf
index 1bb8b7a6e..a5f533267 100644
--- a/modules/net-lb-app-ext-regional/backend-service.tf
+++ b/modules/net-lb-app-ext-regional/backend-service.tf
@@ -247,4 +247,18 @@ resource "google_compute_region_backend_service" "default" {
}
}
}
+
+ dynamic "tls_settings" {
+ for_each = each.value.tls_settings == null ? [] : [each.value.tls_settings]
+ content {
+ authentication_config = tls_settings.value.authentication_config
+ sni = tls_settings.value.sni
+ dynamic "subject_alt_names" {
+ for_each = tls_settings.value.subject_alt_names == null ? [] : tls_settings.value.subject_alt_names
+ content {
+ dns_name = subject_alt_names.value
+ }
+ }
+ }
+ }
}
diff --git a/modules/net-lb-app-ext-regional/variables-backend-service.tf b/modules/net-lb-app-ext-regional/variables-backend-service.tf
index 42f1b8995..4a0cf25f0 100644
--- a/modules/net-lb-app-ext-regional/variables-backend-service.tf
+++ b/modules/net-lb-app-ext-regional/variables-backend-service.tf
@@ -120,6 +120,11 @@ variable "backend_service_configs" {
nanos = optional(number)
}))
}))
+ tls_settings = optional(object({
+ authentication_config = optional(string)
+ sni = optional(string)
+ subject_alt_names = optional(list(string))
+ }))
}))
default = {}
nullable = false
diff --git a/modules/net-lb-app-ext/README.md b/modules/net-lb-app-ext/README.md
index a1755fe29..d7be6fd12 100644
--- a/modules/net-lb-app-ext/README.md
+++ b/modules/net-lb-app-ext/README.md
@@ -28,6 +28,7 @@ Due to the complexity of the underlying resources, changes to the configuration
- [Cross Project Backend](#cross-project-backend)
- [URL Map](#url-map)
- [SSL Certificates](#ssl-certificates)
+ - [Backend Authenticated TLS](#backend-authenticated-tls)
- [Complex example](#complex-example)
- [Deploying changes to load balancer configurations](#deploying-changes-to-load-balancer-configurations)
- [Changing the Network Endpoint Group](#changing-the-network-endpoint-group)
@@ -762,6 +763,29 @@ module "glb-0" {
# tftest modules=3 resources=12 fixtures=fixtures/compute-vm-group-bc.tf inventory=ssl-certificates.yaml e2e
```
+### Backend Authenticated TLS
+
+This example shows how to configure Backend Authenticated TLS using the `tls_settings` block.
+
+```hcl
+module "glb-0" {
+ source = "./fabric/modules/net-lb-app-ext"
+ project_id = var.project_id
+ name = "glb-test-0"
+ backend_service_configs = {
+ default = {
+ backends = [
+ { backend = module.compute-vm-group-b.group.id },
+ ]
+ tls_settings = {
+ sni = "backend.example.com"
+ }
+ }
+ }
+}
+# tftest modules=3 resources=9 fixtures=fixtures/compute-vm-group-bc.tf inventory=tls-settings.yaml
+```
+
### Complex example
This example mixes group and NEG backends, and shows how to set HTTPS for specific backends.
@@ -1065,7 +1089,7 @@ After provisioning this change, and verifying that the new certificate is provis
| [name](variables.tf#L126) | Load balancer name. | string | ✓ | |
| [project_id](variables.tf#L241) | Project id. | string | ✓ | |
| [backend_buckets_config](variables.tf#L17) | Backend buckets configuration. | map(object({…})) | | {} |
-| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) })) | | {} |
+| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} |
| [description](variables.tf#L52) | Optional description used for resources. | string | | "Terraform managed." |
| [forwarding_rules_config](variables.tf#L58) | The optional forwarding rules configuration. | map(object({…})) | | {…} |
| [group_configs](variables.tf#L79) | Optional unmanaged groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} |
diff --git a/modules/net-lb-app-ext/backend-service.tf b/modules/net-lb-app-ext/backend-service.tf
index ec9cfabbe..0bd35bfef 100644
--- a/modules/net-lb-app-ext/backend-service.tf
+++ b/modules/net-lb-app-ext/backend-service.tf
@@ -292,4 +292,11 @@ resource "google_compute_backend_service" "default" {
}
}
}
+
+ dynamic "tls_settings" {
+ for_each = each.value.tls_settings == null ? [] : [each.value.tls_settings]
+ content {
+ sni = tls_settings.value.sni
+ }
+ }
}
diff --git a/modules/net-lb-app-ext/variables-backend-service.tf b/modules/net-lb-app-ext/variables-backend-service.tf
index 7e702a7b6..20d58d7f4 100644
--- a/modules/net-lb-app-ext/variables-backend-service.tf
+++ b/modules/net-lb-app-ext/variables-backend-service.tf
@@ -142,7 +142,11 @@ variable "backend_service_configs" {
access_key_version = optional(string)
origin_region = optional(string)
}))
- })) }))
+ }))
+ tls_settings = optional(object({
+ sni = optional(string)
+ }))
+ }))
default = {}
nullable = false
validation {
diff --git a/modules/net-lb-app-int/README.md b/modules/net-lb-app-int/README.md
index 10876d9a1..b1a4612b5 100644
--- a/modules/net-lb-app-int/README.md
+++ b/modules/net-lb-app-int/README.md
@@ -21,6 +21,7 @@ Due to the complexity of the underlying resources, changes to the configuration
- [Internet NEG creation](#internet-neg-creation)
- [URL Map](#url-map)
- [SSL Certificates](#ssl-certificates)
+ - [Backend Authenticated TLS](#backend-authenticated-tls)
- [PSC service attachment](#psc-service-attachment)
- [Complex example](#complex-example)
- [Deploying changes to load balancer configurations](#deploying-changes-to-load-balancer-configurations)
@@ -634,6 +635,36 @@ module "ilb-l7" {
# tftest modules=1 resources=8
```
+### Backend Authenticated TLS
+
+This example shows how to configure Backend Authenticated TLS using the `tls_settings` block.
+
+```hcl
+module "ilb-l7" {
+ source = "./fabric/modules/net-lb-app-int"
+ name = "ilb-test"
+ project_id = var.project_id
+ region = "europe-west1"
+ backend_service_configs = {
+ default = {
+ backends = [{
+ group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig"
+ }]
+ tls_settings = {
+ # authentication_config = "projects/myprj/locations/europe-west1/backendTlsPolicies/my-policy"
+ sni = "backend.example.com"
+ subject_alt_names = ["backend.example.com"]
+ }
+ }
+ }
+ vpc_config = {
+ network = var.vpc.self_link
+ subnetwork = var.subnet.self_link
+ }
+}
+# tftest modules=1 resources=5 inventory=tls-settings.yaml
+```
+
### PSC service attachment
The optional `service_attachment` variable allows [publishing Private Service Connect service](https://cloud.google.com/vpc/docs/configure-private-service-connect-producer) by configuring service attachment for the forwarding rule.
@@ -826,7 +857,7 @@ For deploying changes to load balancer configuration please refer to [net-lb-app
| [region](variables.tf#L196) | The region where to allocate the ILB resources. | string | ✓ | |
| [vpc_config](variables.tf#L239) | VPC-level configuration. | object({…}) | ✓ | |
| [address](variables.tf#L17) | Optional IP address used for the forwarding rule. | string | | null |
-| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} |
+| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} |
| [description](variables.tf#L23) | Optional description used for resources. | string | | "Terraform managed." |
| [global_access](variables.tf#L30) | Allow client access from all regions. | bool | | null |
| [group_configs](variables.tf#L36) | Optional unmanaged groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} |
diff --git a/modules/net-lb-app-int/backend-service.tf b/modules/net-lb-app-int/backend-service.tf
index 58864adaf..85b39e9bb 100644
--- a/modules/net-lb-app-int/backend-service.tf
+++ b/modules/net-lb-app-int/backend-service.tf
@@ -220,4 +220,20 @@ resource "google_compute_region_backend_service" "default" {
policy = "CONSISTENT_HASH_SUBSETTING"
}
}
+
+ dynamic "tls_settings" {
+ for_each = each.value.tls_settings == null ? [] : [each.value.tls_settings]
+ content {
+ # authentication_config is not supported by the beta provider in this resource?
+ # Wait, lint will tell me. Search result said yes.
+ authentication_config = tls_settings.value.authentication_config
+ sni = tls_settings.value.sni
+ dynamic "subject_alt_names" {
+ for_each = tls_settings.value.subject_alt_names == null ? [] : tls_settings.value.subject_alt_names
+ content {
+ dns_name = subject_alt_names.value
+ }
+ }
+ }
+ }
}
diff --git a/modules/net-lb-app-int/variables-backend-service.tf b/modules/net-lb-app-int/variables-backend-service.tf
index fbd89f8af..f38019fb7 100644
--- a/modules/net-lb-app-int/variables-backend-service.tf
+++ b/modules/net-lb-app-int/variables-backend-service.tf
@@ -97,6 +97,11 @@ variable "backend_service_configs" {
nanos = optional(number)
}))
}))
+ tls_settings = optional(object({
+ authentication_config = optional(string)
+ sni = optional(string)
+ subject_alt_names = optional(list(string))
+ }))
}))
default = {}
nullable = false
diff --git a/modules/project-factory/schemas/budget.schema.json b/modules/project-factory/schemas/budget.schema.json
index 61a97730a..af145dcc1 100644
--- a/modules/project-factory/schemas/budget.schema.json
+++ b/modules/project-factory/schemas/budget.schema.json
@@ -42,7 +42,17 @@
"include_specified": {
"type": "array",
"items": {
- "type": "string"
+ "type": "string",
+ "enum": [
+ "COMMITTED_USAGE_DISCOUNT",
+ "COMMITTED_USAGE_DISCOUNT_DOLLAR_BASE",
+ "DISCOUNT",
+ "FREE_TIER",
+ "PROMOTION",
+ "RESELLER_MARGIN",
+ "SUBSCRIPTION_BENEFIT",
+ "SUSTAINED_USAGE_DISCOUNT"
+ ]
}
}
}
diff --git a/tests/fast/stages/s3_gke_dev/hardened.yaml b/tests/fast/stages/s3_gke_dev/hardened.yaml
index 4ad059e05..3d0b3106e 100644
--- a/tests/fast/stages/s3_gke_dev/hardened.yaml
+++ b/tests/fast/stages/s3_gke_dev/hardened.yaml
@@ -245,8 +245,9 @@ values:
name: mynodepool
node_config:
- advanced_machine_features: []
+ boot_disk:
+ - disk_type: pd-balanced
boot_disk_kms_key: projects/prj-host/locations/europe-west1/keyRings/dev-primary-default/cryptoKeys/gke
- disk_type: pd-balanced
enable_confidential_storage: null
ephemeral_storage_config: []
ephemeral_storage_local_ssd_config: []
diff --git a/tests/fast/stages/s3_gke_dev/simple.yaml b/tests/fast/stages/s3_gke_dev/simple.yaml
index 0c7a5e2ab..8954dddfb 100644
--- a/tests/fast/stages/s3_gke_dev/simple.yaml
+++ b/tests/fast/stages/s3_gke_dev/simple.yaml
@@ -203,8 +203,9 @@ values:
name: mynodepool
node_config:
- advanced_machine_features: []
+ boot_disk:
+ - disk_type: pd-balanced
boot_disk_kms_key: null
- disk_type: pd-balanced
enable_confidential_storage: null
ephemeral_storage_config: []
ephemeral_storage_local_ssd_config: []
diff --git a/tests/modules/gke_nodepool/examples/dws.yaml b/tests/modules/gke_nodepool/examples/dws.yaml
index 07a770c3e..680c4b39b 100644
--- a/tests/modules/gke_nodepool/examples/dws.yaml
+++ b/tests/modules/gke_nodepool/examples/dws.yaml
@@ -19,8 +19,9 @@ values:
name: nodepool-dws
node_config:
- boot_disk_kms_key: null
- disk_size_gb: 50
- disk_type: pd-ssd
+ boot_disk:
+ - size_gb: 50
+ disk_type: pd-ssd
ephemeral_storage_config:
- local_ssd_count: 1
ephemeral_storage_local_ssd_config: []
diff --git a/tests/modules/gke_nodepool/examples/guest-accelerator.yaml b/tests/modules/gke_nodepool/examples/guest-accelerator.yaml
index ea63d37bf..78f2e062d 100644
--- a/tests/modules/gke_nodepool/examples/guest-accelerator.yaml
+++ b/tests/modules/gke_nodepool/examples/guest-accelerator.yaml
@@ -19,8 +19,9 @@ values:
name: nodepool-gpu-1
node_config:
- boot_disk_kms_key: null
- disk_size_gb: 50
- disk_type: pd-ssd
+ boot_disk:
+ - size_gb: 50
+ disk_type: pd-ssd
ephemeral_storage_config:
- local_ssd_count: 1
ephemeral_storage_local_ssd_config: []
diff --git a/tests/modules/gke_nodepool/examples/hyperdisk.yaml b/tests/modules/gke_nodepool/examples/hyperdisk.yaml
new file mode 100644
index 000000000..07ed33001
--- /dev/null
+++ b/tests/modules/gke_nodepool/examples/hyperdisk.yaml
@@ -0,0 +1,30 @@
+# 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.
+# You may obtain a copy of the License at
+#
+# http://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.cluster-1-nodepool-hyperdisk.google_container_node_pool.nodepool:
+ cluster: cluster-1
+ location: europe-west4-a
+ name: nodepool-hyperdisk
+ project: myproject
+ node_config:
+ - machine_type: c3-standard-4
+ boot_disk:
+ - disk_type: hyperdisk-balanced
+ size_gb: 100
+ provisioned_iops: 3000
+ provisioned_throughput: 140
+
+counts:
+ google_container_node_pool: 1
diff --git a/tests/modules/net_lb_app_ext/examples/tls-settings.yaml b/tests/modules/net_lb_app_ext/examples/tls-settings.yaml
new file mode 100644
index 000000000..86018ea12
--- /dev/null
+++ b/tests/modules/net_lb_app_ext/examples/tls-settings.yaml
@@ -0,0 +1,296 @@
+# 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
+#
+# 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.compute-vm-group-b.google_compute_instance.default[0]:
+ advanced_machine_features: []
+ allow_stopping_for_update: true
+ attached_disk: []
+ boot_disk:
+ - auto_delete: true
+ disk_encryption_key_raw: null
+ disk_encryption_key_rsa: null
+ disk_encryption_service_account: null
+ force_attach: null
+ initialize_params:
+ - enable_confidential_compute: null
+ image: cos-cloud/cos-stable
+ resource_manager_tags: null
+ size: 10
+ source_image_encryption_key: []
+ source_snapshot_encryption_key: []
+ storage_pool: null
+ type: pd-balanced
+ interface: null
+ mode: READ_WRITE
+ can_ip_forward: false
+ deletion_protection: false
+ description: Managed by the compute-vm Terraform module.
+ desired_status: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ enable_display: false
+ hostname: null
+ instance_encryption_key: []
+ key_revocation_action_type: NONE
+ labels: null
+ machine_type: f1-micro
+ metadata: null
+ metadata_startup_script: null
+ name: my-ig-b
+ network_interface:
+ - access_config: []
+ alias_ip_range: []
+ ipv6_access_config: []
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ nic_type: null
+ queue_count: null
+ security_policy: null
+ subnetwork: subnet_self_link
+ network_performance_config: []
+ params: []
+ partner_metadata: null
+ project: project-id
+ resource_policies: null
+ scheduling:
+ - automatic_restart: true
+ availability_domain: null
+ graceful_shutdown: []
+ host_error_timeout_seconds: null
+ instance_termination_action: null
+ local_ssd_recovery_timeout: []
+ maintenance_interval: null
+ max_run_duration: []
+ min_node_cpus: null
+ node_affinities: []
+ on_host_maintenance: MIGRATE
+ on_instance_stop_action: []
+ preemptible: false
+ provisioning_model: STANDARD
+ skip_guest_os_shutdown: false
+ termination_time: null
+ scratch_disk: []
+ service_account:
+ - scopes:
+ - https://www.googleapis.com/auth/devstorage.read_only
+ - https://www.googleapis.com/auth/logging.write
+ - https://www.googleapis.com/auth/monitoring.write
+ shielded_instance_config: []
+ tags: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ zone: europe-west8-b
+ module.compute-vm-group-b.google_compute_instance_group.unmanaged[0]:
+ description: Managed by the compute-vm Terraform module.
+ name: my-ig-b
+ named_port: []
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ project: project-id
+ timeouts: null
+ zone: europe-west8-b
+ module.compute-vm-group-c.google_compute_instance.default[0]:
+ advanced_machine_features: []
+ allow_stopping_for_update: true
+ attached_disk: []
+ boot_disk:
+ - auto_delete: true
+ disk_encryption_key_raw: null
+ disk_encryption_key_rsa: null
+ disk_encryption_service_account: null
+ force_attach: null
+ initialize_params:
+ - enable_confidential_compute: null
+ image: cos-cloud/cos-stable
+ resource_manager_tags: null
+ size: 10
+ source_image_encryption_key: []
+ source_snapshot_encryption_key: []
+ storage_pool: null
+ type: pd-balanced
+ interface: null
+ mode: READ_WRITE
+ can_ip_forward: false
+ deletion_protection: false
+ description: Managed by the compute-vm Terraform module.
+ desired_status: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ enable_display: false
+ hostname: null
+ instance_encryption_key: []
+ key_revocation_action_type: NONE
+ labels: null
+ machine_type: f1-micro
+ metadata: null
+ metadata_startup_script: null
+ name: my-ig-c
+ network_interface:
+ - access_config: []
+ alias_ip_range: []
+ ipv6_access_config: []
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ nic_type: null
+ queue_count: null
+ security_policy: null
+ subnetwork: subnet_self_link
+ network_performance_config: []
+ params: []
+ partner_metadata: null
+ project: project-id
+ resource_policies: null
+ scheduling:
+ - automatic_restart: true
+ availability_domain: null
+ graceful_shutdown: []
+ host_error_timeout_seconds: null
+ instance_termination_action: null
+ local_ssd_recovery_timeout: []
+ maintenance_interval: null
+ max_run_duration: []
+ min_node_cpus: null
+ node_affinities: []
+ on_host_maintenance: MIGRATE
+ on_instance_stop_action: []
+ preemptible: false
+ provisioning_model: STANDARD
+ skip_guest_os_shutdown: false
+ termination_time: null
+ scratch_disk: []
+ service_account:
+ - scopes:
+ - https://www.googleapis.com/auth/devstorage.read_only
+ - https://www.googleapis.com/auth/logging.write
+ - https://www.googleapis.com/auth/monitoring.write
+ shielded_instance_config: []
+ tags: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ zone: europe-west8-c
+ module.compute-vm-group-c.google_compute_instance_group.unmanaged[0]:
+ description: Managed by the compute-vm Terraform module.
+ name: my-ig-c
+ named_port: []
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ project: project-id
+ timeouts: null
+ zone: europe-west8-c
+ module.glb-0.google_compute_backend_service.default["default"]:
+ affinity_cookie_ttl_sec: null
+ circuit_breakers: []
+ compression_mode: null
+ connection_draining_timeout_sec: 300
+ consistent_hash: []
+ custom_metrics: []
+ custom_request_headers: null
+ custom_response_headers: null
+ description: Terraform managed.
+ dynamic_forwarding: []
+ edge_security_policy: null
+ enable_cdn: null
+ external_managed_migration_state: null
+ external_managed_migration_testing_percentage: null
+ ip_address_selection_policy: null
+ load_balancing_scheme: EXTERNAL
+ locality_lb_policies: []
+ locality_lb_policy: null
+ max_stream_duration: []
+ name: glb-test-0-default
+ network_pass_through_lb_traffic_policy: []
+ outlier_detection: []
+ params: []
+ port_name: http
+ project: project-id
+ protocol: HTTP
+ security_policy: null
+ security_settings: []
+ service_lb_policy: null
+ strong_session_affinity_cookie: []
+ timeouts: null
+ tls_settings:
+ - authentication_config: null
+ sni: backend.example.com
+ subject_alt_names: []
+ module.glb-0.google_compute_global_forwarding_rule.default[""]:
+ allow_psc_global_access: null
+ description: Terraform managed.
+ external_managed_backend_bucket_migration_state: null
+ external_managed_backend_bucket_migration_testing_percentage: null
+ ip_protocol: TCP
+ ip_version: IPV4
+ labels: null
+ load_balancing_scheme: EXTERNAL
+ metadata_filters: []
+ name: glb-test-0
+ no_automate_dns_zone: null
+ port_range: '80'
+ project: project-id
+ source_ip_ranges: null
+ timeouts: null
+ module.glb-0.google_compute_health_check.default["default"]:
+ check_interval_sec: 5
+ description: Terraform managed.
+ grpc_health_check: []
+ grpc_tls_health_check: []
+ healthy_threshold: 2
+ http2_health_check: []
+ http_health_check:
+ - host: null
+ port: null
+ port_name: null
+ port_specification: USE_SERVING_PORT
+ proxy_header: NONE
+ request_path: /
+ response: null
+ https_health_check: []
+ name: glb-test-0-default
+ project: project-id
+ source_regions: null
+ ssl_health_check: []
+ tcp_health_check: []
+ timeout_sec: 5
+ timeouts: null
+ unhealthy_threshold: 2
+ module.glb-0.google_compute_target_http_proxy.default[0]:
+ description: Terraform managed.
+ http_keep_alive_timeout_sec: null
+ name: glb-test-0
+ project: project-id
+ timeouts: null
+ module.glb-0.google_compute_url_map.default:
+ default_custom_error_response_policy: []
+ default_route_action: []
+ default_url_redirect: []
+ description: Terraform managed.
+ header_action: []
+ host_rule: []
+ name: glb-test-0
+ path_matcher: []
+ project: project-id
+ test: []
+ timeouts: null
+
+counts:
+ google_compute_backend_service: 1
+ google_compute_global_forwarding_rule: 1
+ google_compute_health_check: 1
+ google_compute_instance: 2
+ google_compute_instance_group: 2
+ google_compute_target_http_proxy: 1
+ google_compute_url_map: 1
+ modules: 3
+ resources: 9
+
+outputs: {}
diff --git a/tests/modules/net_lb_app_ext_regional/examples/tls-settings.yaml b/tests/modules/net_lb_app_ext_regional/examples/tls-settings.yaml
new file mode 100644
index 000000000..9d66af5de
--- /dev/null
+++ b/tests/modules/net_lb_app_ext_regional/examples/tls-settings.yaml
@@ -0,0 +1,302 @@
+# 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
+#
+# 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.compute-vm-group-b.google_compute_instance.default[0]:
+ advanced_machine_features: []
+ allow_stopping_for_update: true
+ attached_disk: []
+ boot_disk:
+ - auto_delete: true
+ disk_encryption_key_raw: null
+ disk_encryption_key_rsa: null
+ disk_encryption_service_account: null
+ force_attach: null
+ initialize_params:
+ - enable_confidential_compute: null
+ image: cos-cloud/cos-stable
+ resource_manager_tags: null
+ size: 10
+ source_image_encryption_key: []
+ source_snapshot_encryption_key: []
+ storage_pool: null
+ type: pd-balanced
+ interface: null
+ mode: READ_WRITE
+ can_ip_forward: false
+ deletion_protection: false
+ description: Managed by the compute-vm Terraform module.
+ desired_status: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ enable_display: false
+ hostname: null
+ instance_encryption_key: []
+ key_revocation_action_type: NONE
+ labels: null
+ machine_type: f1-micro
+ metadata: null
+ metadata_startup_script: null
+ name: my-ig-b
+ network_interface:
+ - access_config: []
+ alias_ip_range: []
+ ipv6_access_config: []
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ nic_type: null
+ queue_count: null
+ security_policy: null
+ subnetwork: subnet_self_link
+ network_performance_config: []
+ params: []
+ partner_metadata: null
+ project: project-id
+ resource_policies: null
+ scheduling:
+ - automatic_restart: true
+ availability_domain: null
+ graceful_shutdown: []
+ host_error_timeout_seconds: null
+ instance_termination_action: null
+ local_ssd_recovery_timeout: []
+ maintenance_interval: null
+ max_run_duration: []
+ min_node_cpus: null
+ node_affinities: []
+ on_host_maintenance: MIGRATE
+ on_instance_stop_action: []
+ preemptible: false
+ provisioning_model: STANDARD
+ skip_guest_os_shutdown: false
+ termination_time: null
+ scratch_disk: []
+ service_account:
+ - scopes:
+ - https://www.googleapis.com/auth/devstorage.read_only
+ - https://www.googleapis.com/auth/logging.write
+ - https://www.googleapis.com/auth/monitoring.write
+ shielded_instance_config: []
+ tags: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ zone: europe-west8-b
+ module.compute-vm-group-b.google_compute_instance_group.unmanaged[0]:
+ description: Managed by the compute-vm Terraform module.
+ name: my-ig-b
+ named_port: []
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ project: project-id
+ timeouts: null
+ zone: europe-west8-b
+ module.compute-vm-group-c.google_compute_instance.default[0]:
+ advanced_machine_features: []
+ allow_stopping_for_update: true
+ attached_disk: []
+ boot_disk:
+ - auto_delete: true
+ disk_encryption_key_raw: null
+ disk_encryption_key_rsa: null
+ disk_encryption_service_account: null
+ force_attach: null
+ initialize_params:
+ - enable_confidential_compute: null
+ image: cos-cloud/cos-stable
+ resource_manager_tags: null
+ size: 10
+ source_image_encryption_key: []
+ source_snapshot_encryption_key: []
+ storage_pool: null
+ type: pd-balanced
+ interface: null
+ mode: READ_WRITE
+ can_ip_forward: false
+ deletion_protection: false
+ description: Managed by the compute-vm Terraform module.
+ desired_status: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ enable_display: false
+ hostname: null
+ instance_encryption_key: []
+ key_revocation_action_type: NONE
+ labels: null
+ machine_type: f1-micro
+ metadata: null
+ metadata_startup_script: null
+ name: my-ig-c
+ network_interface:
+ - access_config: []
+ alias_ip_range: []
+ ipv6_access_config: []
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ nic_type: null
+ queue_count: null
+ security_policy: null
+ subnetwork: subnet_self_link
+ network_performance_config: []
+ params: []
+ partner_metadata: null
+ project: project-id
+ resource_policies: null
+ scheduling:
+ - automatic_restart: true
+ availability_domain: null
+ graceful_shutdown: []
+ host_error_timeout_seconds: null
+ instance_termination_action: null
+ local_ssd_recovery_timeout: []
+ maintenance_interval: null
+ max_run_duration: []
+ min_node_cpus: null
+ node_affinities: []
+ on_host_maintenance: MIGRATE
+ on_instance_stop_action: []
+ preemptible: false
+ provisioning_model: STANDARD
+ skip_guest_os_shutdown: false
+ termination_time: null
+ scratch_disk: []
+ service_account:
+ - scopes:
+ - https://www.googleapis.com/auth/devstorage.read_only
+ - https://www.googleapis.com/auth/logging.write
+ - https://www.googleapis.com/auth/monitoring.write
+ shielded_instance_config: []
+ tags: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ zone: europe-west8-c
+ module.compute-vm-group-c.google_compute_instance_group.unmanaged[0]:
+ description: Managed by the compute-vm Terraform module.
+ name: my-ig-c
+ named_port: []
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ project: project-id
+ timeouts: null
+ zone: europe-west8-c
+ module.ralb-0.google_compute_forwarding_rule.default:
+ all_ports: null
+ allow_global_access: null
+ allow_psc_global_access: null
+ backend_service: null
+ description: Terraform managed.
+ ip_collection: null
+ ip_protocol: TCP
+ is_mirroring_collector: null
+ labels: null
+ load_balancing_scheme: EXTERNAL_MANAGED
+ name: ralb-test-0
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ network_tier: STANDARD
+ no_automate_dns_zone: null
+ port_range: '80'
+ ports: null
+ project: project-id
+ recreate_closed_psc: false
+ region: europe-west8
+ service_label: null
+ source_ip_ranges: null
+ timeouts: null
+ module.ralb-0.google_compute_region_backend_service.default["default"]:
+ affinity_cookie_ttl_sec: null
+ circuit_breakers: []
+ connection_draining_timeout_sec: 300
+ connection_tracking_policy: []
+ consistent_hash: []
+ custom_metrics: []
+ description: Terraform managed.
+ dynamic_forwarding: []
+ enable_cdn: null
+ failover_policy: []
+ ha_policy: []
+ ip_address_selection_policy: null
+ load_balancing_scheme: EXTERNAL_MANAGED
+ locality_lb_policy: null
+ name: ralb-test-0-default
+ network: null
+ network_pass_through_lb_traffic_policy: []
+ outlier_detection: []
+ params: []
+ port_name: http
+ project: project-id
+ protocol: HTTP
+ region: europe-west8
+ security_policy: null
+ strong_session_affinity_cookie: []
+ subsetting: []
+ timeouts: null
+ tls_settings:
+ - authentication_config: null
+ sni: backend.example.com
+ subject_alt_names:
+ - dns_name: backend.example.com
+ uniform_resource_identifier: null
+ module.ralb-0.google_compute_region_health_check.default["default"]:
+ check_interval_sec: 5
+ description: Terraform managed.
+ grpc_health_check: []
+ grpc_tls_health_check: []
+ healthy_threshold: 2
+ http2_health_check: []
+ http_health_check:
+ - host: null
+ port: null
+ port_name: null
+ port_specification: USE_SERVING_PORT
+ proxy_header: NONE
+ request_path: /
+ response: null
+ https_health_check: []
+ name: ralb-test-0-default
+ project: project-id
+ region: europe-west8
+ ssl_health_check: []
+ tcp_health_check: []
+ timeout_sec: 5
+ timeouts: null
+ unhealthy_threshold: 2
+ module.ralb-0.google_compute_region_target_http_proxy.default[0]:
+ description: Terraform managed.
+ http_keep_alive_timeout_sec: null
+ name: ralb-test-0
+ project: project-id
+ region: europe-west8
+ timeouts: null
+ module.ralb-0.google_compute_region_url_map.default:
+ default_route_action: []
+ default_url_redirect: []
+ description: Terraform managed.
+ header_action: []
+ host_rule: []
+ name: ralb-test-0
+ path_matcher: []
+ project: project-id
+ region: europe-west8
+ test: []
+ timeouts: null
+
+counts:
+ google_compute_forwarding_rule: 1
+ google_compute_region_backend_service: 1
+ google_compute_region_health_check: 1
+ google_compute_region_target_http_proxy: 1
+ google_compute_region_url_map: 1
+ google_compute_instance: 2
+ google_compute_instance_group: 2
+ modules: 3
+ resources: 9
+
+outputs: {}
diff --git a/tests/modules/net_lb_app_int/examples/tls-settings.yaml b/tests/modules/net_lb_app_int/examples/tls-settings.yaml
new file mode 100644
index 000000000..ac9c7ed87
--- /dev/null
+++ b/tests/modules/net_lb_app_int/examples/tls-settings.yaml
@@ -0,0 +1,141 @@
+# 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
+#
+# 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.ilb-l7.google_compute_forwarding_rule.default:
+ all_ports: null
+ allow_global_access: null
+ allow_psc_global_access: null
+ backend_service: null
+ description: Terraform managed.
+ ip_collection: null
+ ip_protocol: TCP
+ is_mirroring_collector: null
+ labels: null
+ load_balancing_scheme: INTERNAL_MANAGED
+ name: ilb-test
+ network: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa
+ network_tier: PREMIUM
+ no_automate_dns_zone: null
+ port_range: '80'
+ ports: null
+ project: project-id
+ recreate_closed_psc: false
+ region: europe-west1
+ service_label: null
+ source_ip_ranges: null
+ subnetwork: subnet_self_link
+ timeouts: null
+ module.ilb-l7.google_compute_health_check.default["default"]:
+ check_interval_sec: 5
+ description: Terraform managed.
+ grpc_health_check: []
+ grpc_tls_health_check: []
+ healthy_threshold: 2
+ http2_health_check: []
+ http_health_check:
+ - host: null
+ port: null
+ port_name: null
+ port_specification: USE_SERVING_PORT
+ proxy_header: NONE
+ request_path: /
+ response: null
+ https_health_check: []
+ name: ilb-test-default
+ project: project-id
+ source_regions: null
+ ssl_health_check: []
+ tcp_health_check: []
+ timeout_sec: 5
+ timeouts: null
+ unhealthy_threshold: 2
+ module.ilb-l7.google_compute_region_backend_service.default["default"]:
+ affinity_cookie_ttl_sec: null
+ backend:
+ - balancing_mode: UTILIZATION
+ capacity_scaler: 1
+ custom_metrics: []
+ description: Terraform managed.
+ failover: false
+ group: projects/myprj/zones/europe-west1-a/instanceGroups/my-ig
+ max_connections: null
+ max_connections_per_endpoint: null
+ max_connections_per_instance: null
+ max_rate: null
+ max_rate_per_endpoint: null
+ max_rate_per_instance: null
+ max_utilization: null
+ traffic_duration: ''
+ circuit_breakers: []
+ connection_draining_timeout_sec: 300
+ connection_tracking_policy: []
+ consistent_hash: []
+ custom_metrics: []
+ description: Terraform managed.
+ dynamic_forwarding: []
+ enable_cdn: null
+ failover_policy: []
+ ha_policy: []
+ ip_address_selection_policy: null
+ load_balancing_scheme: INTERNAL_MANAGED
+ locality_lb_policy: null
+ name: ilb-test-default
+ network: null
+ network_pass_through_lb_traffic_policy: []
+ outlier_detection: []
+ params: []
+ project: project-id
+ protocol: HTTP
+ region: europe-west1
+ security_policy: null
+ strong_session_affinity_cookie: []
+ subsetting: []
+ timeouts: null
+ tls_settings:
+ - authentication_config: null
+ sni: backend.example.com
+ subject_alt_names:
+ - dns_name: backend.example.com
+ uniform_resource_identifier: null
+ module.ilb-l7.google_compute_region_target_http_proxy.default[0]:
+ description: Terraform managed.
+ http_keep_alive_timeout_sec: null
+ name: ilb-test
+ project: project-id
+ region: europe-west1
+ timeouts: null
+ module.ilb-l7.google_compute_region_url_map.default:
+ default_route_action: []
+ default_url_redirect: []
+ description: Terraform managed.
+ header_action: []
+ host_rule: []
+ name: ilb-test
+ path_matcher: []
+ project: project-id
+ region: europe-west1
+ test: []
+ timeouts: null
+
+counts:
+ google_compute_forwarding_rule: 1
+ google_compute_health_check: 1
+ google_compute_region_backend_service: 1
+ google_compute_region_target_http_proxy: 1
+ google_compute_region_url_map: 1
+ modules: 1
+ resources: 5
+
+outputs: {}