diff --git a/blueprints/README.md b/blueprints/README.md index c0b7687b..49bf16ab 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -8,7 +8,7 @@ Currently available blueprints: - **data solutions** - [GCE and GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms), [Cloud Composer version 2 private instance, supporting Shared VPC and external CMEK key](./data-solutions/composer-2), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion), [Data Platform](./data-solutions/data-platform-foundations), [Spinning up a foundation data pipeline on Google Cloud using Cloud Storage, Dataflow and BigQuery](./data-solutions/gcs-to-bq-with-least-privileges), [#SQL Server Always On Groups blueprint](./data-solutions/sqlserver-alwayson), [Data Playground](./data-solutions/data-playground) - **factories** - [The why and the how of Resource Factories](./factories), [Google Cloud Identity Group Factory](./factories/cloud-identity-group-factory), [Google Cloud BQ Factory](./factories/bigquery-factory), [Google Cloud VPC Firewall Factory](./factories/net-vpc-firewall-yaml), [Minimal Project Factory](./factories/project-factory) - **GKE** - [Binary Authorization Pipeline Blueprint](./gke/binauthz), [Storage API](./gke/binauthz/image), [Multi-cluster mesh on GKE (fleet API)](./gke/multi-cluster-mesh-gke-fleet-api), [GKE Multitenant Blueprint](./gke/multitenant-fleet), [Shared VPC with GKE support](./networking/shared-vpc-gke/) -- **networking** - [Decentralized firewall management](./networking/decentralized-firewall), [Decentralized firewall validator](./networking/decentralized-firewall/validator), [Network filtering with Squid](./networking/filtering-proxy), [Network filtering with Squid with isolated VPCs using Private Service Connect](./networking/filtering-proxy-psc), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Hub and Spoke via VPN](./networking/hub-and-spoke-vpn), [Hub and Spoke via VPC Peering](./networking/hub-and-spoke-peering), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), [Nginx-based reverse proxy cluster](./networking/nginx-reverse-proxy-cluster), [On-prem DNS and Google Private Access](./networking/onprem-google-access-dns), [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke) +- **networking** - [Decentralized firewall management](./networking/decentralized-firewall), [Decentralized firewall validator](./networking/decentralized-firewall/validator), [Network filtering with Squid](./networking/filtering-proxy), [Network filtering with Squid with isolated VPCs using Private Service Connect](./networking/filtering-proxy-psc), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Hub and Spoke via VPN](./networking/hub-and-spoke-vpn), [Hub and Spoke via VPC Peering](./networking/hub-and-spoke-peering), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), [On-prem DNS and Google Private Access](./networking/onprem-google-access-dns), [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke) - **serverless** - [Creating multi-region deployments for API Gateway](./serverless/api-gateway) - **third party solutions** - [OpenShift on GCP user-provisioned infrastructure](./third-party-solutions/openshift), [Wordpress deployment on Cloud Run](./third-party-solutions/wordpress/cloudrun) diff --git a/blueprints/cloud-operations/adfs/main.tf b/blueprints/cloud-operations/adfs/main.tf index ed904417..8948e49b 100644 --- a/blueprints/cloud-operations/adfs/main.tf +++ b/blueprints/cloud-operations/adfs/main.tf @@ -87,7 +87,7 @@ module "server" { } group = { named_ports = { - http = 443 + https = 443 } } tags = ["https-server"] @@ -97,69 +97,25 @@ module "glb" { source = "../../../modules/net-glb" name = "${var.prefix}-glb" project_id = module.project.project_id - - https = true - reserve_ip_address = true - - ssl_certificates_config = { - adfs-domain = { - domains = [ - "${var.adfs_dns_domain_name}" - ], - unmanaged_config = null + protocol = "HTTPS" + backend_service_configs = { + default = { + backends = [{ backend = module.server.group.id }] + log_sample_rate = 1 + protocol = "HTTPS" } } - - target_proxy_https_config = { - ssl_certificates = [ - "adfs-domain" - ] - } - - backend_services_config = { - adfs-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = module.server.group.id - options = null - } - ], - health_checks = ["hc"] - log_config = { - enable = true - sample_rate = 1 - } - options = { - affinity_cookie_ttl_sec = null - custom_request_headers = null - custom_response_headers = null - connection_draining_timeout_sec = null - load_balancing_scheme = null - locality_lb_policy = null - port_name = null - security_policy = null - session_affinity = null - timeout_sec = null - circuits_breakers = null - consistent_hash = null - iap = null - protocol = "HTTPS" - } + health_check_configs = { + default = { + https = { + port_specification = "USE_SERVING_PORT" } } } - health_checks_config = { - hc = { - type = "tcp" - logging = true - options = null - check = { - port_name = "http" - port_specification = "USE_NAMED_PORT" + ssl_certificates = { + managed_configs = { + adfs-domain = { + domains = ["${var.adfs_dns_domain_name}"] } } } diff --git a/blueprints/cloud-operations/adfs/outputs.tf b/blueprints/cloud-operations/adfs/outputs.tf index b9012312..371dd477 100644 --- a/blueprints/cloud-operations/adfs/outputs.tf +++ b/blueprints/cloud-operations/adfs/outputs.tf @@ -14,5 +14,5 @@ output "ip_address" { description = "IP address." - value = module.glb.ip_address -} \ No newline at end of file + value = module.glb.address +} diff --git a/blueprints/cloud-operations/apigee/main.tf b/blueprints/cloud-operations/apigee/main.tf index d6c8e7d1..9781a4de 100644 --- a/blueprints/cloud-operations/apigee/main.tf +++ b/blueprints/cloud-operations/apigee/main.tf @@ -39,11 +39,21 @@ module "project" { "storage.googleapis.com" ] iam = { - "roles/bigquery.jobUser" = [module.function_gcs2bq.service_account_iam_email] - "roles/logging.logWriter" = [module.function_export.service_account_iam_email] - "roles/logging.logWriter" = [module.function_gcs2bq.service_account_iam_email] - "roles/apigee.admin" = [module.function_export.service_account_iam_email] - "roles/storage.admin" = ["serviceAccount:${module.project.service_accounts.robots.apigee}"] + "roles/bigquery.jobUser" = [ + module.function_gcs2bq.service_account_iam_email + ] + "roles/logging.logWriter" = [ + module.function_export.service_account_iam_email + ] + "roles/logging.logWriter" = [ + module.function_gcs2bq.service_account_iam_email + ] + "roles/apigee.admin" = [ + module.function_export.service_account_iam_email + ] + "roles/storage.admin" = [ + "serviceAccount:${module.project.service_accounts.robots.apigee}" + ] } } @@ -52,16 +62,14 @@ module "vpc" { project_id = module.project.project_id name = var.organization.authorized_network vpc_create = var.vpc_create - subnets_psc = [for k, v in var.psc_config : - { - ip_cidr_range = v - name = "subnet-psc-${k}" - region = k - } - ] + subnets_psc = [for k, v in var.psc_config : { + ip_cidr_range = v + name = "subnet-psc-${k}" + region = k + }] psa_config = { - ranges = { for k, v in var.instances : - "apigee-${k}" => v.psa_ip_cidr_range + ranges = { + for k, v in var.instances : "apigee-${k}" => v.psa_ip_cidr_range } } } @@ -78,76 +86,39 @@ module "apigee" { ] } -resource "google_compute_region_network_endpoint_group" "neg" { - for_each = var.instances - name = "apigee-neg-${each.key}" - project = module.project.project_id - region = each.value.region - network_endpoint_type = "PRIVATE_SERVICE_CONNECT" - psc_target_service = module.apigee.instances[each.key].service_attachment - network = module.vpc.network.self_link - subnetwork = module.vpc.subnets_psc["${each.value.region}/subnet-psc-${each.value.region}"].self_link -} - module "glb" { - source = "../../../modules/net-glb" - name = "glb" - project_id = module.project.project_id - - https = true - reserve_ip_address = true - - ssl_certificates_config = { for k, v in var.envgroups : - "${k}-domain" => { - domains = v, - unmanaged_config = null + source = "../../../modules/net-glb" + name = "glb" + project_id = module.project.project_id + protocol = "HTTPS" + use_classic_version = false + backend_service_configs = { + default = { + backends = [for k, v in var.instances : { backend = k }] + protocol = "HTTPS" } } - - target_proxy_https_config = { - ssl_certificates = [for k, v in var.envgroups : "${k}-domain"] + health_check_configs = { + default = { + https = { port_specification = "USE_SERVING_PORT" } + } } - - health_checks_config_defaults = null - - backend_services_config = { - apigee = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [for k, v in google_compute_region_network_endpoint_group.neg : - { - group = v.id - options = null - } - ], - health_checks = [] - log_config = null - options = { - affinity_cookie_ttl_sec = null - custom_request_headers = null - custom_response_headers = null - connection_draining_timeout_sec = null - load_balancing_scheme = "EXTERNAL_MANAGED" - locality_lb_policy = null - port_name = null - security_policy = null - session_affinity = null - timeout_sec = null - circuits_breakers = null - consistent_hash = null - iap = null - protocol = "HTTPS" - } + neg_configs = { + for k, v in var.instances : k => { + psc = { + region = v.region + target_service = module.apigee.instances[k].service_attachment + network = module.vpc.network.self_link + subnetwork = ( + module.vpc.subnets_psc["${v.region}/subnet-psc-${v.region}"].self_link + ) } } } - global_forwarding_rule_config = { - load_balancing_scheme = "EXTERNAL_MANAGED" - ip_protocol = "TCP" - ip_version = "IPV4" - port_range = null + ssl_certificates = { + managed_config = { + for k, v in var.envgroups : k => { domains = [v] } + } } } @@ -162,7 +133,9 @@ module "bucket_export" { project_id = module.project.project_id name = "${module.project.project_id}-export" iam = { - "roles/storage.objectViewer" = [module.function_gcs2bq.service_account_iam_email] + "roles/storage.objectViewer" = [ + module.function_gcs2bq.service_account_iam_email + ] } notification_config = { enabled = true @@ -266,15 +239,24 @@ module "bigquery_dataset" { } } iam = { - "roles/bigquery.dataEditor" = [module.function_gcs2bq.service_account_iam_email] + "roles/bigquery.dataEditor" = [ + module.function_gcs2bq.service_account_iam_email + ] } } resource "google_app_engine_application" "app" { project = module.project.project_id - location_id = ((var.organization.analytics_region == "europe-west1" || var.organization.analytics_region == "us-central1") ? - substr(var.organization.analytics_region, 0, length(var.organization.analytics_region) - 1) : - var.organization.analytics_region) + location_id = ( + ( + var.organization.analytics_region == "europe-west1" || + var.organization.analytics_region == "us-central1" + ) + ? substr( + var.organization.analytics_region, 0, length(var.organization.analytics_region) - 1 + ) + : var.organization.analytics_region + ) } resource "google_cloud_scheduler_job" "job" { diff --git a/blueprints/cloud-operations/apigee/outputs.tf b/blueprints/cloud-operations/apigee/outputs.tf index 73789eb0..58df8ac0 100644 --- a/blueprints/cloud-operations/apigee/outputs.tf +++ b/blueprints/cloud-operations/apigee/outputs.tf @@ -16,5 +16,5 @@ output "ip_address" { description = "IP address." - value = module.glb.ip_address + value = module.glb.address } diff --git a/blueprints/networking/README.md b/blueprints/networking/README.md index c4a3d2f0..ef393268 100644 --- a/blueprints/networking/README.md +++ b/blueprints/networking/README.md @@ -46,11 +46,13 @@ The blueprint shows how to implement spoke transitivity via BGP advertisements,
+ ### DNS and Private Access for On-premises diff --git a/blueprints/networking/_deprecated/README.md b/blueprints/networking/_deprecated/README.md new file mode 100644 index 00000000..21d3e674 --- /dev/null +++ b/blueprints/networking/_deprecated/README.md @@ -0,0 +1,5 @@ +# Deprecated or unsupported blueprints + +The blueprints in this folder are either deprecated or need work on them. + +- nginx reverse proxy cluster needs tests and resolving a cycle diff --git a/blueprints/networking/nginx-reverse-proxy-cluster/Dockerfile b/blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/Dockerfile similarity index 100% rename from blueprints/networking/nginx-reverse-proxy-cluster/Dockerfile rename to blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/Dockerfile diff --git a/blueprints/networking/nginx-reverse-proxy-cluster/README.md b/blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/README.md similarity index 100% rename from blueprints/networking/nginx-reverse-proxy-cluster/README.md rename to blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/README.md diff --git a/blueprints/networking/nginx-reverse-proxy-cluster/main.tf b/blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/main.tf similarity index 73% rename from blueprints/networking/nginx-reverse-proxy-cluster/main.tf rename to blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/main.tf index 50f5374f..df0397bd 100644 --- a/blueprints/networking/nginx-reverse-proxy-cluster/main.tf +++ b/blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/main.tf @@ -71,7 +71,7 @@ locals { error_log stderr; access_log /dev/stdout combined; - set_real_ip_from ${module.xlb.ip_address}/32; + set_real_ip_from ${module.glb.address}/32; set_real_ip_from 35.191.0.0/16; set_real_ip_from 130.211.0.0/22; real_ip_header X-Forwarded-For; @@ -121,7 +121,8 @@ locals { module "project" { source = "../../../modules/project" - billing_account = (var.project_create != null + billing_account = ( + var.project_create != null ? var.project_create.billing_account_id : null ) @@ -130,7 +131,7 @@ module "project" { ? var.project_create.parent : null ) - + project_create = var.project_create != null services = [ "cloudresourcemanager.googleapis.com", "compute.googleapis.com", @@ -138,21 +139,17 @@ module "project" { "logging.googleapis.com", "monitoring.googleapis.com", ] - - project_create = var.project_create != null } module "vpc" { source = "../../../modules/net-vpc" project_id = module.project.project_id name = var.network - subnets = [ - { - name = var.subnetwork - ip_cidr_range = var.cidrs[var.subnetwork] - region = var.region - }, - ] + subnets = [{ + name = var.subnetwork + ip_cidr_range = var.cidrs[var.subnetwork] + region = var.region + }] vpc_create = var.network_create } @@ -181,23 +178,21 @@ module "firewall" { } module "nat" { - source = "../../../modules/net-cloudnat" - project_id = module.project.project_id - region = var.region - name = "${var.prefix}-nat" - router_network = module.vpc.name - config_source_subnets = "LIST_OF_SUBNETWORKS" - - logging_filter = "ALL" - + source = "../../../modules/net-cloudnat" + project_id = module.project.project_id + region = var.region + name = "${var.prefix}-nat" config_min_ports_per_vm = 4000 - subnetworks = [ - { - self_link = module.vpc.subnet_self_links[format("%s/%s", var.region, var.subnetwork)] - config_source_ranges = ["ALL_IP_RANGES"] - secondary_ranges = null - } - ] + config_source_subnets = "LIST_OF_SUBNETWORKS" + logging_filter = "ALL" + router_network = module.vpc.name + subnetworks = [{ + self_link = ( + module.vpc.subnet_self_links[format("%s/%s", var.region, var.subnetwork)] + ) + config_source_ranges = ["ALL_IP_RANGES"] + secondary_ranges = null + }] } ############################################################################### @@ -240,7 +235,6 @@ module "mig-proxy" { source = "../../../modules/compute-mig" project_id = module.project.project_id location = var.region - regional = true name = "${var.prefix}-proxy-cluster" named_ports = { http = "80" @@ -255,19 +249,14 @@ module "mig-proxy" { metric = var.autoscaling_metric } update_policy = { - instance_redistribution_type = "PROACTIVE" - max_surge_type = "fixed" - max_surge = 3 - max_unavailable_type = null - max_unavailable = null - minimal_action = "REPLACE" - min_ready_sec = 60 - type = "PROACTIVE" - } - default_version = { - instance_template = module.proxy-vm.template.self_link - name = "proxy-vm" + minimal_action = "REPLACE" + type = "PROACTIVE" + min_ready_sec = 30 + max_surge = { + fixed = 1 + } } + instance_template = module.proxy-vm.template.self_link health_check_config = { type = "http" check = { @@ -311,59 +300,28 @@ module "proxy-vm" { service_account_create = false } -module "xlb" { - source = "../../../modules/net-glb" - name = "${var.prefix}-reverse-proxy-xlb" - project_id = module.project.project_id - reserve_ip_address = true - health_checks_config = { - "${var.prefix}-reverse-proxy-hc" = { - type = "http" - logging = false - options = { - check_interval_sec = 10 - healthy_threshold = 1 - unhealthy_threshold = 1 - timeout_sec = 10 - } - check = { +module "glb" { + source = "../../../modules/net-glb" + project_id = module.project.project_id + name = "${var.prefix}-reverse-proxy-glb" + health_check_configs = { + default = { + check_interval_sec = 10 + healthy_threshold = 1 + unhealthy_threshold = 1 + timeout_sec = 10 + http = { port_specification = "USE_NAMED_PORT" port_name = "http" request_path = "/healthz" } } } - backend_services_config = { - "${var.prefix}-reverse-proxy-backend" = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = module.mig-proxy.group_manager.instance_group - options = null - } - ] - health_checks = ["${var.prefix}-reverse-proxy-hc"] - log_config = null - options = { - affinity_cookie_ttl_sec = null - custom_request_headers = null - custom_response_headers = null - connection_draining_timeout_sec = null - load_balancing_scheme = null - locality_lb_policy = null - port_name = !var.tls ? "http" : "https" - protocol = !var.tls ? "HTTP" : "HTTPS" - security_policy = null - session_affinity = null - timeout_sec = null - circuits_breakers = null - consistent_hash = null - iap = null - } - } + backend_service_configs = { + default = { + backends = [{ backend = module.mig-proxy.group_manager.instance_group }] + port_name = !var.tls ? "http" : "https" + protocol = !var.tls ? "HTTP" : "HTTPS" } } } diff --git a/blueprints/networking/nginx-reverse-proxy-cluster/outputs.tf b/blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/outputs.tf similarity index 85% rename from blueprints/networking/nginx-reverse-proxy-cluster/outputs.tf rename to blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/outputs.tf index ee529a3d..1e0fda00 100644 --- a/blueprints/networking/nginx-reverse-proxy-cluster/outputs.tf +++ b/blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/outputs.tf @@ -16,5 +16,5 @@ output "load_balancer_url" { description = "Load balancer for the reverse proxy instance group." - value = !var.tls ? format("http://%s/", module.xlb.ip_address) : format("https://%s/", module.xlb.ip_address) + value = format("http%s://%s/", var.tls ? "s" : "", module.glb.address) } diff --git a/blueprints/networking/nginx-reverse-proxy-cluster/reverse-proxy.png b/blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/reverse-proxy.png similarity index 100% rename from blueprints/networking/nginx-reverse-proxy-cluster/reverse-proxy.png rename to blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/reverse-proxy.png diff --git a/blueprints/networking/nginx-reverse-proxy-cluster/variables.tf b/blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/variables.tf similarity index 100% rename from blueprints/networking/nginx-reverse-proxy-cluster/variables.tf rename to blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/variables.tf diff --git a/blueprints/networking/nginx-reverse-proxy-cluster/versions.tf b/blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/versions.tf similarity index 100% rename from blueprints/networking/nginx-reverse-proxy-cluster/versions.tf rename to blueprints/networking/_deprecated/nginx-reverse-proxy-cluster/versions.tf diff --git a/blueprints/networking/glb-and-armor/main.tf b/blueprints/networking/glb-and-armor/main.tf index 26f90ac1..9f33572c 100644 --- a/blueprints/networking/glb-and-armor/main.tf +++ b/blueprints/networking/glb-and-armor/main.tf @@ -204,55 +204,14 @@ module "glb" { source = "../../../modules/net-glb" name = "${var.prefix}-http-lb" project_id = module.project.project_id - backend_services_config = { - http-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = module.mig_ew1.group_manager.instance_group - options = null - }, - { - group = module.mig_ue1.group_manager.instance_group - options = null - } - ], - health_checks = ["hc"] - log_config = { - enable = true - sample_rate = 1 - } - options = { - affinity_cookie_ttl_sec = null - custom_request_headers = null - custom_response_headers = null - connection_draining_timeout_sec = null - load_balancing_scheme = null - locality_lb_policy = null - port_name = "http" - security_policy = try(google_compute_security_policy.policy[0].name, null) - session_affinity = null - timeout_sec = null - circuits_breakers = null - consistent_hash = null - iap = null - protocol = "HTTP" - } - } - } - } - health_checks_config = { - hc = { - type = "http" - logging = true - options = null - check = { - port_name = "http" - port_specification = "USE_NAMED_PORT" - } + backend_service_configs = { + default = { + backends = [ + { backend = module.mig_ew1.group_manager.instance_group }, + { backend = module.mig_ue1.group_manager.instance_group } + ] + log_sample_rate = 1 + security_policy = try(google_compute_security_policy.policy[0].name, null) } } } diff --git a/blueprints/networking/glb-and-armor/outputs.tf b/blueprints/networking/glb-and-armor/outputs.tf index 72b4d9a4..61bb7484 100644 --- a/blueprints/networking/glb-and-armor/outputs.tf +++ b/blueprints/networking/glb-and-armor/outputs.tf @@ -17,7 +17,7 @@ output "glb_ip_address" { description = "Load balancer IP address." - value = module.glb.global_forwarding_rule.ip_address + value = module.glb.address } output "vm_siege_external_ip" { diff --git a/blueprints/serverless/api-gateway/main.tf b/blueprints/serverless/api-gateway/main.tf index 4fe132ed..fc1b4aa1 100644 --- a/blueprints/serverless/api-gateway/main.tf +++ b/blueprints/serverless/api-gateway/main.tf @@ -103,26 +103,16 @@ module "gateways" { module "glb" { source = "../../../modules/net-glb" - name = "glb" project_id = module.project.project_id - # This is important as serverless backends require no HCs - health_checks_config_defaults = null - reserve_ip_address = true - backend_services_config = { - serverless-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [for region in var.regions : { - group = google_compute_region_network_endpoint_group.serverless-negs[region].id - options = null - } - ], - health_checks = [] - log_config = null - options = null - } + name = "glb" + backend_service_configs = { + default = { + backends = [ + for region in var.regions : { + backend = google_compute_region_network_endpoint_group.serverless-negs[region].id + } + ] + health_checks = [] } } } diff --git a/blueprints/serverless/api-gateway/outputs.tf b/blueprints/serverless/api-gateway/outputs.tf index 9cc48f5b..0eec77d8 100644 --- a/blueprints/serverless/api-gateway/outputs.tf +++ b/blueprints/serverless/api-gateway/outputs.tf @@ -16,5 +16,5 @@ output "ip_address" { description = "The reserved global IP address." - value = module.glb.ip_address + value = module.glb.address } diff --git a/modules/net-glb/.gitignore b/modules/net-glb/.gitignore new file mode 100644 index 00000000..a00202d5 --- /dev/null +++ b/modules/net-glb/.gitignore @@ -0,0 +1,3 @@ +*backup +*tfstate +tfvars diff --git a/modules/net-glb/README.md b/modules/net-glb/README.md index 4044fcaa..4fc97b07 100644 --- a/modules/net-glb/README.md +++ b/modules/net-glb/README.md @@ -1,287 +1,365 @@ -# External Global (HTTP/S) Load Balancer Module +# Global HTTP/S Classic Load Balancer Module -The module allows managing External Global HTTP/HTTPS Load Balancers (XGLB), integrating the global forwarding rule, the url-map, the backends (supporting buckets and groups), and optional health checks and SSL certificates (both managed and unmanaged). It's designed to be a simple match for the [`gcs`](../gcs) and the [`compute-vm`](../compute-vm) modules, which can be used to manage GCS buckets, instance templates and instance groups. +This module allows managing Global HTTP/HTTPS Classic Load Balancers (GLBs). It's designed to expose the full configuration of the underlying resources, and to facilitate common usage patterns by providing sensible defaults, and optionally managing prerequisite resources like health checks, instance groups, etc. + +Due to the complexity of the underlying resources, changes to the configuration that involve recreation of resources are best applied in stages, starting by disabling the configuration in the urlmap that references the resources that neeed recreation, then doing the same for the backend service, etc. ## Examples -### GCS Bucket Minimal Example +- [Minimal HTTP Example](#minimal-http-example) +- [Minimal HTTPS Examples](#minimal-https-examples) +- [Health Checks](#health-checks) +- [Backend Types and Management](#backend-types-and-management) + - [Instance Groups](#instance-groups) + - [Storage Buckets](#storage-buckets) + - [Network Endpoint Groups](#network-endpoint-groups-negs) + - [Zonal NEGs](#zonal-neg-creation) + - [Hybrid NEGs](#hybrid-neg-creation) + - [Internet NEGs](#internet-neg-creation) + - [Serverless NEGs](#serverless-neg-creation) +- [URL Map](#url-map) +- [SSL Certificates](#ssl-certificates) +- [Complex Example](#complex-example) -This is a minimal example, which creates a global HTTP load balancer, pointing the path `/` to an existing GCS bucket called `my_test_bucket`. +### Minimal HTTP Example + +An HTTP load balancer with a backend service pointing to a GCE instance group: ```hcl -module "glb" { +module "glb-0" { source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - - backend_services_config = { - my-bucket-backend = { - bucket_config = { - bucket_name = "my_test_bucket" - options = null - } - group_config = null - enable_cdn = false - cdn_config = null - } - } -} -# tftest modules=1 resources=4 -``` - -### Group Backend Service Minimal Example - -A very similar coniguration also applies to GCE instance groups: - -```hcl -module "glb" { - source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - - backend_services_config = { - my-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_test_group" - options = null - } - ], - health_checks = [] - log_config = null - options = null - } + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { backend = "projects/myprj/zones/europe-west8-b/instanceGroups/myig-b" }, + { backend = "projects/myprj/zones/europe-west8-c/instanceGroups/myig-c" }, + ] } } } # tftest modules=1 resources=5 ``` -### Health Checks For Group Backend Services +### Minimal HTTPS examples -Group backend services support health checks. -If no health checks are specified, a default HTTP health check is created and associated to each group backend service. The default health check configuration can be modified through the `health_checks_config_defaults` variable. +#### HTTP backends -Alternatively, one or more health checks can be either contextually created or attached, if existing. If the id of the health checks specified is equal to one of the keys of the `health_checks_config` variable, the health check is contextually created; otherwise, the health check id is used as is, assuming an health check alredy exists. - -For example, to contextually create a health check and attach it to the backend service: +An HTTPS load balancer needs a certificate and backends can be HTTP or HTTPS. THis is an example With HTTP backends and a managed certificate: ```hcl -module "glb" { +module "glb-0" { source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - - backend_services_config = { - my-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_test_group" - options = null - } - ], - health_checks = ["hc_1"] - log_config = null - options = null + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { backend = "projects/myprj/zones/europe-west8-b/instanceGroups/myig-b" }, + { backend = "projects/myprj/zones/europe-west8-c/instanceGroups/myig-c" }, + ] + protocol = "HTTP" + } + } + protocol = "HTTPS" + ssl_certificates = { + managed_configs = { + default = { + domains = ["glb-test-0.example.org"] } } } +} +# tftest modules=1 resources=6 +``` - health_checks_config = { - hc_1 = { - type = "http" - logging = true - options = { - timeout_sec = 40 - } - check = { +#### HTTPS backends + +For HTTPS backends the backend service protocol needs to be set to `HTTPS`. The port name if omitted is inferred from the protocol, in this case it is set internally to `https`. The health check also needs to be set to https. This is a complete example: + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { backend = "projects/myprj/zones/europe-west8-b/instanceGroups/myig-b" }, + { backend = "projects/myprj/zones/europe-west8-c/instanceGroups/myig-c" }, + ] + protocol = "HTTPS" + } + } + health_check_configs = { + default = { + https = { port_specification = "USE_SERVING_PORT" } } } -} -# tftest modules=1 resources=5 -``` - -### Serverless Backends - -Serverless backends can also be used, as shown in the example below. - -```hcl -module "glb" { - source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - - # This is important as serverless backends require no HCs - health_checks_config_defaults = null - - backend_services_config = { - my-serverless-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = google_compute_region_network_endpoint_group.serverless-neg.id - options = null - } - ], - health_checks = [] - log_config = null - options = null + protocol = "HTTPS" + ssl_certificates = { + managed_configs = { + default = { + domains = ["glb-test-0.example.org"] } } } } +# tftest modules=1 resources=6 +``` -resource "google_compute_region_network_endpoint_group" "serverless-neg" { - name = "my-serverless-neg" - project = var.project_id - region = "europe-west1" - network_endpoint_type = "SERVERLESS" +### Classic vs Non-classic - cloud_run { - service = "my-cloud-run-service" +The module uses a classic Global Load Balancer by default. To use the non-classic version set the `use_classic_version` variable to `false` as in the following example, note that the module is not enforcing feature sets between the two versions: + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + use_classic_version = false + backend_service_configs = { + default = { + backends = [ + { backend = "projects/myprj/zones/europe-west8-b/instanceGroups/myig-b" }, + { backend = "projects/myprj/zones/europe-west8-c/instanceGroups/myig-c" }, + ] + } } } # tftest modules=1 resources=5 ``` -### Mixing Backends +### Health Checks -Backends can be multiple, group and bucket backends can be mixed and group backends support multiple groups. +You can leverage externally defined health checks for backend services, or have the module create them for you. + +By default a simple HTTP health check named `default` is created and used in backend services. If you need to override the default, simply define your own health check using the same key (`default`). For more complex configurations you can define your own health checks and reference them via keys in the backend service configurations. + +Health checks created by this module are controlled via the `health_check_configs` variable, which behaves in a similar way to other LB modules in this repository. This is an example that overrides the default health check configuration using a TCP health check: ```hcl -module "glb" { +module "glb-0" { source = "./fabric/modules/net-glb" - name = "glb-test" project_id = var.project_id + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [{ + backend = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + # no need to reference the hc explicitly when using the `default` key + # health_checks = ["default"] + } + } + health_check_configs = { + default = { + tcp = { port = 80 } + } + } +} +# tftest modules=1 resources=5 +``` - backend_services_config = { - my-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_test_group" - options = null - } - ], - health_checks = [] - log_config = null - options = null +To leverage existing health checks without having the module create them, simply pass their self links to backend services and set the `health_check_configs` variable to an empty map: + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = var.project_id + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [{ + backend = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig" + }] + health_checks = ["projects/myprj/global/healthChecks/custom"] + } + } + health_check_configs = {} +} +# tftest modules=1 resources=4 +``` + +### Backend Types and Management + +#### Instance Groups + +The module can optionally create unmanaged instance groups, which can then be referred to in backends via their key. THis is the simple HTTP example above but with instance group creation managed by the module: + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { backend = "default-b" } + ] + } + } + group_configs = { + default-b = { + zone = "europe-west8-b" + instances = [ + "projects/myprj/zones/europe-west8-b/instances/vm-a" + ] + named_ports = { http = 80 } + } + } +} +# tftest modules=1 resources=6 +``` + +#### Storage Buckets + +GCS bucket backends can also be managed and used in this module in a similar way to regular backend services.Multiple GCS bucket backends can be defined and referenced in URL maps by their keys (or self links if defined externally) together with regular backend services, [an example is provided later in this document](#complex-example). This is a simple example that defines a GCS backend as the default for the URL map: + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_buckets_config = { + default = { + bucket_name = "tf-playground-svpc-gce-public" + } + } + # with a single GCS backend the implied default health check is not needed + health_check_configs = {} +} +# tftest modules=1 resources=4 +``` + +#### Network Endpoint Groups (NEGs) + +Supported Network Endpoint Groups (NEGs) can also be used as backends. Similarly to groups, you can pass a self link for existing NEGs or have the module manage them for you. A simple example using an existing zonal NEG: + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { + backend = "projects/myprj/zones/europe-west8-b/networkEndpointGroups/myneg-b" + balancing_mode = "RATE" + max_rate = { per_endpoint = 10 } + } + ] + } + } +} +# tftest modules=1 resources=5 +``` + +#### Zonal NEG creation + +This example shows how to create and manage zonal NEGs using GCE VMs as endpoints: + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { + backend = "neg-0" + balancing_mode = "RATE" + max_rate = { per_endpoint = 10 } + } + ] + } + } + neg_configs = { + neg-0 = { + gce = { + network = "projects/myprj-host/global/networks/svpc" + subnetwork = "projects/myprj-host/regions/europe-west8/subnetworks/gce" + zone = "europe-west8-b" + endpoints = [{ + instance = "myinstance-b-0" + ip_address = "10.24.32.25" + port = 80 + }] } - }, - another-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_other_test_group" - options = null - } - ], - health_checks = [] - log_config = null - options = null - } - }, - a-bucket-backend = { - bucket_config = { - bucket_name = "my_test_bucket" - options = null - } - group_config = null - enable_cdn = false - cdn_config = null } } } # tftest modules=1 resources=7 ``` -### Url-map +#### Hybrid NEG creation -The url-map can be customized with lots of different configurations. This includes leveraging multiple backends in different parts of the configuration. -Given its complexity, it's left to the user passing the right data structure. - -For simplicity, *if no configurations are given* the first backend service defined (in alphabetical order, with priority to bucket backend services, if any) is used as the *default_service*, thus answering to the root (*/*) path. - -Backend services can be specified as needed in the url-map configuration, referencing the id used to declare them in the backend services map. If a corresponding backend service is found, their object id is automatically used; otherwise, it is assumed that the string passed is the id of an already existing backend and it is given to the provider as it was passed. - -In this example, we're using one backend service as the default backend +This example shows how to create and manage hybrid NEGs: ```hcl -module "glb" { +module "glb-0" { source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - - url_map_config = { - default_service = "my-group-backend" - default_route_action = null - default_url_redirect = null - tests = null - header_action = null - host_rules = [] - path_matchers = [ - { - name = "my-example-page" - path_rules = [ - { - paths = ["/my-example-page"] - service = "another-group-backend" - } - ] - } - ] + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { + backend = "neg-0" + balancing_mode = "RATE" + max_rate = { per_endpoint = 10 } + } + ] + } } - - backend_services_config = { - my-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_test_group" - options = null - } - ], - health_checks = [] - log_config = null - options = null + neg_configs = { + neg-0 = { + hybrid = { + network = "projects/myprj-host/global/networks/svpc" + zone = "europe-west8-b" + endpoints = [{ + ip_address = "10.0.0.10" + port = 80 + }] } - }, - my-example-page = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_other_test_group" - options = null - } - ], - health_checks = [] - log_config = null - options = null + } + } +} +# tftest modules=1 resources=7 +``` + +#### Internet NEG creation + +This example shows how to create and manage internet NEGs: + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { backend = "neg-0" } + ] + health_checks = [] + } + } + # with a single internet NEG the implied default health check is not needed + health_check_configs = {} + neg_configs = { + neg-0 = { + internet = { + use_fqdn = true + endpoints = [{ + destination = "www.example.org" + port = 80 + }] } } } @@ -289,224 +367,31 @@ module "glb" { # tftest modules=1 resources=6 ``` -### Reserve a static IP address +#### Private Service Connect NEG creation -Optionally, a static IP address can be reserved: +The module supports managing PSC NEGs if the non-classic version of the load balancer is used: ```hcl -module "glb" { +module "glb-0" { source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - - reserve_ip_address = true - - backend_services_config = { - my-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_test_group" - options = null - } - ], - health_checks = [] - log_config = null - options = null - } + project_id = "myprj" + name = "glb-test-0" + use_classic_version = false + backend_service_configs = { + default = { + backends = [ + { backend = "neg-0" } + ] + health_checks = [] } } -} -# tftest modules=1 resources=6 -``` - -### HTTPS And SSL Certificates - -HTTPS is disabled by default but it can be optionally enabled. -The module supports both managed and unmanaged certificates, and they can be either created contextually with other resources or attached, if already existing. - -If no `ssl_certificates_config` variable is specified, a managed certificate for the domain *example.com* is automatically created. - -```hcl -module "glb" { - source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - - https = true - - backend_services_config = { - my-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_test_group" - options = null - } - ], - health_checks = [] - log_config = null - options = null - } - } - } -} -# tftest modules=1 resources=6 -``` - -Otherwise, SSL certificates can be explicitely defined. In this case, they'll need to be referenced from the `target_proxy_https_config.ssl_certificates` variable. - -If the ids specified in the `target_proxy_https_config` variable are not found in the `ssl_certificates_config` map, they are used as is, assuming the ssl certificates already exist. - -```hcl -module "glb" { - source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - - https = true - - ssl_certificates_config = { - my-domain = { - domains = [ - "my-domain.com" - ], - unmanaged_config = null - } - } - - target_proxy_https_config = { - ssl_certificates = [ - "my-domain", - "an-existing-cert" - ] - } - - backend_services_config = { - my-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_test_group" - options = null - } - ] - health_checks = [] - log_config = null - options = null - } - } - } -} -# tftest modules=1 resources=6 -``` - -Using unamanged certificates is also possible. Here is an example: - -```hcl -module "glb" { - source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - - https = true - - ssl_certificates_config = { - my-domain = { - domains = [ - "my-domain.com" - ], - unmanaged_config = { - tls_private_key = nonsensitive(tls_private_key.self_signed_key.private_key_pem) - tls_self_signed_cert = nonsensitive(tls_self_signed_cert.self_signed_cert.cert_pem) - } - } - } - - target_proxy_https_config = { - ssl_certificates = [ - "my-domain" - ] - } - - backend_services_config = { - my-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_test_group" - options = null - } - ], - health_checks = [] - log_config = null - options = null - } - } - } -} - -resource "tls_private_key" "self_signed_key" { - algorithm = "RSA" - rsa_bits = 2048 -} - -resource "tls_self_signed_cert" "self_signed_cert" { - private_key_pem = tls_private_key.self_signed_key.private_key_pem - validity_period_hours = 12 - early_renewal_hours = 3 - dns_names = ["example.com"] - allowed_uses = [ - "key_encipherment", - "digital_signature", - "server_auth" - ] - subject { - common_name = "example.com" - organization = "My Test Org" - } -} -# tftest modules=1 resources=8 -``` - -## Regional Load Balancing - -You can also use regional load balancing by specifying a `region` parameter: - -```hcl -module "glb" { - source = "./fabric/modules/net-glb" - name = "glb-test" - project_id = var.project_id - region = var.region - - backend_services_config = { - my-group-backend = { - bucket_config = null - enable_cdn = false - cdn_config = null - group_config = { - backends = [ - { - group = "my_test_group" - options = null - } - ], - health_checks = [] - log_config = null - options = null + # with a single PSC NEG the implied default health check is not needed + health_check_configs = {} + neg_configs = { + neg-0 = { + psc = { + region = "europe-west8" + target_service = "europe-west8-cloudkms.googleapis.com" } } } @@ -514,16 +399,263 @@ module "glb" { # tftest modules=1 resources=5 ``` +#### Serverless NEG creation -## Components And Files Mapping +The module supports managing Serverless NEGs for Cloud Run and Cloud Function. This is an example of a Cloud Run NEG: -An External Global Load Balancer is made of multiple components, that change depending on the configurations. Sometimes, it may be tricky to understand what they are, and how they relate to each other. Following, we provide a very brief overview to become more familiar with them. +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { backend = "neg-0" } + ] + health_checks = [] + } + } + # with a single serverless NEG the implied default health check is not needed + health_check_configs = {} + neg_configs = { + neg-0 = { + cloudrun = { + region = "europe-west8" + target_service = { + name = "hello" + } + } + } + } +} +# tftest modules=1 resources=5 +``` -- The global load balancer [forwarding rule](global-forwarding-rule.tf) binds a frontend public Virtual IP (VIP) to an HTTP(S) [target proxy](target-proxy.tf). -- If the target proxy is HTTPS, it requires one or more managed or unmanaged [SSL certificates](ssl-certificates.tf). -Target proxies leverage [url-maps](url-map.tf): set of L7 rules, which create a mapping between specific hostnames, URIs (and more) to one or more [backends services](backend-services.tf). -- [Backend services](backend-services.tf) can either link to a bucket or one or multiple groups, which can be GCE instance groups or NEGs. It is assumed in this module that buckets and groups are previously created through other modules, and passed in as input variables. -- Backend services support one or more [health checks](health-checks.tf), used to verify that the backend is indeed healthy, so that traffic can be forwarded to it. Health checks currently supported in this module are HTTP, HTTPS, HTTP2, SSL, TCP. +### URL Map + +The module exposes the full URL map resource configuration, with some minor changes to the interface to decrease verbosity, and support for aliasing backend services via keys. + +The default URL map configuration sets the `default` backend service as the default service for the load balancer as a convenience. Just override the `urlmap_config` variable to change the default behaviour: + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [{ + backend = "projects/myprj/zones/europe-west8-b/instanceGroups/ig-0" + }] + } + other = { + backends = [{ + backend = "projects/myprj/zones/europe-west8-c/instanceGroups/ig-1" + }] + } + } + urlmap_config = { + default_service = "default" + host_rules = [{ + hosts = ["*"] + path_matcher = "pathmap" + }] + path_matchers = { + pathmap = { + default_service = "default" + path_rules = [{ + paths = ["/other", "/other/*"] + service = "other" + }] + } + } + } +} + +# tftest modules=1 resources=6 +``` + +### SSL Certificates + +The module also allows managing managed and self-managed SSL certificates via the `ssl_certificates` variable. Any certificate defined there will be added to the HTTPS proxy resource. + +THe [HTTPS example above](#minimal-https-examples) shows how to configure manage certificated, the following example shows how to use an unmanaged (or self managed) certificate. The example uses Terraform resource for the key and certificate so that the we don't depend on external files when running tests, in real use the key and certificate are generally provided via external files read by the Terraform `file()` function. + +```hcl + +resource "tls_private_key" "default" { + algorithm = "RSA" + rsa_bits = 4096 +} + +resource "tls_self_signed_cert" "default" { + private_key_pem = tls_private_key.default.private_key_pem + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + validity_period_hours = 720 + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_service_configs = { + default = { + backends = [ + { backend = "projects/myprj/zones/europe-west8-b/instanceGroups/myig-b" }, + { backend = "projects/myprj/zones/europe-west8-c/instanceGroups/myig-c" }, + ] + protocol = "HTTP" + } + } + protocol = "HTTPS" + ssl_certificates = { + create_configs = { + default = { + # certificate and key could also be read via file() from external files + certificate = tls_self_signed_cert.default.cert_pem + private_key = tls_private_key.default.private_key_pem + } + } + } +} +# tftest modules=1 resources=8 +``` + +### Complex example + +This example mixes group and NEG backends, and shows how to set HTTPS for specific backends. + +```hcl +module "glb-0" { + source = "./fabric/modules/net-glb" + project_id = "myprj" + name = "glb-test-0" + backend_buckets_config = { + gcs-0 = { + bucket_name = "my-bucket" + } + } + backend_service_configs = { + default = { + backends = [ + { backend = "ew8-b" }, + { backend = "ew8-c" }, + ] + } + neg-gce-0 = { + backends = [{ + balancing_mode = "RATE" + backend = "neg-ew8-c" + max_rate = { per_endpoint = 10 } + }] + } + neg-hybrid-0 = { + backends = [{ + backend = "neg-hello" + }] + health_checks = ["neg"] + protocol = "HTTPS" + } + } + group_configs = { + ew8-b = { + zone = "europe-west8-b" + instances = [ + "projects/prj-gce/zones/europe-west8-b/instances/nginx-ew8-b" + ] + named_ports = { http = 80 } + } + ew8-c = { + zone = "europe-west8-c" + instances = [ + "projects/prj-gce/zones/europe-west8-c/instances/nginx-ew8-c" + ] + named_ports = { http = 80 } + } + } + health_check_configs = { + default = { + http = { + port = 80 + } + } + neg = { + https = { + host = "hello.example.com" + port = 443 + } + } + } + neg_configs = { + neg-ew8-c = { + gce = { + network = "projects/myprj-host/global/networks/svpc" + subnetwork = "projects/myprj-host/regions/europe-west8/subnetworks/gce" + zone = "europe-west8-c" + endpoints = [{ + instance = "nginx-ew8-c" + ip_address = "10.24.32.26" + port = 80 + }] + } + } + neg-hello = { + hybrid = { + network = "projects/myprj-host/global/networks/svpc" + zone = "europe-west8-b" + endpoints = [{ + ip_address = "192.168.0.3" + port = 443 + }] + } + } + } + urlmap_config = { + default_service = "default" + host_rules = [ + { + hosts = ["*"] + path_matcher = "gce" + }, + { + hosts = ["hello.example.com"] + path_matcher = "hello" + }, + { + hosts = ["static.example.com"] + path_matcher = "static" + } + ] + path_matchers = { + gce = { + default_service = "default" + path_rules = [ + { + paths = ["/gce-neg", "/gce-neg/*"] + service = "neg-gce-0" + } + ] + } + hello = { + default_service = "neg-hybrid-0" + } + static = { + default_service = "gcs-0" + } + } + } +} +# tftest modules=1 resources=15 +``` @@ -532,16 +664,16 @@ Target proxies leverage [url-maps](url-map.tf): set of L7 rules, which create a | name | description | resources | |---|---|---| -| [backend-services.tf](./backend-services.tf) | Bucket and group backend services. | google_compute_backend_bucket · google_compute_backend_service | -| [global-forwarding-rule.tf](./global-forwarding-rule.tf) | Global address and forwarding rule. | google_compute_global_address · google_compute_global_forwarding_rule | -| [health-checks.tf](./health-checks.tf) | Health checks. | google_compute_health_check · google_compute_region_health_check | +| [backend-service.tf](./backend-service.tf) | Backend service resources. | google_compute_backend_service | +| [backends.tf](./backends.tf) | Backend groups and backend buckets resources. | google_compute_backend_bucket · google_compute_instance_group | +| [health-check.tf](./health-check.tf) | Health check resource. | google_compute_health_check | +| [main.tf](./main.tf) | Module-level locals and resources. | google_compute_global_forwarding_rule · google_compute_managed_ssl_certificate · google_compute_ssl_certificate · google_compute_target_http_proxy · google_compute_target_https_proxy | +| [negs.tf](./negs.tf) | NEG resources. | google_compute_global_network_endpoint · google_compute_global_network_endpoint_group · google_compute_network_endpoint · google_compute_network_endpoint_group · google_compute_region_network_endpoint_group | | [outputs.tf](./outputs.tf) | Module outputs. | | -| [regional-backend-services.tf](./regional-backend-services.tf) | Bucket and group backend services for regional load balancers. | google_compute_region_backend_service | -| [regional-forwarding-rule.tf](./regional-forwarding-rule.tf) | Global address and forwarding rule. | google_compute_address · google_compute_forwarding_rule | -| [regional-url-map.tf](./regional-url-map.tf) | regional URL maps. | google_compute_region_url_map | -| [ssl-certificates.tf](./ssl-certificates.tf) | SSL certificates. | google_compute_managed_ssl_certificate · google_compute_ssl_certificate | -| [target-proxy.tf](./target-proxy.tf) | HTTP and HTTPS target proxies. | google_compute_region_target_http_proxy · google_compute_region_target_https_proxy · google_compute_target_http_proxy · google_compute_target_https_proxy | -| [url-map.tf](./url-map.tf) | URL maps. | google_compute_url_map | +| [urlmap.tf](./urlmap.tf) | URL map resources. | google_compute_url_map | +| [variables-backend-service.tf](./variables-backend-service.tf) | Backend services variables. | | +| [variables-health-check.tf](./variables-health-check.tf) | Health check variable. | | +| [variables-urlmap.tf](./variables-urlmap.tf) | URLmap variable. | | | [variables.tf](./variables.tf) | Module variables. | | | [versions.tf](./versions.tf) | Version pins. | | @@ -549,33 +681,32 @@ Target proxies leverage [url-maps](url-map.tf): set of L7 rules, which create a | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L186) | Load balancer name. | string | ✓ | | -| [project_id](variables.tf#L191) | Project id. | string | ✓ | | -| [backend_services_config](variables.tf#L17) | The backends services configuration. | map(object({…})) | | {} | -| [forwarding_rule_config](variables.tf#L112) | Regional forwarding rule configurations. | object({…}) | | {…} | -| [global_forwarding_rule_config](variables.tf#L133) | Global forwarding rule configurations. | object({…}) | | {…} | -| [health_checks_config](variables.tf#L151) | Custom health checks configuration. | map(object({…})) | | {} | -| [health_checks_config_defaults](variables.tf#L162) | Auto-created health check default configuration. | object({…}) | | {…} | -| [https](variables.tf#L180) | Whether to enable HTTPS. | bool | | false | -| [region](variables.tf#L196) | Create a regional load balancer in this region. | string | | null | -| [reserve_ip_address](variables.tf#L202) | Whether to reserve a static global IP address. | bool | | false | -| [ssl_certificates_config](variables.tf#L208) | The SSL certificate configuration. | map(object({…})) | | {} | -| [ssl_certificates_config_defaults](variables.tf#L221) | The SSL certificate default configuration. | object({…}) | | {…} | -| [target_proxy_https_config](variables.tf#L237) | The HTTPS target proxy configuration. | object({…}) | | null | -| [url_map_config](variables.tf#L245) | The url-map configuration. | object({…}) | | null | +| [name](variables.tf#L91) | Load balancer name. | string | ✓ | | +| [project_id](variables.tf#L193) | Project id. | string | ✓ | | +| [address](variables.tf#L17) | Optional IP address used for the forwarding rule. | string | | null | +| [backend_buckets_config](variables.tf#L23) | Backend buckets configuration. | map(object({…})) | | {} | +| [backend_service_configs](variables-backend-service.tf#L19) | Backend service level configuration. | map(object({…})) | | {} | +| [description](variables.tf#L56) | Optional description used for resources. | string | | "Terraform managed." | +| [group_configs](variables.tf#L62) | 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({…})) | | {…} | +| [https_proxy_config](variables.tf#L74) | HTTPS proxy connfiguration. | object({…}) | | {} | +| [labels](variables.tf#L85) | Labels set on resources. | map(string) | | {} | +| [neg_configs](variables.tf#L96) | Optional network endpoint groups to create. Can be referenced in backends via key or outputs. | map(object({…})) | | {} | +| [ports](variables.tf#L187) | Optional ports for HTTP load balancer, valid ports are 80 and 8080. | list(string) | | null | +| [protocol](variables.tf#L198) | Protocol supported by this load balancer. | string | | "HTTP" | +| [ssl_certificates](variables.tf#L211) | SSL target proxy certificates (only if protocol is HTTPS) for existing, custom, and managed certificates. | object({…}) | | {} | +| [urlmap_config](variables-urlmap.tf#L19) | The URL map configuration. | object({…}) | | {…} | +| [use_classic_version](variables.tf#L228) | Use classic Global Load Balancer. | bool | | true | ## Outputs | name | description | sensitive | |---|---|:---:| -| [backend_services](outputs.tf#L17) | Backend service resources. | | -| [forwarding_rule](outputs.tf#L25) | The regional forwarding rule. | | -| [global_forwarding_rule](outputs.tf#L30) | The global forwarding rule. | | -| [health_checks](outputs.tf#L35) | Health-check resources. | | -| [ip_address](outputs.tf#L40) | The reserved global IP address. | | -| [ip_address_self_link](outputs.tf#L45) | The URI of the reserved global IP address. | | -| [ssl_certificates](outputs.tf#L50) | The SSL certificate. | | -| [target_proxy](outputs.tf#L59) | The target proxy. | | -| [url_map](outputs.tf#L67) | The url-map. | | +| [address](outputs.tf#L17) | Forwarding rule address. | | +| [backend_service_ids](outputs.tf#L22) | Backend service resources. | | +| [forwarding_rule](outputs.tf#L29) | Forwarding rule resource. | | +| [group_ids](outputs.tf#L34) | Autogenerated instance group ids. | | +| [health_check_ids](outputs.tf#L41) | Autogenerated health check ids. | | +| [neg_ids](outputs.tf#L48) | Autogenerated network endpoint group ids. | | diff --git a/modules/net-glb/backend-service.tf b/modules/net-glb/backend-service.tf new file mode 100644 index 00000000..f8956e13 --- /dev/null +++ b/modules/net-glb/backend-service.tf @@ -0,0 +1,262 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description Backend service resources. + +locals { + group_ids = merge( + { + for k, v in google_compute_instance_group.default : k => v.id + }, + { + for k, v in google_compute_global_network_endpoint_group.default : k => v.id + }, + { + for k, v in google_compute_network_endpoint_group.default : k => v.id + }, + { + for k, v in google_compute_region_network_endpoint_group.psc : k => v.id + }, + { + for k, v in google_compute_region_network_endpoint_group.serverless : k => v.id + } + ) + hc_ids = { + for k, v in google_compute_health_check.default : k => v.id + } +} + +# google_compute_backend_bucket + +resource "google_compute_backend_service" "default" { + provider = google-beta + for_each = var.backend_service_configs + project = ( + each.value.project_id == null + ? var.project_id + : each.value.project_id + ) + name = "${var.name}-${each.key}" + description = var.description + affinity_cookie_ttl_sec = each.value.affinity_cookie_ttl_sec + compression_mode = each.value.compression_mode + connection_draining_timeout_sec = each.value.connection_draining_timeout_sec + custom_request_headers = each.value.custom_request_headers + custom_response_headers = each.value.custom_response_headers + enable_cdn = each.value.enable_cdn + health_checks = length(each.value.health_checks) == 0 ? null : [ + for k in each.value.health_checks : lookup(local.hc_ids, k, k) + ] + load_balancing_scheme = "EXTERNAL" + port_name = ( + each.value.port_name == null + ? lower(each.value.protocol == null ? var.protocol : each.value.protocol) + : each.value.port_name + ) + protocol = ( + each.value.protocol == null ? var.protocol : each.value.protocol + ) + security_policy = each.value.security_policy + session_affinity = each.value.session_affinity + timeout_sec = each.value.timeout_sec + + dynamic "backend" { + for_each = { for b in coalesce(each.value.backends, []) : b.backend => b } + content { + group = lookup(local.group_ids, backend.key, backend.key) + balancing_mode = backend.value.balancing_mode # UTILIZATION, RATE + capacity_scaler = backend.value.capacity_scaler + description = backend.value.description + max_connections = try( + backend.value.max_connections.per_group, null + ) + max_connections_per_endpoint = try( + backend.value.max_connections.per_endpoint, null + ) + max_connections_per_instance = try( + backend.value.max_connections.per_instance, null + ) + max_rate = try( + backend.value.max_rate.per_group, null + ) + max_rate_per_endpoint = try( + backend.value.max_rate.per_endpoint, null + ) + max_rate_per_instance = try( + backend.value.max_rate.per_instance, null + ) + max_utilization = backend.value.max_utilization + } + } + + dynamic "cdn_policy" { + for_each = ( + each.value.cdn_policy == null ? [] : [each.value.cdn_policy] + ) + iterator = cdn + content { + cache_mode = cdn.value.cache_mode + client_ttl = cdn.value.client_ttl + default_ttl = cdn.value.default_ttl + max_ttl = cdn.value.max_ttl + negative_caching = cdn.value.negative_caching + serve_while_stale = cdn.value.serve_while_stale + signed_url_cache_max_age_sec = cdn.value.signed_url_cache_max_age_sec + dynamic "cache_key_policy" { + for_each = ( + cdn.value.cache_key_policy == null + ? [] + : [cdn.value.cache_key_policy] + ) + iterator = ck + content { + include_host = ck.value.include_host + include_named_cookies = ck.value.include_named_cookies + include_protocol = ck.value.include_protocol + include_query_string = ck.value.include_query_string + query_string_blacklist = ck.value.query_string_blacklist + query_string_whitelist = ck.value.query_string_whitelist + } + } + dynamic "negative_caching_policy" { + for_each = ( + cdn.value.negative_caching_policy == null + ? [] + : [cdn.value.negative_caching_policy] + ) + iterator = nc + content { + code = nc.value.code + ttl = nc.value.ttl + } + } + } + } + + dynamic "circuit_breakers" { + for_each = ( + each.value.circuit_breakers == null ? [] : [each.value.circuit_breakers] + ) + iterator = cb + content { + max_connections = cb.value.max_connections + max_pending_requests = cb.value.max_pending_requests + max_requests = cb.value.max_requests + max_requests_per_connection = cb.value.max_requests_per_connection + max_retries = cb.value.max_retries + dynamic "connect_timeout" { + for_each = ( + cb.value.connect_timeout == null ? [] : [cb.value.connect_timeout] + ) + content { + seconds = connect_timeout.value.seconds + nanos = connect_timeout.value.nanos + } + } + } + } + + dynamic "consistent_hash" { + for_each = ( + each.value.consistent_hash == null ? [] : [each.value.consistent_hash] + ) + iterator = ch + content { + http_header_name = ch.value.http_header_name + minimum_ring_size = ch.value.minimum_ring_size + dynamic "http_cookie" { + for_each = ch.value.http_cookie == null ? [] : [ch.value.http_cookie] + content { + name = http_cookie.value.name + path = http_cookie.value.path + dynamic "ttl" { + for_each = ( + http_cookie.value.ttl == null ? [] : [http_cookie.value.ttl] + ) + content { + seconds = ttl.value.seconds + nanos = ttl.value.nanos + } + } + } + } + } + } + + dynamic "iap" { + for_each = each.value.iap_config == null ? [] : [each.value.iap_config] + content { + oauth2_client_id = iap.value.oauth2_client_id + oauth2_client_secret = iap.value.oauth2_client_secret + oauth2_client_secret_sha256 = iap.value.oauth2_client_secret_sha256 + } + } + + dynamic "log_config" { + for_each = each.value.log_sample_rate == null ? [] : [""] + content { + enable = true + sample_rate = each.value.log_sample_rate + } + } + + dynamic "outlier_detection" { + for_each = ( + each.value.outlier_detection == null ? [] : [each.value.outlier_detection] + ) + iterator = od + content { + consecutive_errors = od.value.consecutive_errors + consecutive_gateway_failure = od.value.consecutive_gateway_failure + enforcing_consecutive_errors = od.value.enforcing_consecutive_errors + enforcing_consecutive_gateway_failure = od.value.enforcing_consecutive_gateway_failure + enforcing_success_rate = od.value.enforcing_success_rate + max_ejection_percent = od.value.max_ejection_percent + success_rate_minimum_hosts = od.value.success_rate_minimum_hosts + success_rate_request_volume = od.value.success_rate_request_volume + success_rate_stdev_factor = od.value.success_rate_stdev_factor + dynamic "base_ejection_time" { + for_each = ( + od.value.base_ejection_time == null ? [] : [od.value.base_ejection_time] + ) + content { + seconds = base_ejection_time.value.seconds + nanos = base_ejection_time.value.nanos + } + } + dynamic "interval" { + for_each = ( + od.value.interval == null ? [] : [od.value.interval] + ) + content { + seconds = interval.value.seconds + nanos = interval.value.nanos + } + } + } + } + + dynamic "security_settings" { + for_each = ( + each.value.security_settings == null ? [] : [each.value.security_settings] + ) + iterator = ss + content { + client_tls_policy = ss.value.client_tls_policy + subject_alt_names = ss.value.subject_alt_names + } + } +} diff --git a/modules/net-glb/backend-services.tf b/modules/net-glb/backend-services.tf deleted file mode 100644 index 3ca93da8..00000000 --- a/modules/net-glb/backend-services.tf +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright 2022 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. - */ - -# tfdoc:file:description Bucket and group backend services. - -locals { - backend_services_bucket = { - for k, v in coalesce(var.backend_services_config, {}) : - k => v if v.bucket_config != null - } - backend_services_group = { - for k, v in coalesce(var.backend_services_config, {}) : - k => v if v.group_config != null - } -} - -resource "google_compute_backend_bucket" "bucket" { - for_each = local.backend_services_bucket - name = "${var.name}-${each.key}" - description = "Terraform managed." - project = var.project_id - bucket_name = try(each.value.bucket_config.bucket_name, null) - custom_response_headers = try(each.value.bucket_config.options.custom_response_headers, null) - enable_cdn = try(each.value.enable_cdn, null) - - dynamic "cdn_policy" { - for_each = try(each.value.cdn_policy, null) == null ? [] : [each.value.cdn_policy] - content { - cache_mode = try(cdn_policy.value.cache_mode, null) - client_ttl = try(cdn_policy.value.client_ttl, null) - default_ttl = try(cdn_policy.value.default_ttl, null) - max_ttl = try(cdn_policy.value.max_ttl, null) - negative_caching = try(cdn_policy.value.negative_caching, null) - serve_while_stale = try(cdn_policy.value.serve_while_stale, null) - signed_url_cache_max_age_sec = try(cdn_policy.value.signed_url_cache_max_age_sec, null) - - dynamic "negative_caching_policy" { - for_each = try(cdn_policy.value.negative_caching_policy, null) == null ? [] : [cdn_policy.value.negative_caching_policy] - iterator = ncp - content { - code = ncp.value.code - ttl = ncp.value.ttl - } - } - } - } -} - -resource "google_compute_backend_service" "group" { - for_each = var.region == null ? local.backend_services_group : {} - name = "${var.name}-${each.key}" - project = var.project_id - description = "Terraform managed." - affinity_cookie_ttl_sec = try(each.value.group_config.options.affinity_cookie_ttl_sec, null) - enable_cdn = try(each.value.enable_cdn, null) - custom_request_headers = try(each.value.group_config.options.custom_request_headers, null) - custom_response_headers = try(each.value.group_config.options.custom_response_headers, null) - connection_draining_timeout_sec = try(each.value.group_config.options.connection_draining_timeout_sec, null) - load_balancing_scheme = try(each.value.group_config.options.load_balancing_scheme, null) - locality_lb_policy = try(each.value.group_config.options.locality_lb_policy, null) - port_name = try(each.value.group_config.options.port_name, null) - protocol = try(each.value.group_config.options.protocol, null) - security_policy = try(each.value.group_config.options.security_policy, null) - session_affinity = try(each.value.group_config.options.session_affinity, null) - timeout_sec = try(each.value.group_config.options.timeout_sec, null) - - # If no health checks are defined, use the default one. - # Otherwise, look in the health_checks_config map. - # Otherwise, use the health_check id as is (already existing). - health_checks = ( - try(length(each.value.group_config.health_checks), 0) == 0 - ? try( - [google_compute_health_check.health_check["default"].id], - null - ) - : [ - for hc in each.value.group_config.health_checks : - try(google_compute_health_check.health_check[hc].id, hc) - ] - ) - - dynamic "backend" { - for_each = try(each.value.group_config.backends, []) - content { - balancing_mode = try(backend.value.options.balancing_mode, null) - capacity_scaler = try(backend.value.options.capacity_scaler, null) - group = try(backend.value.group, null) - max_connections = try(backend.value.options.max_connections, null) - max_connections_per_instance = try(backend.value.options.max_connections_per_instance, null) - max_connections_per_endpoint = try(backend.value.options.max_connections_per_endpoint, null) - max_rate = try(backend.value.options.max_rate, null) - max_rate_per_instance = try(backend.value.options.max_rate_per_instance, null) - max_rate_per_endpoint = try(backend.value.options.max_rate_per_endpoint, null) - max_utilization = try(backend.value.options.max_utilization, null) - } - } - - dynamic "circuit_breakers" { - for_each = ( - try(each.value.group_config.options.circuit_breakers, null) == null - ? [] - : [each.value.group_config.options.circuit_breakers] - ) - iterator = cb - content { - max_requests_per_connection = try(cb.value.max_requests_per_connection, null) - max_connections = try(cb.value.max_connections, null) - max_pending_requests = try(cb.value.max_pending_requests, null) - max_requests = try(cb.value.max_requests, null) - max_retries = try(cb.value.max_retries, null) - } - } - - dynamic "consistent_hash" { - for_each = ( - try(each.value.group_config.options.consistent_hash, null) == null - ? [] - : [each.value.group_config.options.consistent_hash] - ) - content { - http_header_name = try(consistent_hash.value.http_header_name, null) - minimum_ring_size = try(consistent_hash.value.minimum_ring_size, null) - - dynamic "http_cookie" { - for_each = try(consistent_hash.value.http_cookie, null) == null ? [] : [consistent_hash.value.http_cookie] - content { - name = try(http_cookie.value.name, null) - path = try(http_cookie.value.path, null) - - dynamic "ttl" { - for_each = try(consistent_hash.value.ttl, null) == null ? [] : [consistent_hash.value.ttl] - content { - seconds = try(ttl.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - nanos = try(ttl.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - } - } - } - } - } - } - - dynamic "cdn_policy" { - for_each = ( - try(each.value.cdn_policy, null) == null - ? [] - : [each.value.cdn_policy] - ) - iterator = cdn_policy - content { - signed_url_cache_max_age_sec = try(cdn_policy.value.signed_url_cache_max_age_sec, null) - default_ttl = try(cdn_policy.value.default_ttl, null) - max_ttl = try(cdn_policy.value.max_ttl, null) - client_ttl = try(cdn_policy.value.client_ttl, null) - negative_caching = try(cdn_policy.value.negative_caching, null) - cache_mode = try(cdn_policy.value.cache_mode, null) - serve_while_stale = try(cdn_policy.value.serve_while_stale, null) - - dynamic "negative_caching_policy" { - for_each = ( - try(cdn_policy.value.negative_caching_policy, null) == null - ? [] - : [cdn_policy.value.negative_caching_policy] - ) - iterator = ncp - content { - code = try(ncp.value.code, null) - ttl = try(ncp.value.ttl, null) - } - } - } - } - - dynamic "iap" { - for_each = ( - try(each.value.group_config.options.iap, null) == null - ? [] - : [each.value.group_config.options.iap] - ) - content { - oauth2_client_id = try(iap.value.oauth2_client_id, null) - oauth2_client_secret = try(iap.value.oauth2_client_secret, null) # sensitive - oauth2_client_secret_sha256 = try(iap.value.oauth2_client_secret_sha256, null) # sensitive - } - } - - dynamic "log_config" { - for_each = ( - try(each.value.group_config.log_config, null) == null - ? [] - : [each.value.group_config.log_config] - ) - content { - enable = try(log_config.value.enable, null) - sample_rate = try(log_config.value.sample_rate, null) - } - } -} - diff --git a/modules/net-glb/backends.tf b/modules/net-glb/backends.tf new file mode 100644 index 00000000..7f4f59cc --- /dev/null +++ b/modules/net-glb/backends.tf @@ -0,0 +1,98 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description Backend groups and backend buckets resources. + +resource "google_compute_backend_bucket" "default" { + for_each = var.backend_buckets_config + project = var.project_id + name = "${var.name}-${each.key}" + bucket_name = each.value.bucket_name + compression_mode = each.value.compression_mode + custom_response_headers = each.value.custom_response_headers + description = each.value.description + edge_security_policy = each.value.edge_security_policy + enable_cdn = each.value.enable_cdn + + dynamic "cdn_policy" { + for_each = each.value.cdn_policy == null ? [] : [each.value.cdn_policy] + iterator = p + content { + cache_mode = p.value.cache_mode + client_ttl = p.value.client_ttl + default_ttl = p.value.default_ttl + max_ttl = p.value.max_ttl + negative_caching = p.value.negative_caching + request_coalescing = p.value.request_coalescing + serve_while_stale = p.value.serve_while_stale + signed_url_cache_max_age_sec = p.value.signed_url_cache_max_age_sec + dynamic "bypass_cache_on_request_headers" { + for_each = ( + p.value.bypass_cache_on_request_headers == null + ? [] + : [p.value.bypass_cache_on_request_headers] + ) + iterator = h + content { + header_name = h.value + } + } + dynamic "cache_key_policy" { + for_each = ( + p.value.cache_key_policy == null ? [] : [p.value.cache_key_policy] + ) + iterator = ckp + content { + include_http_headers = ckp.value.include_http_headers + query_string_whitelist = ckp.value.query_string_whitelist + } + } + dynamic "negative_caching_policy" { + for_each = ( + p.value.negative_caching_policy == null + ? [] + : [p.value.negative_caching_policy] + ) + iterator = ncp + content { + code = ncp.value.code + ttl = ncp.value.ttl + } + } + } + } +} + +resource "google_compute_instance_group" "default" { + for_each = var.group_configs + project = ( + each.value.project_id == null + ? var.project_id + : each.value.project_id + ) + zone = each.value.zone + name = "${var.name}-${each.key}" + description = var.description + instances = each.value.instances + + dynamic "named_port" { + for_each = each.value.named_ports + content { + name = named_port.key + port = named_port.value + } + } +} diff --git a/modules/net-glb/global-forwarding-rule.tf b/modules/net-glb/global-forwarding-rule.tf deleted file mode 100644 index 54c268f0..00000000 --- a/modules/net-glb/global-forwarding-rule.tf +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2022 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. - */ - -# tfdoc:file:description Global address and forwarding rule. - -locals { - ip_address = var.region == null ? ( - var.region == null && var.reserve_ip_address - ? google_compute_global_address.static_ip.0.id - : null - ) : null - - port_range = coalesce( - var.global_forwarding_rule_config.port_range, - var.https ? "443" : "80" - ) - - target = var.region == null ? ( - var.https - ? google_compute_target_https_proxy.https.0.id - : google_compute_target_http_proxy.http.0.id - ) : null -} - -resource "google_compute_global_address" "static_ip" { - count = var.region == null && var.reserve_ip_address ? 1 : 0 - provider = google-beta - name = var.name - project = var.project_id - description = "Terraform managed." -} - -resource "google_compute_global_forwarding_rule" "forwarding_rule" { - count = var.region == null ? 1 : 0 - provider = google-beta - name = var.name - project = var.project_id - description = "Terraform managed." - ip_protocol = var.global_forwarding_rule_config.ip_protocol - load_balancing_scheme = var.global_forwarding_rule_config.load_balancing_scheme - port_range = local.port_range - target = local.target - ip_address = local.ip_address -} diff --git a/modules/net-glb/health-check.tf b/modules/net-glb/health-check.tf new file mode 100644 index 00000000..66ba58c5 --- /dev/null +++ b/modules/net-glb/health-check.tf @@ -0,0 +1,113 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description Health check resource. + +resource "google_compute_health_check" "default" { + provider = google-beta + for_each = var.health_check_configs + project = ( + each.value.project_id == null + ? var.project_id + : each.value.project_id + ) + name = "${var.name}-${each.key}" + description = each.value.description + check_interval_sec = each.value.check_interval_sec + healthy_threshold = each.value.healthy_threshold + timeout_sec = each.value.timeout_sec + unhealthy_threshold = each.value.unhealthy_threshold + + dynamic "grpc_health_check" { + for_each = try(each.value.grpc, null) != null ? [""] : [] + content { + port = each.value.grpc.port + port_name = each.value.grpc.port_name + port_specification = each.value.grpc.port_specification + grpc_service_name = each.value.grpc.service_name + } + } + + dynamic "http_health_check" { + for_each = try(each.value.http, null) != null ? [""] : [] + content { + host = each.value.http.host + port = each.value.http.port + port_name = each.value.http.port_name + port_specification = each.value.http.port_specification + proxy_header = each.value.http.proxy_header + request_path = each.value.http.request_path + response = each.value.http.response + } + } + + dynamic "http2_health_check" { + for_each = try(each.value.http2, null) != null ? [""] : [] + content { + host = each.value.http2.host + port = each.value.http2.port + port_name = each.value.http2.port_name + port_specification = each.value.http2.port_specification + proxy_header = each.value.http2.proxy_header + request_path = each.value.http2.request_path + response = each.value.http2.response + } + } + + dynamic "https_health_check" { + for_each = try(each.value.https, null) != null ? [""] : [] + content { + host = each.value.https.host + port = each.value.https.port + port_name = each.value.https.port_name + port_specification = each.value.https.port_specification + proxy_header = each.value.https.proxy_header + request_path = each.value.https.request_path + response = each.value.https.response + } + } + + dynamic "ssl_health_check" { + for_each = try(each.value.ssl, null) != null ? [""] : [] + content { + port = each.value.ssl.port + port_name = each.value.ssl.port_name + port_specification = each.value.ssl.port_specification + proxy_header = each.value.ssl.proxy_header + request = each.value.ssl.request + response = each.value.ssl.response + } + } + + dynamic "tcp_health_check" { + for_each = try(each.value.tcp, null) != null ? [""] : [] + content { + port = each.value.tcp.port + port_name = each.value.tcp.port_name + port_specification = each.value.tcp.port_specification + proxy_header = each.value.tcp.proxy_header + request = each.value.tcp.request + response = each.value.tcp.response + } + } + + dynamic "log_config" { + for_each = try(each.value.enable_logging, null) == true ? [""] : [] + content { + enable = true + } + } +} diff --git a/modules/net-glb/health-checks.tf b/modules/net-glb/health-checks.tf deleted file mode 100644 index 0c0aad63..00000000 --- a/modules/net-glb/health-checks.tf +++ /dev/null @@ -1,253 +0,0 @@ -/** - * Copyright 2022 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. - */ - -# tfdoc:file:description Health checks. - -locals { - # Get group backend services without health checks defined - _backends_without_hcs = [ - for k, v in coalesce(var.backend_services_config, {}) : - v if( - v.group_config != null - && ( - try(v.group_config.health_checks, null) == null - || length(try(v.group_config.health_checks, [])) == 0 - ) - ) - ] - - health_checks_config_defaults = ( - try(var.health_checks_config_defaults, null) == null - ? null - : { default = var.health_checks_config_defaults } - ) - - # If at least one group backend service without HC is defined, - # create also a default HC (if default HC is not null) - health_checks_config = ( - length(local._backends_without_hcs) > 0 - ? merge( - coalesce(local.health_checks_config_defaults, {}), - coalesce(var.health_checks_config, {}) - ) - : coalesce(var.health_checks_config, {}) - ) -} - -resource "google_compute_health_check" "health_check" { - for_each = var.region == null ? local.health_checks_config : {} - provider = google-beta - name = "${var.name}-${each.key}" - project = var.project_id - description = "Terraform managed." - check_interval_sec = try(each.value.options.check_interval_sec, null) - healthy_threshold = try(each.value.options.healthy_threshold, null) - timeout_sec = try(each.value.options.timeout_sec, null) - unhealthy_threshold = try(each.value.options.unhealthy_threshold, null) - - dynamic "http_health_check" { - for_each = ( - try(each.value.type, null) == "http" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - host = try(each.value.check.host, null) - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request_path = try(each.value.check.request_path, null) - response = try(each.value.check.response, null) - } - } - - dynamic "https_health_check" { - for_each = ( - try(each.value.type, null) == "https" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - host = try(each.value.check.host, null) - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request_path = try(each.value.check.request_path, null) - response = try(each.value.check.response, null) - } - } - - dynamic "tcp_health_check" { - for_each = ( - try(each.value.type, null) == "tcp" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request = try(each.value.check.request, null) - response = try(each.value.check.response, null) - } - } - - dynamic "ssl_health_check" { - for_each = ( - try(each.value.type, null) == "ssl" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request = try(each.value.check.request, null) - response = try(each.value.check.response, null) - } - } - - dynamic "http2_health_check" { - for_each = ( - try(each.value.type, null) == "http2" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - host = try(each.value.check.host, null) - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request_path = try(each.value.check.request_path, null) - response = try(each.value.check.response, null) - } - } - - dynamic "log_config" { - for_each = try(each.value.logging, false) ? { 0 = 0 } : {} - content { - enable = true - } - } -} - -resource "google_compute_region_health_check" "health_check" { - for_each = var.region != null ? local.health_checks_config : {} - provider = google-beta - name = "${var.name}-${each.key}" - project = var.project_id - region = var.region - description = "Terraform managed." - check_interval_sec = try(each.value.options.check_interval_sec, null) - healthy_threshold = try(each.value.options.healthy_threshold, null) - timeout_sec = try(each.value.options.timeout_sec, null) - unhealthy_threshold = try(each.value.options.unhealthy_threshold, null) - - dynamic "http_health_check" { - for_each = ( - try(each.value.type, null) == "http" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - host = try(each.value.check.host, null) - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request_path = try(each.value.check.request_path, null) - response = try(each.value.check.response, null) - } - } - - dynamic "https_health_check" { - for_each = ( - try(each.value.type, null) == "https" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - host = try(each.value.check.host, null) - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request_path = try(each.value.check.request_path, null) - response = try(each.value.check.response, null) - } - } - - dynamic "tcp_health_check" { - for_each = ( - try(each.value.type, null) == "tcp" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request = try(each.value.check.request, null) - response = try(each.value.check.response, null) - } - } - - dynamic "ssl_health_check" { - for_each = ( - try(each.value.type, null) == "ssl" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request = try(each.value.check.request, null) - response = try(each.value.check.response, null) - } - } - - dynamic "http2_health_check" { - for_each = ( - try(each.value.type, null) == "http2" || try(each.value.type, null) == null - ? { 1 = 1 } - : {} - ) - content { - host = try(each.value.check.host, null) - port = try(each.value.check.port, null) - port_name = try(each.value.check.port_name, null) - port_specification = try(each.value.check.port_specification, null) - proxy_header = try(each.value.check.proxy_header, null) - request_path = try(each.value.check.request_path, null) - response = try(each.value.check.response, null) - } - } - - dynamic "log_config" { - for_each = try(each.value.logging, false) ? { 0 = 0 } : {} - content { - enable = true - } - } -} diff --git a/modules/net-glb/main.tf b/modules/net-glb/main.tf new file mode 100644 index 00000000..ebe438ec --- /dev/null +++ b/modules/net-glb/main.tf @@ -0,0 +1,91 @@ +/** + * Copyright 2022 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. + */ + +locals { + fwd_rule_ports = ( + var.protocol == "HTTPS" ? [443] : coalesce(var.ports, [80]) + ) + fwd_rule_target = ( + var.protocol == "HTTPS" + ? google_compute_target_https_proxy.default.0.id + : google_compute_target_http_proxy.default.0.id + ) + proxy_ssl_certificates = concat( + coalesce(var.ssl_certificates.certificate_ids, []), + [for k, v in google_compute_ssl_certificate.default : v.id], + [for k, v in google_compute_managed_ssl_certificate.default : v.id] + ) +} + +resource "google_compute_global_forwarding_rule" "default" { + provider = google-beta + project = var.project_id + name = var.name + description = var.description + ip_address = var.address + ip_protocol = "TCP" + load_balancing_scheme = ( + var.use_classic_version ? "EXTERNAL" : "EXTERNAL_MANAGED" + ) + port_range = join(",", local.fwd_rule_ports) + labels = var.labels + target = local.fwd_rule_target +} + +# certificates + +resource "google_compute_ssl_certificate" "default" { + for_each = var.ssl_certificates.create_configs + project = var.project_id + name = "${var.name}-${each.key}" + certificate = trimspace(each.value.certificate) + private_key = trimspace(each.value.private_key) +} + +resource "google_compute_managed_ssl_certificate" "default" { + for_each = var.ssl_certificates.managed_configs + project = var.project_id + name = "${var.name}-${each.key}" + description = each.value.description + managed { + domains = each.value.domains + } + lifecycle { + create_before_destroy = true + } +} + +# proxies + +resource "google_compute_target_http_proxy" "default" { + count = var.protocol == "HTTPS" ? 0 : 1 + project = var.project_id + name = var.name + description = var.description + url_map = google_compute_url_map.default.id +} + +resource "google_compute_target_https_proxy" "default" { + count = var.protocol == "HTTPS" ? 1 : 0 + project = var.project_id + name = var.name + description = var.description + certificate_map = var.https_proxy_config.certificate_map + quic_override = var.https_proxy_config.quic_override + ssl_certificates = local.proxy_ssl_certificates + ssl_policy = var.https_proxy_config.ssl_policy + url_map = google_compute_url_map.default.id +} diff --git a/modules/net-glb/negs.tf b/modules/net-glb/negs.tf new file mode 100644 index 00000000..9edae1cd --- /dev/null +++ b/modules/net-glb/negs.tf @@ -0,0 +1,159 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description NEG resources. + +locals { + _neg_endpoints_global = flatten([ + for k, v in local.neg_global : [ + for vv in v.internet.endpoints : + merge(vv, { neg = k, use_fqdn = v.internet.use_fqdn }) + ] + ]) + _neg_endpoints_zonal = flatten([ + for k, v in local.neg_zonal : [ + for vv in v.endpoints : + merge(vv, { neg = k, zone = v.zone }) + ] + ]) + neg_endpoints_global = { + for v in local._neg_endpoints_global : + "${v.neg}-${v.destination}-${coalesce(v.port, "none")}" => v + } + neg_endpoints_zonal = { + for v in local._neg_endpoints_zonal : + "${v.neg}-${v.ip_address}-${coalesce(v.port, "none")}" => v + } + neg_global = { + for k, v in var.neg_configs : + k => v if v.internet != null + } + neg_regional_psc = { + for k, v in var.neg_configs : + k => v if v.psc != null + } + neg_regional_serverless = { + for k, v in var.neg_configs : + k => v if v.cloudrun != null || v.cloudfunction != null + } + neg_zonal = { + # we need to rebuild new objects as we cannot merge different types + for k, v in var.neg_configs : k => { + description = v.description + endpoints = v.gce != null ? v.gce.endpoints : v.hybrid.endpoints + network = v.gce != null ? v.gce.network : v.hybrid.network + subnetwork = v.gce != null ? v.gce.subnetwork : null + type = v.gce != null ? "GCE_VM_IP_PORT" : "NON_GCP_PRIVATE_IP_PORT" + zone = v.gce != null ? v.gce.zone : v.hybrid.zone + } if v.gce != null || v.hybrid != null + } +} + + +resource "google_compute_global_network_endpoint_group" "default" { + for_each = local.neg_global + project = var.project_id + name = "${var.name}-${each.key}" + # re-enable once provider properly supports this + # default_port = each.value.default_port + description = coalesce(each.value.description, var.description) + network_endpoint_type = ( + each.value.internet.use_fqdn ? "INTERNET_FQDN_PORT" : "INTERNET_IP_PORT" + ) +} + +resource "google_compute_global_network_endpoint" "default" { + for_each = local.neg_endpoints_global + project = ( + google_compute_global_network_endpoint_group.default[each.value.neg].project + ) + global_network_endpoint_group = ( + google_compute_global_network_endpoint_group.default[each.value.neg].name + ) + fqdn = each.value.use_fqdn ? each.value.destination : null + ip_address = each.value.use_fqdn ? null : each.value.destination + port = each.value.port +} + + +resource "google_compute_network_endpoint_group" "default" { + for_each = local.neg_zonal + project = var.project_id + zone = each.value.zone + name = "${var.name}-${each.key}" + # re-enable once provider properly supports this + # default_port = each.value.default_port + description = coalesce(each.value.description, var.description) + network_endpoint_type = each.value.type + network = each.value.network + subnetwork = ( + each.value.type == "NON_GCP_PRIVATE_IP_PORT" + ? null + : each.value.subnetwork + ) +} + +resource "google_compute_network_endpoint" "default" { + for_each = local.neg_endpoints_zonal + project = ( + google_compute_network_endpoint_group.default[each.value.neg].project + ) + network_endpoint_group = ( + google_compute_network_endpoint_group.default[each.value.neg].name + ) + instance = try(each.value.instance, null) + ip_address = each.value.ip_address + port = each.value.port + zone = each.value.zone +} + +resource "google_compute_region_network_endpoint_group" "psc" { + for_each = local.neg_regional_psc + project = var.project_id + region = each.value.psc.region + name = "${var.name}-${each.key}" + description = coalesce(each.value.description, var.description) + network_endpoint_type = "PRIVATE_SERVICE_CONNECT" + psc_target_service = each.value.psc.target_service + network = each.value.psc.network + subnetwork = each.value.psc.subnetwork +} + +resource "google_compute_region_network_endpoint_group" "serverless" { + for_each = local.neg_regional_serverless + project = var.project_id + region = try( + each.value.cloudrun.region, each.value.cloudfunction.region, null + ) + name = "${var.name}-${each.key}" + description = coalesce(each.value.description, var.description) + network_endpoint_type = "SERVERLESS" + dynamic "cloud_function" { + for_each = each.value.cloudfunction == null ? [] : [""] + content { + function = each.value.cloudfunction.target_function + url_mask = each.value.cloudfunction.target_urlmask + } + } + dynamic "cloud_run" { + for_each = each.value.cloudrun == null ? [] : [""] + content { + service = try(each.value.cloudrun.target_service.name, null) + tag = try(each.value.cloudrun.target_service.tag, null) + url_mask = each.value.cloudrun.target_urlmask + } + } +} diff --git a/modules/net-glb/outputs.tf b/modules/net-glb/outputs.tf index 38aeed13..bc95e985 100644 --- a/modules/net-glb/outputs.tf +++ b/modules/net-glb/outputs.tf @@ -14,57 +14,40 @@ * limitations under the License. */ -output "backend_services" { +output "address" { + description = "Forwarding rule address." + value = google_compute_global_forwarding_rule.default.ip_address +} + +output "backend_service_ids" { description = "Backend service resources." value = { - bucket = try(google_compute_backend_bucket.bucket, []) - group = try(google_compute_backend_service.group, []) + for k, v in google_compute_backend_service.default : k => v.id } } output "forwarding_rule" { - description = "The regional forwarding rule." - value = var.region == null ? google_compute_global_forwarding_rule.forwarding_rule.0 : google_compute_forwarding_rule.forwarding_rule.0 + description = "Forwarding rule resource." + value = google_compute_global_forwarding_rule.default } -output "global_forwarding_rule" { - description = "The global forwarding rule." - value = var.region == null ? google_compute_global_forwarding_rule.forwarding_rule.0 : null +output "group_ids" { + description = "Autogenerated instance group ids." + value = { + for k, v in google_compute_instance_group.default : k => v.id + } } -output "health_checks" { - description = "Health-check resources." - value = try(google_compute_health_check.health_check, []) +output "health_check_ids" { + description = "Autogenerated health check ids." + value = { + for k, v in google_compute_health_check.default : k => v.id + } } -output "ip_address" { - description = "The reserved global IP address." - value = var.region == null ? try(google_compute_global_address.static_ip.0.address, null) : try(google_compute_address.static_ip.0.address, null) -} - -output "ip_address_self_link" { - description = "The URI of the reserved global IP address." - value = var.region == null ? google_compute_global_forwarding_rule.forwarding_rule.0.ip_address : google_compute_forwarding_rule.forwarding_rule.0.ip_address -} - -output "ssl_certificates" { - description = "The SSL certificate." - value = try( - google_compute_managed_ssl_certificate.managed, - google_compute_ssl_certificate.unmanaged, - null - ) -} - -output "target_proxy" { - description = "The target proxy." - value = try( - google_compute_target_http_proxy.http, - google_compute_target_https_proxy.https - ) -} - -output "url_map" { - description = "The url-map." - value = google_compute_url_map.url_map +output "neg_ids" { + description = "Autogenerated network endpoint group ids." + value = { + for k, v in google_compute_network_endpoint_group.default : k => v.id + } } diff --git a/modules/net-glb/regional-backend-services.tf b/modules/net-glb/regional-backend-services.tf deleted file mode 100644 index 228c958a..00000000 --- a/modules/net-glb/regional-backend-services.tf +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright 2022 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. - */ - -# tfdoc:file:description Bucket and group backend services for regional load balancers. - -resource "google_compute_region_backend_service" "group" { - for_each = var.region != null ? local.backend_services_group : {} - name = "${var.name}-${each.key}" - project = var.project_id - region = var.region - description = "Terraform managed." - affinity_cookie_ttl_sec = try(each.value.group_config.options.affinity_cookie_ttl_sec, null) - enable_cdn = try(each.value.enable_cdn, null) - connection_draining_timeout_sec = try(each.value.group_config.options.connection_draining_timeout_sec, null) - load_balancing_scheme = try(each.value.group_config.options.load_balancing_scheme, null) - locality_lb_policy = try(each.value.group_config.options.locality_lb_policy, null) - port_name = try(each.value.group_config.options.port_name, null) - protocol = try(each.value.group_config.options.protocol, null) - session_affinity = try(each.value.group_config.options.session_affinity, null) - timeout_sec = try(each.value.group_config.options.timeout_sec, null) - - # If no health checks are defined, use the default one. - # Otherwise, look in the health_checks_config map. - # Otherwise, use the health_check id as is (already existing). - health_checks = ( - try(length(each.value.group_config.health_checks), 0) == 0 - ? try( - [google_compute_region_health_check.health_check["default"].id], - null - ) - : [ - for hc in each.value.group_config.health_checks : - try(google_compute_region_health_check.health_check[hc].id, hc) - ] - ) - - dynamic "backend" { - for_each = try(each.value.group_config.backends, []) - content { - balancing_mode = try(backend.value.options.balancing_mode, null) - capacity_scaler = try(backend.value.options.capacity_scaler, null) - group = try(backend.value.group, null) - max_connections = try(backend.value.options.max_connections, null) - max_connections_per_instance = try(backend.value.options.max_connections_per_instance, null) - max_connections_per_endpoint = try(backend.value.options.max_connections_per_endpoint, null) - max_rate = try(backend.value.options.max_rate, null) - max_rate_per_instance = try(backend.value.options.max_rate_per_instance, null) - max_rate_per_endpoint = try(backend.value.options.max_rate_per_endpoint, null) - max_utilization = try(backend.value.options.max_utilization, null) - } - } - - dynamic "circuit_breakers" { - for_each = ( - try(each.value.group_config.options.circuit_breakers, null) == null - ? [] - : [each.value.group_config.options.circuit_breakers] - ) - iterator = cb - content { - max_requests_per_connection = try(cb.value.max_requests_per_connection, null) - max_connections = try(cb.value.max_connections, null) - max_pending_requests = try(cb.value.max_pending_requests, null) - max_requests = try(cb.value.max_requests, null) - max_retries = try(cb.value.max_retries, null) - } - } - - dynamic "consistent_hash" { - for_each = ( - try(each.value.group_config.options.consistent_hash, null) == null - ? [] - : [each.value.group_config.options.consistent_hash] - ) - content { - http_header_name = try(consistent_hash.value.http_header_name, null) - minimum_ring_size = try(consistent_hash.value.minimum_ring_size, null) - - dynamic "http_cookie" { - for_each = try(consistent_hash.value.http_cookie, null) == null ? [] : [consistent_hash.value.http_cookie] - content { - name = try(http_cookie.value.name, null) - path = try(http_cookie.value.path, null) - - dynamic "ttl" { - for_each = try(consistent_hash.value.ttl, null) == null ? [] : [consistent_hash.value.ttl] - content { - seconds = try(ttl.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - nanos = try(ttl.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - } - } - } - } - } - } - - dynamic "cdn_policy" { - for_each = ( - try(each.value.cdn_policy, null) == null - ? [] - : [each.value.cdn_policy] - ) - iterator = cdn_policy - content { - signed_url_cache_max_age_sec = try(cdn_policy.value.signed_url_cache_max_age_sec, null) - default_ttl = try(cdn_policy.value.default_ttl, null) - max_ttl = try(cdn_policy.value.max_ttl, null) - client_ttl = try(cdn_policy.value.client_ttl, null) - negative_caching = try(cdn_policy.value.negative_caching, null) - cache_mode = try(cdn_policy.value.cache_mode, null) - serve_while_stale = try(cdn_policy.value.serve_while_stale, null) - - dynamic "negative_caching_policy" { - for_each = ( - try(cdn_policy.value.negative_caching_policy, null) == null - ? [] - : [cdn_policy.value.negative_caching_policy] - ) - iterator = ncp - content { - code = try(ncp.value.code, null) - } - } - } - } - - dynamic "iap" { - for_each = ( - try(each.value.group_config.options.iap, null) == null - ? [] - : [each.value.group_config.options.iap] - ) - content { - oauth2_client_id = try(iap.value.oauth2_client_id, null) - oauth2_client_secret = try(iap.value.oauth2_client_secret, null) # sensitive - oauth2_client_secret_sha256 = try(iap.value.oauth2_client_secret_sha256, null) # sensitive - } - } - - dynamic "log_config" { - for_each = ( - try(each.value.group_config.log_config, null) == null - ? [] - : [each.value.group_config.log_config] - ) - content { - enable = try(log_config.value.enable, null) - sample_rate = try(log_config.value.sample_rate, null) - } - } -} diff --git a/modules/net-glb/regional-forwarding-rule.tf b/modules/net-glb/regional-forwarding-rule.tf deleted file mode 100644 index 5f4e3d71..00000000 --- a/modules/net-glb/regional-forwarding-rule.tf +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2022 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. - */ - -# tfdoc:file:description Global address and forwarding rule. - -locals { - regional_ip_address = var.region != null ? ( - var.reserve_ip_address - ? google_compute_address.static_ip.0.id - : null - ) : null - - regional_port_range = coalesce( - var.forwarding_rule_config.port_range, - var.https ? "443" : "80" - ) - - regional_target = var.region != null ? ( - var.https - ? google_compute_region_target_https_proxy.https.0.id - : google_compute_region_target_http_proxy.http.0.id - ) : null -} - -resource "google_compute_address" "static_ip" { - count = var.region != null && var.reserve_ip_address ? 1 : 0 - provider = google-beta - name = var.name - project = var.project_id - region = var.region - description = "Terraform managed." - address_type = "EXTERNAL" -} - -resource "google_compute_forwarding_rule" "forwarding_rule" { - count = var.region != null ? 1 : 0 - provider = google-beta - name = var.name - project = var.project_id - region = var.region - description = "Terraform managed." - ip_protocol = var.forwarding_rule_config.ip_protocol - load_balancing_scheme = var.forwarding_rule_config.load_balancing_scheme - port_range = local.regional_port_range - target = local.regional_target - ip_address = local.regional_ip_address - network_tier = var.forwarding_rule_config.network_tier - network = var.forwarding_rule_config.network -} diff --git a/modules/net-glb/regional-url-map.tf b/modules/net-glb/regional-url-map.tf deleted file mode 100644 index a836bb69..00000000 --- a/modules/net-glb/regional-url-map.tf +++ /dev/null @@ -1,717 +0,0 @@ -/** - * Copyright 2022 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. - */ - -# tfdoc:file:description regional URL maps. -locals { - # Look for a backend services in the config whose id is - # the default_service given in the url-map. - # If not found, use the default_service id as given - # (assuming it's already existing). - # If the variable is null, will be set to null. - _regional_default_service = try( - google_compute_backend_bucket.bucket[var.url_map_config.default_service].id, - google_compute_region_backend_service.group[var.url_map_config.default_service].id, - var.url_map_config.default_service, - null - ) - - # If no backend services are specified, - # the first backend service defined is associated - regional_default_service = ( - try(local._regional_default_service, null) == null - && try(var.url_map_config.default_route_action.weighted_backend_services, null) == null - && try(var.url_map_config.default_url_redirect, null) == null - ? try( - google_compute_backend_bucket.bucket[keys(google_compute_backend_bucket.bucket)[0]].id, - google_compute_region_backend_service.group[keys(google_compute_region_backend_service.group)[0]].id, - null - ) - : null - ) -} - -resource "google_compute_region_url_map" "url_map" { - count = var.region != null ? 1 : 0 - name = var.name - description = "Terraform managed." - project = var.project_id - region = var.region - default_service = local.regional_default_service - - dynamic "host_rule" { - for_each = ( - try(var.url_map_config.host_rules, null) == null - ? [] - : var.url_map_config.host_rules - ) - content { - description = try(host_rule.value.description, null) - hosts = try(host_rule.value.hosts, null) - path_matcher = try(host_rule.value.path_matcher, null) - } - } - - dynamic "path_matcher" { - for_each = ( - try(var.url_map_config.path_matchers, null) == null - ? [] - : var.url_map_config.path_matchers - ) - content { - name = try(path_matcher.value.name, null) - description = try(path_matcher.value.description, null) - default_service = try( - google_compute_backend_bucket.bucket[var.url_map_config.default_service].id, - google_compute_region_backend_service.group[var.url_map_config.default_service].id, - path_matcher.value.default_service, - null - ) - - dynamic "path_rule" { - for_each = ( - try(path_matcher.value.path_rules, null) == null - ? [] - : path_matcher.value.path_rules - ) - content { - paths = try(path_rule.value.paths, null) - service = try( - google_compute_backend_bucket.bucket[path_rule.value.service].id, - google_compute_region_backend_service.group[path_rule.value.service].id, - path_rule.value.service, - null - ) - - dynamic "route_action" { - for_each = ( - try(path_rule.value.route_action, null) == null - ? [] - : [path_rule.value.route_action] - ) - content { - - dynamic "cors_policy" { - for_each = ( - try(route_action.value.cors_policy, null) == null - ? [] - : [route_action.value.cors_policy] - ) - content { - allow_credentials = try(cors_policy.value.allow_credentials, null) - allow_headers = try(cors_policy.value.allow_headers, null) - allow_methods = try(cors_policy.value.allow_methods, null) - allow_origin_regexes = try(cors_policy.value.allow_origin_regexes, null) - allow_origins = try(cors_policy.value.allow_origins, null) - disabled = try(cors_policy.value.disabled, null) - expose_headers = try(cors_policy.value.expose_headers, null) - max_age = try(cors_policy.value.max_age, null) - } - } - - dynamic "fault_injection_policy" { - for_each = ( - try(route_action.value.fault_injection_policy, null) == null - ? [] - : [route_action.value.fault_injection_policy] - ) - iterator = policy - content { - - dynamic "abort" { - for_each = ( - try(policy.value.abort, null) == null - ? [] - : [policy.value.abort] - ) - content { - http_status = try(abort.value.http_status, null) # Must be between 200 and 599 inclusive - percentage = try(abort.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - } - } - - dynamic "delay" { - for_each = ( - try(policy.value.delay, null) == null - ? [] - : [policy.value.delay] - ) - content { - percentage = try(delay.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - - dynamic "fixed_delay" { - for_each = ( - try(delay.value.fixed_delay, null) == null - ? [] - : [delay.value.fixed_delay] - ) - content { - nanos = try(fixed_delay.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(fixed_delay.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - } - } - - dynamic "request_mirror_policy" { - for_each = ( - try(route_action.value.request_mirror_policy, null) == null - ? [] - : [route_action.value.request_mirror_policy] - ) - iterator = policy - content { - backend_service = try( - google_compute_backend_bucket.bucket[policy.value.backend_service].id, - google_compute_region_backend_service.group[policy.value.backend_service].id, - policy.value.backend_service, - null - ) - } - } - - dynamic "retry_policy" { - for_each = ( - try(route_action.value.retry_policy, null) == null - ? [] - : [route_action.value.retry_policy] - ) - iterator = policy - content { - num_retries = try(policy.num_retries, null) # Must be > 0 - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#retry_conditions - retry_conditions = try(policy.retry_conditions, null) - - dynamic "per_try_timeout" { - for_each = ( - try(policy.value.per_try_timeout, null) == null - ? [] - : [policy.value.per_try_timeout] - ) - iterator = timeout - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - - dynamic "timeout" { - for_each = ( - try(route_action.value.timeout, null) == null - ? [] - : [route_action.value.timeout] - ) - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - - dynamic "url_rewrite" { - for_each = ( - try(route_action.value.url_rewrite, null) == null - ? [] - : [route_action.value.url_rewrite] - ) - content { - host_rewrite = try(url_rewrite.value.host_rewrite, null) # Must be between 1 and 255 characters - path_prefix_rewrite = try(url_rewrite.value.path_prefix_rewrite, null) # Must be between 1 and 1024 characters - } - } - - dynamic "weighted_backend_services" { - for_each = ( - try(route_action.value.weighted_backend_services, null) == null - ? [] - : route_action.value.weighted_backend_services - ) - iterator = weighted - content { - weight = try(weighted.value.weigth, null) - backend_service = try( - google_compute_backend_bucket.bucket[weighted.value.backend_service].id, - google_compute_backend_service.group[weighted.value.backend_service].id, - policy.value.backend_service, - null - ) - dynamic "header_action" { - for_each = ( - try(path_matcher.value.header_action, null) == null - ? [] - : [path_matcher.value.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] : - [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - } - } - } - } - - dynamic "url_redirect" { - for_each = ( - try(path_rule.value.url_redirect, null) == null - ? [] - : path_rule.value.url_redirect - ) - content { - host_redirect = try(url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters - https_redirect = try(url_redirect.value.https_redirect, null) - path_redirect = try(url_redirect.value.path_redirect, null) - prefix_redirect = try(url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters - # Valid valus at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code - redirect_response_code = try(url_redirect.value.redirect_response_code, null) - strip_query = try(url_redirect.value.strip_query, null) - } - } - } - } - - dynamic "route_rules" { - for_each = ( - try(path_matcher.value.route_rules, null) == null - ? [] - : path_matcher.value.route_rules - ) - content { - priority = try(route_rules.value.priority, null) - service = try( - google_compute_backend_bucket.bucket[route_rules.value.service].id, - google_compute_backend_service.group[route_rules.value.service].id, - route_rules.value.service, - null - ) - - dynamic "header_action" { - for_each = ( - try(path_matcher.value.header_action, null) == null - ? [] - : [path_matcher.value.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] - : [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - - dynamic "match_rules" { - for_each = ( - try(path_matcher.value.match_rules, null) == null - ? [] - : path_matcher.value.match_rules - ) - content { - full_path_match = try(match_rules.value.full_path_match, null) # Must be between 1 and 1024 characters - ignore_case = try(match_rules.value.ignore_case, null) - prefix_match = try(match_rules.value.prefix_match, null) - regex_match = try(match_rules.value.regex_match, null) - - dynamic "header_matches" { - for_each = ( - try(match_rules.value.header_matches, null) == null - ? [] - : [match_rules.value.header_matches] - ) - content { - exact_match = try(header_matches.value.exact_match, null) - header_name = try(header_matches.value.header_name, null) - invert_match = try(header_matches.value.invert_match, null) - prefix_match = try(header_matches.value.prefix_match, null) - present_match = try(header_matches.value.present_match, null) - regex_match = try(header_matches.value.regex_match, null) - suffix_match = try(header_matches.value, null) - - dynamic "range_match" { - for_each = try(header_matches.value.range_match, null) == null ? [] : [header_matches.value.range_match] - content { - range_end = try(range_match.value.range_end, null) - range_start = try(range_match.value.range_start, null) - } - } - } - } - - dynamic "metadata_filters" { - for_each = ( - try(match_rules.value.metadata_filters, null) == null - ? [] - : [match_rules.value.metadata_filters] - ) - content { - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#filter_match_criteria - filter_match_criteria = try(metadata_filters.value.filter_match_criteria, null) - - dynamic "filter_labels" { - for_each = ( - try(metadata_filters.value.filter_labels, null) == null - ? [] - : metadata_filters.value.filter_labels - ) - content { - name = try(filter_labels.value.name, null) # Must be between 1 and 1024 characters - value = try(filter_labels.value.value, null) # Must be between 1 and 1024 characters - } - } - } - } - - dynamic "query_parameter_matches" { - for_each = ( - try(match_rules.value.query_parameter_matches, null) == null - ? [] - : [match_rules.value.query_parameter_matches] - ) - iterator = query - content { - exact_match = try(query.value.exact_match, null) - name = try(query.value.name, null) - present_match = try(query.value.present_match, null) - regex_match = try(query.value.regex_match, null) - } - } - } - } - - dynamic "route_action" { - for_each = ( - try(route_rules.value.route_action, null) == null - ? [] - : [route_rules.value.route_action] - ) - content { - - dynamic "cors_policy" { - for_each = ( - try(route_action.value.cors_policy, null) == null - ? [] - : [route_action.value.cors_policy] - ) - content { - allow_credentials = try(cors_policy.value.allow_credentials, null) - allow_headers = try(cors_policy.value.allow_headers, null) - allow_methods = try(cors_policy.value.allow_methods, null) - allow_origin_regexes = try(cors_policy.value.allow_origin_regexes, null) - allow_origins = try(cors_policy.value.allow_origins, null) - disabled = try(cors_policy.value.disabled, null) - expose_headers = try(cors_policy.value.expose_headers, null) - max_age = try(cors_policy.value.max_age, null) - } - } - - dynamic "fault_injection_policy" { - for_each = ( - try(route_action.value.fault_injection_policy, null) == null - ? [] - : [route_action.value.fault_injection_policy] - ) - iterator = policy - content { - - dynamic "abort" { - for_each = ( - try(policy.value.abort, null) == null - ? [] - : [policy.value.abort] - ) - content { - http_status = try(abort.value.http_status, null) # Must be between 200 and 599 inclusive - percentage = try(abort.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - } - } - - dynamic "delay" { - for_each = ( - try(policy.value.delay, null) == null - ? [] - : [policy.value.delay] - ) - content { - percentage = try(delay.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - - dynamic "fixed_delay" { - for_each = ( - try(delay.value.fixed_delay, null) == null - ? [] - : [delay.value.fixed_delay] - ) - content { - nanos = try(fixed_delay.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(fixed_delay.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - } - } - - dynamic "request_mirror_policy" { - for_each = ( - try(route_action.value.request_mirror_policy, null) == null - ? [] - : [route_action.value.request_mirror_policy] - ) - iterator = policy - content { - backend_service = try( - google_compute_backend_bucket.bucket[policy.value.backend_service].id, - google_compute_backend_service.group[policy.value.backend_service].id, - policy.value.backend_service, - null - ) - } - } - - dynamic "retry_policy" { - for_each = ( - try(route_action.value.retry_policy, null) == null - ? [] - : [route_action.value.retry_policy] - ) - iterator = policy - content { - num_retries = try(policy.num_retries, null) # Must be > 0 - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#retry_conditions - retry_conditions = try(policy.retry_conditions, null) - - dynamic "per_try_timeout" { - for_each = ( - try(policy.value.per_try_timeout, null) == null - ? [] - : [policy.value.per_try_timeout] - ) - iterator = timeout - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - - dynamic "timeout" { - for_each = ( - try(route_action.value.timeout, null) == null - ? [] - : [route_action.value.timeout] - ) - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - - dynamic "url_rewrite" { - for_each = ( - try(route_action.value.url_rewrite, null) == null - ? [] - : [route_action.value.url_rewrite] - ) - content { - host_rewrite = try(url_rewrite.value.host_rewrite, null) # Must be between 1 and 255 characters - path_prefix_rewrite = try(url_rewrite.value.path_prefix_rewrite, null) # Must be between 1 and 1024 characters - } - } - - dynamic "weighted_backend_services" { - for_each = ( - try(route_action.value.weighted_backend_services, null) == null - ? [] - : [route_action.value.url_rewrite] - ) - iterator = weighted - content { - weight = try(weighted.value.weigth, null) - backend_service = try( - google_compute_backend_bucket.bucket[weighted.value.backend_service].id, - google_compute_backend_service.group[weighted.value.backend_service].id, - weighted.value.backend_service, - null - ) - - dynamic "header_action" { - for_each = ( - try(path_matcher.value.header_action, null) == null - ? [] : - [path_matcher.value.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] - : [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - } - } - } - } - - dynamic "url_redirect" { - for_each = ( - try(route_rules.value.url_redirect, null) == null - ? [] - : route_rules.value.url_redirect - ) - content { - host_redirect = try(url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters - https_redirect = try(url_redirect.value.https_redirect, null) - path_redirect = try(url_redirect.value.path_redirect, null) - prefix_redirect = try(url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters - # Valid valus at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code - redirect_response_code = try(url_redirect.value.redirect_response_code, null) - strip_query = try(url_redirect.value.strip_query, null) - } - } - } - } - - dynamic "default_url_redirect" { - for_each = ( - try(path_matcher.value.default_url_redirect, null) == null - ? [] - : path_matcher.value.default_url_redirect - ) - content { - host_redirect = try(default_url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters - https_redirect = try(default_url_redirect.value.https_redirect, null) - path_redirect = try(default_url_redirect.value.path_redirect, null) # Must be between 1 and 1024 characters - prefix_redirect = try(default_url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code - redirect_response_code = try(default_url_redirect.value.redirect_response_code, null) - strip_query = try(default_url_redirect.value.strip_query, null) - } - } - } - } - - # Up to 100 tests per url_map - dynamic "test" { - for_each = ( - try(var.url_map_config.tests, null) == null - ? [] - : var.url_map_config.tests - ) - content { - description = try(test.value.description, null) - host = try(test.value.host, null) - path = try(test.value.path, null) - service = try( - google_compute_backend_bucket.bucket[test.value.service].id, - google_compute_backend_service.group[test.value.service].id, - test.value.service, - null - ) - } - } - - dynamic "default_url_redirect" { - for_each = ( - try(var.url_map_config.default_url_redirect, null) == null - ? [] - : [var.url_map_config.default_url_redirect] - ) - content { - host_redirect = try(default_url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters - https_redirect = try(default_url_redirect.value.https_redirect, null) - path_redirect = try(default_url_redirect.value.path_redirect, null) # Must be between 1 and 1024 characters - prefix_redirect = try(default_url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code - redirect_response_code = try(default_url_redirect.value.redirect_response_code, null) - strip_query = try(default_url_redirect.value.strip_query, null) - } - } -} diff --git a/modules/net-glb/ssl-certificates.tf b/modules/net-glb/ssl-certificates.tf deleted file mode 100644 index 8e45f5ff..00000000 --- a/modules/net-glb/ssl-certificates.tf +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2022 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. - */ - -# tfdoc:file:description SSL certificates. - -locals { - # If the HTTPS target proxy has no SSL certs - # set, create also a default managed SSL cert - ssl_certificates_config = merge( - coalesce(var.ssl_certificates_config, {}), - try(length(var.target_proxy_https_config.ssl_certificates), 0) == 0 - ? { default = var.ssl_certificates_config_defaults } - : {} - ) - - ssl_certs_managed = ( - var.https - ? { - for k, v in coalesce(local.ssl_certificates_config, {}) : - k => v if v.unmanaged_config == null - } - : {} - ) - ssl_certs_unmanaged = ( - var.https - ? { - for k, v in coalesce(local.ssl_certificates_config, {}) : - k => v if v.unmanaged_config != null - } - : {} - ) -} - -resource "google_compute_managed_ssl_certificate" "managed" { - for_each = local.ssl_certs_managed - project = var.project_id - name = "${var.name}-${each.key}" - managed { - domains = try(each.value.domains, null) - } -} - -resource "google_compute_ssl_certificate" "unmanaged" { - for_each = local.ssl_certs_unmanaged - project = var.project_id - name = "${var.name}-${each.key}" - certificate = try(each.value.unmanaged_config.tls_self_signed_cert, null) - private_key = try(each.value.unmanaged_config.tls_private_key, null) -} diff --git a/modules/net-glb/target-proxy.tf b/modules/net-glb/target-proxy.tf deleted file mode 100644 index ddfbe454..00000000 --- a/modules/net-glb/target-proxy.tf +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2022 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. - */ - -# tfdoc:file:description HTTP and HTTPS target proxies. - -locals { - # If no SSL certificates are defined, use the default one. - # Otherwise, look in the ssl_certificates_config map. - # Otherwise, use the SSL certificate id as is (already existing). - ssl_certificates = ( - try(var.target_proxy_https_config.ssl_certificates, null) == null - || length(coalesce(try(var.target_proxy_https_config.ssl_certificates, null), [])) == 0 - ? try( - [google_compute_managed_ssl_certificate.managed["default"].id], - [google_compute_ssl_certificate.unmanaged["default"].id], - null - ) - : [ - for cert in try(var.target_proxy_https_config.ssl_certificates, []) : - try( - google_compute_managed_ssl_certificate.managed[cert].id, - google_compute_ssl_certificate.unmanaged[cert].id, - cert - ) - ] - ) -} - -resource "google_compute_target_http_proxy" "http" { - count = var.https ? 0 : (var.region == null ? 1 : 0) - name = var.name - project = var.project_id - description = "Terraform managed." - url_map = google_compute_url_map.url_map.0.id -} - -resource "google_compute_target_https_proxy" "https" { - count = var.https ? (var.region == null ? 1 : 0) : 0 - name = var.name - project = var.project_id - description = "Terraform managed." - url_map = google_compute_url_map.url_map.0.id - ssl_certificates = local.ssl_certificates -} - -resource "google_compute_region_target_http_proxy" "http" { - count = var.https ? 0 : (var.region != null ? 1 : 0) - name = var.name - project = var.project_id - region = var.region - description = "Terraform managed." - url_map = google_compute_region_url_map.url_map.0.id -} - -resource "google_compute_region_target_https_proxy" "https" { - count = var.https ? (var.region != null ? 1 : 0) : 0 - name = var.name - project = var.project_id - region = var.region - description = "Terraform managed." - url_map = google_compute_region_url_map.url_map.0.id - ssl_certificates = local.ssl_certificates -} diff --git a/modules/net-glb/url-map.tf b/modules/net-glb/url-map.tf deleted file mode 100644 index a473c349..00000000 --- a/modules/net-glb/url-map.tf +++ /dev/null @@ -1,1184 +0,0 @@ -/** - * Copyright 2022 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. - */ - -# tfdoc:file:description URL maps. - -locals { - # Look for a backend services in the config whose id is - # the default_service given in the url-map. - # If not found, use the default_service id as given - # (assuming it's already existing). - # If the variable is null, will be set to null. - _default_service = try( - google_compute_backend_bucket.bucket[var.url_map_config.default_service].id, - google_compute_backend_service.group[var.url_map_config.default_service].id, - var.url_map_config.default_service, - null - ) - - # If no backend services are specified, - # the first backend service defined is associated - default_service = ( - try(local._default_service, null) == null - && try(var.url_map_config.default_route_action.weighted_backend_services, null) == null - && try(var.url_map_config.default_url_redirect, null) == null - ? try( - google_compute_backend_bucket.bucket[keys(google_compute_backend_bucket.bucket)[0]].id, - google_compute_backend_service.group[keys(google_compute_backend_service.group)[0]].id, - null - ) - : null - ) -} - -resource "google_compute_url_map" "url_map" { - count = var.region == null ? 1 : 0 - name = var.name - description = "Terraform managed." - project = var.project_id - default_service = local.default_service - - dynamic "header_action" { - for_each = ( - try(var.url_map_config.header_action, null) == null - ? [] - : [var.url_map_config.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] - : [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - - dynamic "host_rule" { - for_each = ( - try(var.url_map_config.host_rules, null) == null - ? [] - : var.url_map_config.host_rules - ) - content { - description = try(host_rule.value.description, null) - hosts = try(host_rule.value.hosts, null) - path_matcher = try(host_rule.value.path_matcher, null) - } - } - - dynamic "path_matcher" { - for_each = ( - try(var.url_map_config.path_matchers, null) == null - ? [] - : var.url_map_config.path_matchers - ) - content { - name = try(path_matcher.value.name, null) - description = try(path_matcher.value.description, null) - default_service = try( - google_compute_backend_bucket.bucket[var.url_map_config.default_service].id, - google_compute_backend_service.group[var.url_map_config.default_service].id, - path_matcher.value.default_service, - null - ) - - dynamic "header_action" { - for_each = ( - try(path_matcher.value.header_action, null) == null - ? [] - : [path_matcher.value.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] - : [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - - dynamic "path_rule" { - for_each = ( - try(path_matcher.value.path_rules, null) == null - ? [] - : path_matcher.value.path_rules - ) - content { - paths = try(path_rule.value.paths, null) - service = try( - google_compute_backend_bucket.bucket[path_rule.value.service].id, - google_compute_backend_service.group[path_rule.value.service].id, - path_rule.value.service, - null - ) - - dynamic "route_action" { - for_each = ( - try(path_rule.value.route_action, null) == null - ? [] - : [path_rule.value.route_action] - ) - content { - - dynamic "cors_policy" { - for_each = ( - try(route_action.value.cors_policy, null) == null - ? [] - : [route_action.value.cors_policy] - ) - content { - allow_credentials = try(cors_policy.value.allow_credentials, null) - allow_headers = try(cors_policy.value.allow_headers, null) - allow_methods = try(cors_policy.value.allow_methods, null) - allow_origin_regexes = try(cors_policy.value.allow_origin_regexes, null) - allow_origins = try(cors_policy.value.allow_origins, null) - disabled = try(cors_policy.value.disabled, null) - expose_headers = try(cors_policy.value.expose_headers, null) - max_age = try(cors_policy.value.max_age, null) - } - } - - dynamic "fault_injection_policy" { - for_each = ( - try(route_action.value.fault_injection_policy, null) == null - ? [] - : [route_action.value.fault_injection_policy] - ) - iterator = policy - content { - - dynamic "abort" { - for_each = ( - try(policy.value.abort, null) == null - ? [] - : [policy.value.abort] - ) - content { - http_status = try(abort.value.http_status, null) # Must be between 200 and 599 inclusive - percentage = try(abort.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - } - } - - dynamic "delay" { - for_each = ( - try(policy.value.delay, null) == null - ? [] - : [policy.value.delay] - ) - content { - percentage = try(delay.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - - dynamic "fixed_delay" { - for_each = ( - try(delay.value.fixed_delay, null) == null - ? [] - : [delay.value.fixed_delay] - ) - content { - nanos = try(fixed_delay.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(fixed_delay.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - } - } - - dynamic "request_mirror_policy" { - for_each = ( - try(route_action.value.request_mirror_policy, null) == null - ? [] - : [route_action.value.request_mirror_policy] - ) - iterator = policy - content { - backend_service = try( - google_compute_backend_bucket.bucket[policy.value.backend_service].id, - google_compute_backend_service.group[policy.value.backend_service].id, - policy.value.backend_service, - null - ) - } - } - - dynamic "retry_policy" { - for_each = ( - try(route_action.value.retry_policy, null) == null - ? [] - : [route_action.value.retry_policy] - ) - iterator = policy - content { - num_retries = try(policy.num_retries, null) # Must be > 0 - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#retry_conditions - retry_conditions = try(policy.retry_conditions, null) - - dynamic "per_try_timeout" { - for_each = ( - try(policy.value.per_try_timeout, null) == null - ? [] - : [policy.value.per_try_timeout] - ) - iterator = timeout - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - - dynamic "timeout" { - for_each = ( - try(route_action.value.timeout, null) == null - ? [] - : [route_action.value.timeout] - ) - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - - dynamic "url_rewrite" { - for_each = ( - try(route_action.value.url_rewrite, null) == null - ? [] - : [route_action.value.url_rewrite] - ) - content { - host_rewrite = try(url_rewrite.value.host_rewrite, null) # Must be between 1 and 255 characters - path_prefix_rewrite = try(url_rewrite.value.path_prefix_rewrite, null) # Must be between 1 and 1024 characters - } - } - - dynamic "weighted_backend_services" { - for_each = ( - try(route_action.value.weighted_backend_services, null) == null - ? [] - : route_action.value.weighted_backend_services - ) - iterator = weighted - content { - weight = try(weighted.value.weigth, null) - backend_service = try( - google_compute_backend_bucket.bucket[weighted.value.backend_service].id, - google_compute_backend_service.group[weighted.value.backend_service].id, - policy.value.backend_service, - null - ) - dynamic "header_action" { - for_each = ( - try(path_matcher.value.header_action, null) == null - ? [] - : [path_matcher.value.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] : - [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - } - } - } - } - - dynamic "url_redirect" { - for_each = ( - try(path_rule.value.url_redirect, null) == null - ? [] - : path_rule.value.url_redirect - ) - content { - host_redirect = try(url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters - https_redirect = try(url_redirect.value.https_redirect, null) - path_redirect = try(url_redirect.value.path_redirect, null) - prefix_redirect = try(url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters - # Valid valus at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code - redirect_response_code = try(url_redirect.value.redirect_response_code, null) - strip_query = try(url_redirect.value.strip_query, null) - } - } - } - } - - dynamic "route_rules" { - for_each = ( - try(path_matcher.value.route_rules, null) == null - ? [] - : path_matcher.value.route_rules - ) - content { - priority = try(route_rules.value.priority, null) - service = try( - google_compute_backend_bucket.bucket[route_rules.value.service].id, - google_compute_backend_service.group[route_rules.value.service].id, - route_rules.value.service, - null - ) - - dynamic "header_action" { - for_each = ( - try(path_matcher.value.header_action, null) == null - ? [] - : [path_matcher.value.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] - : [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - - dynamic "match_rules" { - for_each = ( - try(path_matcher.value.match_rules, null) == null - ? [] - : path_matcher.value.match_rules - ) - content { - full_path_match = try(match_rules.value.full_path_match, null) # Must be between 1 and 1024 characters - ignore_case = try(match_rules.value.ignore_case, null) - prefix_match = try(match_rules.value.prefix_match, null) - regex_match = try(match_rules.value.regex_match, null) - - dynamic "header_matches" { - for_each = ( - try(match_rules.value.header_matches, null) == null - ? [] - : [match_rules.value.header_matches] - ) - content { - exact_match = try(header_matches.value.exact_match, null) - header_name = try(header_matches.value.header_name, null) - invert_match = try(header_matches.value.invert_match, null) - prefix_match = try(header_matches.value.prefix_match, null) - present_match = try(header_matches.value.present_match, null) - regex_match = try(header_matches.value.regex_match, null) - suffix_match = try(header_matches.value, null) - - dynamic "range_match" { - for_each = try(header_matches.value.range_match, null) == null ? [] : [header_matches.value.range_match] - content { - range_end = try(range_match.value.range_end, null) - range_start = try(range_match.value.range_start, null) - } - } - } - } - - dynamic "metadata_filters" { - for_each = ( - try(match_rules.value.metadata_filters, null) == null - ? [] - : [match_rules.value.metadata_filters] - ) - content { - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#filter_match_criteria - filter_match_criteria = try(metadata_filters.value.filter_match_criteria, null) - - dynamic "filter_labels" { - for_each = ( - try(metadata_filters.value.filter_labels, null) == null - ? [] - : metadata_filters.value.filter_labels - ) - content { - name = try(filter_labels.value.name, null) # Must be between 1 and 1024 characters - value = try(filter_labels.value.value, null) # Must be between 1 and 1024 characters - } - } - } - } - - dynamic "query_parameter_matches" { - for_each = ( - try(match_rules.value.query_parameter_matches, null) == null - ? [] - : [match_rules.value.query_parameter_matches] - ) - iterator = query - content { - exact_match = try(query.value.exact_match, null) - name = try(query.value.name, null) - present_match = try(query.value.present_match, null) - regex_match = try(query.value.regex_match, null) - } - } - } - } - - dynamic "route_action" { - for_each = ( - try(route_rules.value.route_action, null) == null - ? [] - : [route_rules.value.route_action] - ) - content { - - dynamic "cors_policy" { - for_each = ( - try(route_action.value.cors_policy, null) == null - ? [] - : [route_action.value.cors_policy] - ) - content { - allow_credentials = try(cors_policy.value.allow_credentials, null) - allow_headers = try(cors_policy.value.allow_headers, null) - allow_methods = try(cors_policy.value.allow_methods, null) - allow_origin_regexes = try(cors_policy.value.allow_origin_regexes, null) - allow_origins = try(cors_policy.value.allow_origins, null) - disabled = try(cors_policy.value.disabled, null) - expose_headers = try(cors_policy.value.expose_headers, null) - max_age = try(cors_policy.value.max_age, null) - } - } - - dynamic "fault_injection_policy" { - for_each = ( - try(route_action.value.fault_injection_policy, null) == null - ? [] - : [route_action.value.fault_injection_policy] - ) - iterator = policy - content { - - dynamic "abort" { - for_each = ( - try(policy.value.abort, null) == null - ? [] - : [policy.value.abort] - ) - content { - http_status = try(abort.value.http_status, null) # Must be between 200 and 599 inclusive - percentage = try(abort.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - } - } - - dynamic "delay" { - for_each = ( - try(policy.value.delay, null) == null - ? [] - : [policy.value.delay] - ) - content { - percentage = try(delay.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - - dynamic "fixed_delay" { - for_each = ( - try(delay.value.fixed_delay, null) == null - ? [] - : [delay.value.fixed_delay] - ) - content { - nanos = try(fixed_delay.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(fixed_delay.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - } - } - - dynamic "request_mirror_policy" { - for_each = ( - try(route_action.value.request_mirror_policy, null) == null - ? [] - : [route_action.value.request_mirror_policy] - ) - iterator = policy - content { - backend_service = try( - google_compute_backend_bucket.bucket[policy.value.backend_service].id, - google_compute_backend_service.group[policy.value.backend_service].id, - policy.value.backend_service, - null - ) - } - } - - dynamic "retry_policy" { - for_each = ( - try(route_action.value.retry_policy, null) == null - ? [] - : [route_action.value.retry_policy] - ) - iterator = policy - content { - num_retries = try(policy.num_retries, null) # Must be > 0 - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#retry_conditions - retry_conditions = try(policy.retry_conditions, null) - - dynamic "per_try_timeout" { - for_each = ( - try(policy.value.per_try_timeout, null) == null - ? [] - : [policy.value.per_try_timeout] - ) - iterator = timeout - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - - dynamic "timeout" { - for_each = ( - try(route_action.value.timeout, null) == null - ? [] - : [route_action.value.timeout] - ) - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - - dynamic "url_rewrite" { - for_each = ( - try(route_action.value.url_rewrite, null) == null - ? [] - : [route_action.value.url_rewrite] - ) - content { - host_rewrite = try(url_rewrite.value.host_rewrite, null) # Must be between 1 and 255 characters - path_prefix_rewrite = try(url_rewrite.value.path_prefix_rewrite, null) # Must be between 1 and 1024 characters - } - } - - dynamic "weighted_backend_services" { - for_each = ( - try(route_action.value.weighted_backend_services, null) == null - ? [] - : [route_action.value.url_rewrite] - ) - iterator = weighted - content { - weight = try(weighted.value.weigth, null) - backend_service = try( - google_compute_backend_bucket.bucket[weighted.value.backend_service].id, - google_compute_backend_service.group[weighted.value.backend_service].id, - weighted.value.backend_service, - null - ) - - dynamic "header_action" { - for_each = ( - try(path_matcher.value.header_action, null) == null - ? [] : - [path_matcher.value.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] - : [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - } - } - } - } - - dynamic "url_redirect" { - for_each = ( - try(route_rules.value.url_redirect, null) == null - ? [] - : route_rules.value.url_redirect - ) - content { - host_redirect = try(url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters - https_redirect = try(url_redirect.value.https_redirect, null) - path_redirect = try(url_redirect.value.path_redirect, null) - prefix_redirect = try(url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters - # Valid valus at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code - redirect_response_code = try(url_redirect.value.redirect_response_code, null) - strip_query = try(url_redirect.value.strip_query, null) - } - } - } - } - - dynamic "default_url_redirect" { - for_each = ( - try(path_matcher.value.default_url_redirect, null) == null - ? [] - : path_matcher.value.default_url_redirect - ) - content { - host_redirect = try(default_url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters - https_redirect = try(default_url_redirect.value.https_redirect, null) - path_redirect = try(default_url_redirect.value.path_redirect, null) # Must be between 1 and 1024 characters - prefix_redirect = try(default_url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code - redirect_response_code = try(default_url_redirect.value.redirect_response_code, null) - strip_query = try(default_url_redirect.value.strip_query, null) - } - } - - dynamic "default_route_action" { - for_each = ( - try(path_matcher.value.default_route_action, null) == null - ? [] - : path_matcher.value.default_route_action - ) - content { - dynamic "cors_policy" { - for_each = ( - try(default_route_action.value.cors_policy, null) == null - ? [] - : [default_route_action.value.cors_policy] - ) - content { - allow_credentials = try(cors_policy.value.allow_credentials, null) - allow_headers = try(cors_policy.value.allow_headers, null) - allow_methods = try(cors_policy.value.allow_methods, null) - allow_origin_regexes = try(cors_policy.value.allow_origin_regexes, null) - allow_origins = try(cors_policy.value.allow_origins, null) - disabled = try(cors_policy.value.disabled, null) - expose_headers = try(cors_policy.value.expose_headers, null) - max_age = try(cors_policy.value.max_age, null) - } - } - - dynamic "fault_injection_policy" { - for_each = ( - try(default_route_action.value.fault_injection_policy, null) == null - ? [] - : [default_route_action.value.fault_injection_policy] - ) - iterator = policy - content { - - dynamic "abort" { - for_each = ( - try(policy.value.abort, null) == null - ? [] - : [policy.value.abort] - ) - content { - http_status = try(abort.value.http_status, null) # Must be between 200 and 599 inclusive - percentage = try(abort.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - } - } - - dynamic "delay" { - for_each = ( - try(policy.value.delay, null) == null - ? [] - : [policy.value.delay] - ) - content { - percentage = try(delay.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - - dynamic "fixed_delay" { - for_each = ( - try(delay.value.fixed_delay, null) == null - ? [] - : [delay.value.fixed_delay] - ) - content { - nanos = try(fixed_delay.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(fixed_delay.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - } - } - - dynamic "request_mirror_policy" { - for_each = ( - try(default_route_action.value.request_mirror_policy, null) == null - ? [] - : [default_route_action.value.request_mirror_policy] - ) - iterator = policy - content { - backend_service = try( - google_compute_backend_bucket.bucket[policy.value.backend_service].id, - google_compute_backend_service.group[policy.value.backend_service].id, - policy.value.backend_service, - null - ) - } - } - - dynamic "retry_policy" { - for_each = ( - try(default_route_action.value.retry_policy, null) == null - ? [] - : [default_route_action.value.retry_policy] - ) - iterator = policy - content { - num_retries = try(policy.num_retries, null) # Must be > 0 - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#retry_conditions - retry_conditions = try(policy.retry_conditions, null) - - dynamic "per_try_timeout" { - for_each = ( - try(policy.value.per_try_timeout, null) == null - ? [] - : [policy.value.per_try_timeout] - ) - iterator = timeout - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - - dynamic "timeout" { - for_each = ( - try(default_route_action.value.timeout, null) == null - ? [] - : [default_route_action.value.timeout] - ) - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - - dynamic "url_rewrite" { - for_each = ( - try(default_route_action.value.url_rewrite, null) == null - ? [] - : [default_route_action.value.url_rewrite] - ) - content { - host_rewrite = try(url_rewrite.value.host_rewrite, null) # Must be between 1 and 255 characters - path_prefix_rewrite = try(url_rewrite.value.path_prefix_rewrite, null) # Must be between 1 and 1024 characters - } - } - - dynamic "weighted_backend_services" { - for_each = ( - try(default_route_action.value.weighted_backend_services, null) == null - ? [] - : default_route_action.value.weighted_backend_services - ) - iterator = weighted - content { - weight = try(weighted.value.weigth, null) - backend_service = try( - google_compute_backend_bucket.bucket[weighted.value.backend_service].id, - google_compute_backend_service.group[weighted.value.backend_service].id, - weighted.value.backend_service, - null - ) - - dynamic "header_action" { - for_each = ( - try(path_matcher.value.header_action, null) == null - ? [] - : [path_matcher.value.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] - : [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - } - } - } - } - } - } - - # Up to 100 tests per url_map - dynamic "test" { - for_each = ( - try(var.url_map_config.tests, null) == null - ? [] - : var.url_map_config.tests - ) - content { - description = try(test.value.description, null) - host = try(test.value.host, null) - path = try(test.value.path, null) - service = try( - google_compute_backend_bucket.bucket[test.value.service].id, - google_compute_backend_service.group[test.value.service].id, - test.value.service, - null - ) - } - } - - dynamic "default_url_redirect" { - for_each = ( - try(var.url_map_config.default_url_redirect, null) == null - ? [] - : [var.url_map_config.default_url_redirect] - ) - content { - host_redirect = try(default_url_redirect.value.host_redirect, null) # Must be between 1 and 255 characters - https_redirect = try(default_url_redirect.value.https_redirect, null) - path_redirect = try(default_url_redirect.value.path_redirect, null) # Must be between 1 and 1024 characters - prefix_redirect = try(default_url_redirect.value.prefix_redirect, null) # Must be between 1 and 1024 characters - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#redirect_response_code - redirect_response_code = try(default_url_redirect.value.redirect_response_code, null) - strip_query = try(default_url_redirect.value.strip_query, null) - } - } - - dynamic "default_route_action" { - for_each = ( - try(var.url_map_config.default_route_action, null) == null - ? [] - : [var.url_map_config.default_route_action] - ) - content { - dynamic "cors_policy" { - for_each = ( - try(default_route_action.value.cors_policy, null) == null - ? [] - : [default_route_action.value.cors_policy] - ) - content { - allow_credentials = try(cors_policy.value.allow_credentials, null) - allow_headers = try(cors_policy.value.allow_headers, null) - allow_methods = try(cors_policy.value.allow_methods, null) - allow_origin_regexes = try(cors_policy.value.allow_origin_regexes, null) - allow_origins = try(cors_policy.value.allow_origins, null) - disabled = try(cors_policy.value.disabled, null) - expose_headers = try(cors_policy.value.expose_headers, null) - max_age = try(cors_policy.value.max_age, null) - } - } - - dynamic "fault_injection_policy" { - for_each = ( - try(default_route_action.value.fault_injection_policy, null) == null - ? [] - : [default_route_action.value.fault_injection_policy] - ) - iterator = policy - content { - - dynamic "abort" { - for_each = ( - try(policy.value.abort, null) == null - ? [] - : [policy.value.abort] - ) - content { - http_status = try(abort.value.http_status, null) # Must be between 200 and 599 inclusive - percentage = try(abort.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - } - } - - dynamic "delay" { - for_each = ( - try(policy.value.delay, null) == null - ? [] - : [policy.value.delay] - ) - content { - percentage = try(delay.value.percentage, null) # Must be between 0.0 and 100.0 inclusive - - dynamic "fixed_delay" { - for_each = try(delay.value.fixed_delay, null) == null ? [] : [delay.value.fixed_delay] - content { - nanos = try(fixed_delay.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(fixed_delay.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - } - } - - dynamic "request_mirror_policy" { - for_each = ( - try(default_route_action.value.request_mirror_policy, null) == null - ? [] - : [default_route_action.value.request_mirror_policy] - ) - iterator = policy - content { - backend_service = try( - google_compute_backend_bucket.bucket[policy.value.backend_service].id, - google_compute_backend_service.group[policy.value.backend_service].id, - policy.value.backend_service, - null - ) - } - } - - dynamic "retry_policy" { - for_each = ( - try(default_route_action.value.retry_policy, null) == null - ? [] - : [default_route_action.value.retry_policy] - ) - iterator = policy - content { - num_retries = try(policy.num_retries, null) # Must be > 0 - # Valid values at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_url_map#retry_conditions - retry_conditions = try(policy.retry_conditions, null) - - dynamic "per_try_timeout" { - for_each = ( - try(policy.value.per_try_timeout, null) == null - ? [] - : [policy.value.per_try_timeout] - ) - iterator = timeout - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - } - } - - dynamic "timeout" { - for_each = ( - try(default_route_action.value.timeout, null) == null - ? [] - : [default_route_action.value.timeout] - ) - content { - nanos = try(timeout.value.nanos, null) # Must be from 0 to 999,999,999 inclusive - seconds = try(timeout.value.seconds, null) # Must be from 0 to 315,576,000,000 inclusive - } - } - - dynamic "url_rewrite" { - for_each = ( - try(default_route_action.value.url_rewrite, null) == null - ? [] - : [default_route_action.value.url_rewrite] - ) - content { - host_rewrite = try(url_rewrite.value.host_rewrite, null) # Must be between 1 and 255 characters - path_prefix_rewrite = try(url_rewrite.value.path_prefix_rewrite, null) # Must be between 1 and 1024 characters - } - } - - dynamic "weighted_backend_services" { - for_each = ( - try(default_route_action.value.weighted_backend_services, null) == null - ? [] - : default_route_action.value.weighted_backend_services - ) - iterator = weighted - content { - weight = try(weighted.value.weigth, null) - backend_service = try( - google_compute_backend_bucket.bucket[weighted.value.backend_service].id, - google_compute_backend_service.group[weighted.value.backend_service].id, - weighted.value.backend_service, - null - ) - - dynamic "header_action" { - for_each = ( - try(weighted.value.header_action, null) == null - ? [] - : [weighted.value.header_action] - ) - content { - request_headers_to_remove = try(header_action.value.request_headers_to_remove, null) - response_headers_to_remove = try(header_action.value.response_headers_to_remove, null) - - dynamic "request_headers_to_add" { - for_each = ( - try(header_action.value.request_headers_to_add, null) == null - ? [] - : [header_action.value.request_headers_to_add] - ) - content { - header_name = try(request_headers_to_add.value.header_name, null) - header_value = try(request_headers_to_add.value.header_value, null) - replace = try(request_headers_to_add.value.replace, null) - } - } - - dynamic "response_headers_to_add" { - for_each = ( - try(header_action.response_headers_to_add, null) == null - ? [] - : [header_action.response_headers_to_add] - ) - content { - header_name = try(response_headers_to_add.value.header_name, null) - header_value = try(response_headers_to_add.value.header_value, null) - replace = try(response_headers_to_add.value.replace, null) - } - } - } - } - } - } - } - } -} - diff --git a/modules/net-glb/urlmap.tf b/modules/net-glb/urlmap.tf new file mode 100644 index 00000000..a7f01d55 --- /dev/null +++ b/modules/net-glb/urlmap.tf @@ -0,0 +1,952 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description URL map resources. + +locals { + backend_ids = merge( + { for k, v in google_compute_backend_service.default : k => v.id }, + { for k, v in google_compute_backend_bucket.default : k => v.id } + ) +} + +resource "google_compute_url_map" "default" { + provider = google-beta + project = var.project_id + name = var.name + description = var.description + default_service = ( + var.urlmap_config.default_service == null ? null : lookup( + local.backend_ids, + var.urlmap_config.default_service, + var.urlmap_config.default_service + ) + ) + + dynamic "default_route_action" { + for_each = ( + var.urlmap_config.default_route_action == null + ? [] + : [var.urlmap_config.default_route_action] + ) + iterator = route_action + content { + dynamic "cors_policy" { + for_each = ( + route_action.value.cors_policy == null + ? [] + : [route_action.value.cors_policy] + ) + content { + allow_credentials = cors_policy.value.allow_credentials + allow_headers = cors_policy.value.allow_headers + allow_methods = cors_policy.value.allow_methods + allow_origin_regexes = cors_policy.value.allow_origin_regexes + allow_origins = cors_policy.value.allow_origins + disabled = cors_policy.value.disabled + expose_headers = cors_policy.value.expose_headers + max_age = cors_policy.value.max_age + } + } + dynamic "fault_injection_policy" { + for_each = ( + route_action.value.fault_injection_policy == null + ? [] + : [route_action.value.fault_injection_policy] + ) + content { + dynamic "abort" { + for_each = ( + fault_injection_policy.value.abort == null + ? [] + : [fault_injection_policy.value.abort] + ) + content { + http_status = abort.value.status + percentage = abort.value.percentage + } + } + dynamic "delay" { + for_each = ( + fault_injection_policy.value.delay == null + ? [] + : [fault_injection_policy.value.delay] + ) + content { + percentage = delay.value.percentage + fixed_delay { + nanos = delay.value.fixed.nanos + seconds = delay.value.fixed.seconds + } + } + } + } + } + dynamic "request_mirror_policy" { + for_each = ( + route_action.value.request_mirror_backend == null + ? [] + : [""] + ) + content { + backend_service = lookup( + local.backend_ids, + route_action.value.request_mirror_backend, + route_action.value.request_mirror_backend + ) + } + } + dynamic "retry_policy" { + for_each = ( + route_action.value.retry_policy == null + ? [] + : [route_action.value.retry_policy] + ) + content { + num_retries = retry_policy.value.num_retries + retry_conditions = retry_policy.value.retry_conditions + dynamic "per_try_timeout" { + for_each = ( + retry_policy.value.per_try_timeout == null + ? [] + : [retry_policy.value.per_try_timeout] + ) + content { + nanos = per_try_timeout.value.nanos + seconds = per_try_timeout.value.seconds + } + } + } + } + dynamic "timeout" { + for_each = ( + route_action.value.timeout == null + ? [] + : [route_action.value.timeout] + ) + content { + nanos = timeout.value.nanos + seconds = timeout.value.seconds + } + } + dynamic "url_rewrite" { + for_each = ( + route_action.value.url_rewrite == null + ? [] + : [route_action.value.url_rewrite] + ) + content { + host_rewrite = url_rewrite.value.host + path_prefix_rewrite = url_rewrite.value.path_prefix + } + } + dynamic "weighted_backend_services" { + for_each = coalesce( + route_action.value.weighted_backend_services, {} + ) + iterator = service + content { + backend_service = lookup( + local.backend_ids, service.key, service.key + ) + weight = service.value.weight + dynamic "header_action" { + for_each = ( + service.value.header_action == null + ? [] + : [service.value.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + } + } + } + } + + dynamic "default_url_redirect" { + for_each = ( + var.urlmap_config.default_url_redirect == null + ? [] + : [var.urlmap_config.default_url_redirect] + ) + iterator = r + content { + host_redirect = r.value.host + https_redirect = r.value.https + path_redirect = r.value.path + prefix_redirect = r.value.prefix + redirect_response_code = r.value.response_code + strip_query = r.value.strip_query + } + } + + dynamic "header_action" { + for_each = ( + var.urlmap_config.header_action == null + ? [] + : [var.urlmap_config.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + + dynamic "host_rule" { + for_each = coalesce(var.urlmap_config.host_rules, []) + iterator = r + content { + hosts = r.value.hosts + path_matcher = r.value.path_matcher + description = r.value.description + } + } + + dynamic "path_matcher" { + for_each = coalesce(var.urlmap_config.path_matchers, {}) + iterator = m + content { + default_service = m.value.default_service == null ? null : lookup( + local.backend_ids, m.value.default_service, m.value.default_service + ) + description = m.value.description + name = m.key + dynamic "default_route_action" { + for_each = ( + m.value.default_route_action == null + ? [] + : [m.value.default_route_action] + ) + iterator = route_action + content { + dynamic "cors_policy" { + for_each = ( + route_action.value.cors_policy == null + ? [] + : [route_action.value.cors_policy] + ) + content { + allow_credentials = cors_policy.value.allow_credentials + allow_headers = cors_policy.value.allow_headers + allow_methods = cors_policy.value.allow_methods + allow_origin_regexes = cors_policy.value.allow_origin_regexes + allow_origins = cors_policy.value.allow_origins + disabled = cors_policy.value.disabled + expose_headers = cors_policy.value.expose_headers + max_age = cors_policy.value.max_age + } + } + dynamic "fault_injection_policy" { + for_each = ( + route_action.value.fault_injection_policy == null + ? [] + : [route_action.value.fault_injection_policy] + ) + content { + dynamic "abort" { + for_each = ( + fault_injection_policy.value.abort == null + ? [] + : [fault_injection_policy.value.abort] + ) + content { + http_status = abort.value.status + percentage = abort.value.percentage + } + } + dynamic "delay" { + for_each = ( + fault_injection_policy.value.delay == null + ? [] + : [fault_injection_policy.value.delay] + ) + content { + percentage = delay.value.percentage + fixed_delay { + nanos = delay.value.fixed.nanos + seconds = delay.value.fixed.seconds + } + } + } + } + } + dynamic "request_mirror_policy" { + for_each = ( + route_action.value.request_mirror_backend == null + ? [] + : [""] + ) + content { + backend_service = lookup( + local.backend_ids, + route_action.value.request_mirror_backend, + route_action.value.request_mirror_backend + ) + } + } + dynamic "retry_policy" { + for_each = ( + route_action.value.retry_policy == null + ? [] + : [route_action.value.retry_policy] + ) + content { + num_retries = retry_policy.value.num_retries + retry_conditions = retry_policy.value.retry_conditions + dynamic "per_try_timeout" { + for_each = ( + retry_policy.value.per_try_timeout == null + ? [] + : [retry_policy.value.per_try_timeout] + ) + content { + nanos = per_try_timeout.value.nanos + seconds = per_try_timeout.value.seconds + } + } + } + } + dynamic "timeout" { + for_each = ( + route_action.value.timeout == null + ? [] + : [route_action.value.timeout] + ) + content { + nanos = timeout.value.nanos + seconds = timeout.value.seconds + } + } + dynamic "url_rewrite" { + for_each = ( + route_action.value.url_rewrite == null + ? [] + : [route_action.value.url_rewrite] + ) + content { + host_rewrite = url_rewrite.value.host + path_prefix_rewrite = url_rewrite.value.path_prefix + } + } + dynamic "weighted_backend_services" { + for_each = coalesce( + route_action.value.weighted_backend_services, {} + ) + iterator = service + content { + backend_service = lookup( + local.backend_ids, service.key, service.key + ) + weight = service.value.weight + dynamic "header_action" { + for_each = ( + service.value.header_action == null + ? [] + : [service.value.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + } + } + } + } + dynamic "default_url_redirect" { + for_each = ( + m.value.default_url_redirect == null + ? [] + : [m.value.default_url_redirect] + ) + content { + host_redirect = default_url_redirect.value.host + https_redirect = default_url_redirect.value.https + path_redirect = default_url_redirect.value.path + prefix_redirect = default_url_redirect.value.prefix + redirect_response_code = default_url_redirect.value.response_code + strip_query = default_url_redirect.value.strip_query + } + } + dynamic "header_action" { + for_each = ( + m.value.header_action == null + ? [] + : [m.value.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + dynamic "path_rule" { + for_each = toset(coalesce(m.value.path_rules, [])) + content { + paths = path_rule.value.paths + service = path_rule.value.service == null ? null : lookup( + local.backend_ids, + path_rule.value.service, + path_rule.value.service + ) + dynamic "route_action" { + for_each = ( + path_rule.value.route_action == null + ? [] + : [path_rule.value.route_action] + ) + content { + dynamic "cors_policy" { + for_each = ( + route_action.value.cors_policy == null + ? [] + : [route_action.value.cors_policy] + ) + content { + allow_credentials = cors_policy.value.allow_credentials + allow_headers = cors_policy.value.allow_headers + allow_methods = cors_policy.value.allow_methods + allow_origin_regexes = cors_policy.value.allow_origin_regexes + allow_origins = cors_policy.value.allow_origins + disabled = cors_policy.value.disabled + expose_headers = cors_policy.value.expose_headers + max_age = cors_policy.value.max_age + } + } + dynamic "fault_injection_policy" { + for_each = ( + route_action.value.fault_injection_policy == null + ? [] + : [route_action.value.fault_injection_policy] + ) + content { + dynamic "abort" { + for_each = ( + fault_injection_policy.value.abort == null + ? [] + : [fault_injection_policy.value.abort] + ) + content { + http_status = abort.value.status + percentage = abort.value.percentage + } + } + dynamic "delay" { + for_each = ( + fault_injection_policy.value.delay == null + ? [] + : [fault_injection_policy.value.delay] + ) + content { + percentage = delay.value.percentage + fixed_delay { + nanos = delay.value.fixed.nanos + seconds = delay.value.fixed.seconds + } + } + } + } + } + dynamic "request_mirror_policy" { + for_each = ( + route_action.value.request_mirror_backend == null + ? [] + : [""] + ) + content { + backend_service = lookup( + local.backend_ids, + route_action.value.request_mirror_backend, + route_action.value.request_mirror_backend + ) + } + } + dynamic "retry_policy" { + for_each = ( + route_action.value.retry_policy == null + ? [] + : [route_action.value.retry_policy] + ) + content { + num_retries = retry_policy.value.num_retries + retry_conditions = retry_policy.value.retry_conditions + dynamic "per_try_timeout" { + for_each = ( + retry_policy.value.per_try_timeout == null + ? [] + : [retry_policy.value.per_try_timeout] + ) + content { + nanos = per_try_timeout.value.nanos + seconds = per_try_timeout.value.seconds + } + } + } + } + dynamic "timeout" { + for_each = ( + route_action.value.timeout == null + ? [] + : [route_action.value.timeout] + ) + content { + nanos = timeout.value.nanos + seconds = timeout.value.seconds + } + } + dynamic "url_rewrite" { + for_each = ( + route_action.value.url_rewrite == null + ? [] + : [route_action.value.url_rewrite] + ) + content { + host_rewrite = url_rewrite.value.host + path_prefix_rewrite = url_rewrite.value.path_prefix + } + } + dynamic "weighted_backend_services" { + for_each = coalesce( + route_action.value.weighted_backend_services, {} + ) + iterator = service + content { + backend_service = lookup( + local.backend_ids, service.key, service.key + ) + weight = service.value.weight + dynamic "header_action" { + for_each = ( + service.value.header_action == null + ? [] + : [service.value.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + } + } + } + } + dynamic "url_redirect" { + for_each = ( + path_rule.value.url_redirect == null + ? [] + : [path_rule.value.url_redirect] + ) + content { + host_redirect = url_redirect.value.host + https_redirect = url_redirect.value.https + path_redirect = url_redirect.value.path + prefix_redirect = url_redirect.value.prefix + redirect_response_code = url_redirect.value.response_code + strip_query = url_redirect.value.strip_query + } + } + } + } + dynamic "route_rules" { + for_each = toset(coalesce(m.value.route_rules, [])) + content { + priority = route_rules.value.priority + service = route_rules.value.service == null ? null : lookup( + local.backend_ids, + route_rules.value.service, + route_rules.value.service + ) + dynamic "header_action" { + for_each = ( + route_rules.value.header_action == null + ? [] + : [route_rules.value.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + dynamic "match_rules" { + for_each = toset(coalesce(route_rules.value.match_rules, [])) + content { + ignore_case = match_rules.value.ignore_case + full_path_match = ( + try(match_rules.value.path.type, null) == "full" + ? match_rules.value.path.value + : null + ) + prefix_match = ( + try(match_rules.value.path.type, null) == "prefix" + ? match_rules.value.path.value + : null + ) + regex_match = ( + try(match_rules.value.path.type, null) == "regex" + ? match_rules.value.path.value + : null + ) + dynamic "header_matches" { + for_each = toset(coalesce(match_rules.value.headers, [])) + iterator = h + content { + header_name = h.value.name + exact_match = h.value.type == "exact" ? h.value.value : null + invert_match = h.value.invert_match + prefix_match = h.value.type == "prefix" ? h.value.value : null + present_match = h.value.type == "present" ? h.value.value : null + regex_match = h.value.type == "regex" ? h.value.value : null + suffix_match = h.value.type == "suffix" ? h.value.value : null + dynamic "range_match" { + for_each = ( + h.value.type != "range" || h.value.range_value == null + ? [] + : [""] + ) + content { + range_end = h.value.range_value.end + range_start = h.value.range_value.start + } + } + } + } + dynamic "metadata_filters" { + for_each = toset(coalesce(match_rules.value.metadata_filters, [])) + iterator = m + content { + filter_match_criteria = ( + m.value.match_all ? "MATCH_ALL" : "MATCH_ANY" + ) + dynamic "filter_labels" { + for_each = m.value.labels + content { + name = filter_labels.key + value = filter_labels.value + } + } + } + } + dynamic "query_parameter_matches" { + for_each = toset(coalesce(match_rules.value.query_params, [])) + iterator = q + content { + name = q.value.name + exact_match = ( + q.value.type == "exact" ? q.value.value : null + ) + present_match = ( + q.value.type == "present" ? q.value.value : null + ) + regex_match = ( + q.value.type == "regex" ? q.value.value : null + ) + } + } + } + } + dynamic "route_action" { + for_each = ( + route_rules.value.route_action == null + ? [] + : [route_rules.value.route_action] + ) + content { + dynamic "cors_policy" { + for_each = ( + route_action.value.cors_policy == null + ? [] + : [route_action.value.cors_policy] + ) + content { + allow_credentials = cors_policy.value.allow_credentials + allow_headers = cors_policy.value.allow_headers + allow_methods = cors_policy.value.allow_methods + allow_origin_regexes = cors_policy.value.allow_origin_regexes + allow_origins = cors_policy.value.allow_origins + disabled = cors_policy.value.disabled + expose_headers = cors_policy.value.expose_headers + max_age = cors_policy.value.max_age + } + } + dynamic "fault_injection_policy" { + for_each = ( + route_action.value.fault_injection_policy == null + ? [] + : [route_action.value.fault_injection_policy] + ) + content { + dynamic "abort" { + for_each = ( + fault_injection_policy.value.abort == null + ? [] + : [fault_injection_policy.value.abort] + ) + content { + http_status = abort.value.status + percentage = abort.value.percentage + } + } + dynamic "delay" { + for_each = ( + fault_injection_policy.value.delay == null + ? [] + : [fault_injection_policy.value.delay] + ) + content { + percentage = delay.value.percentage + fixed_delay { + nanos = delay.value.fixed.nanos + seconds = delay.value.fixed.seconds + } + } + } + } + } + dynamic "request_mirror_policy" { + for_each = ( + route_action.value.request_mirror_backend == null + ? [] + : [""] + ) + content { + backend_service = lookup( + local.backend_ids, + route_action.value.request_mirror_backend, + route_action.value.request_mirror_backend + ) + } + } + dynamic "retry_policy" { + for_each = ( + route_action.value.retry_policy == null + ? [] + : [route_action.value.retry_policy] + ) + content { + num_retries = retry_policy.value.num_retries + retry_conditions = retry_policy.value.retry_conditions + dynamic "per_try_timeout" { + for_each = ( + retry_policy.value.per_try_timeout == null + ? [] + : [retry_policy.value.per_try_timeout] + ) + content { + nanos = per_try_timeout.value.nanos + seconds = per_try_timeout.value.seconds + } + } + } + } + dynamic "timeout" { + for_each = ( + route_action.value.timeout == null + ? [] + : [route_action.value.timeout] + ) + content { + nanos = timeout.value.nanos + seconds = timeout.value.seconds + } + } + dynamic "url_rewrite" { + for_each = ( + route_action.value.url_rewrite == null + ? [] + : [route_action.value.url_rewrite] + ) + content { + host_rewrite = url_rewrite.value.host + path_prefix_rewrite = url_rewrite.value.path_prefix + } + } + dynamic "weighted_backend_services" { + for_each = coalesce( + route_action.value.weighted_backend_services, {} + ) + iterator = service + content { + backend_service = lookup( + local.backend_ids, service.key, service.key + ) + weight = service.value.weight + dynamic "header_action" { + for_each = ( + service.value.header_action == null + ? [] + : [service.value.header_action] + ) + iterator = h + content { + request_headers_to_remove = h.value.request_remove + response_headers_to_remove = h.value.response_remove + dynamic "request_headers_to_add" { + for_each = coalesce(h.value.request_add, {}) + content { + header_name = request_headers_to_add.key + header_value = request_headers_to_add.value.value + replace = request_headers_to_add.value.replace + } + } + dynamic "response_headers_to_add" { + for_each = coalesce(h.value.response_add, {}) + content { + header_name = response_headers_to_add.key + header_value = response_headers_to_add.value.value + replace = response_headers_to_add.value.replace + } + } + } + } + } + } + } + } + dynamic "url_redirect" { + for_each = ( + route_rules.value.default_url_redirect == null + ? [] + : [route_rules.value.default_url_redirect] + ) + content { + host_redirect = url_redirect.value.host + https_redirect = url_redirect.value.https + path_redirect = url_redirect.value.path + prefix_redirect = url_redirect.value.prefix + redirect_response_code = url_redirect.value.response_code + strip_query = url_redirect.value.strip_query + } + } + } + } + } + } + + dynamic "test" { + for_each = toset(coalesce(var.urlmap_config.test, [])) + content { + host = test.value.host + path = test.value.path + service = test.value.service + description = test.value.description + } + } + +} diff --git a/modules/net-glb/variables-backend-service.tf b/modules/net-glb/variables-backend-service.tf new file mode 100644 index 00000000..1d70d841 --- /dev/null +++ b/modules/net-glb/variables-backend-service.tf @@ -0,0 +1,150 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description Backend services variables. + +variable "backend_service_configs" { + description = "Backend service level configuration." + type = map(object({ + affinity_cookie_ttl_sec = optional(number) + compression_mode = optional(string) + connection_draining_timeout_sec = optional(number) + custom_request_headers = optional(list(string)) + custom_response_headers = optional(list(string)) + enable_cdn = optional(bool) + health_checks = optional(list(string), ["default"]) + log_sample_rate = optional(number) + port_name = optional(string) + project_id = optional(string) + protocol = optional(string) + security_policy = optional(string) + session_affinity = optional(string) + timeout_sec = optional(number) + backends = list(object({ + # group renamed to backend + backend = string + balancing_mode = optional(string, "UTILIZATION") + capacity_scaler = optional(number, 1) + description = optional(string, "Terraform managed.") + failover = optional(bool, false) + max_connections = optional(object({ + per_endpoint = optional(number) + per_group = optional(number) + per_instance = optional(number) + })) + max_rate = optional(object({ + per_endpoint = optional(number) + per_group = optional(number) + per_instance = optional(number) + })) + max_utilization = optional(number) + })) + cdn_policy = optional(object({ + cache_mode = optional(string) + client_ttl = optional(number) + default_ttl = optional(number) + max_ttl = optional(number) + negative_caching = optional(bool) + serve_while_stale = optional(bool) + signed_url_cache_max_age_sec = optional(number) + cache_key_policy = optional(object({ + include_host = optional(bool) + include_named_cookies = optional(list(string)) + include_protocol = optional(bool) + include_query_string = optional(bool) + query_string_blacklist = optional(list(string)) + query_string_whitelist = optional(list(string)) + })) + negative_caching_policy = optional(object({ + code = optional(number) + ttl = optional(number) + })) + })) + circuit_breakers = optional(object({ + max_connections = optional(number) + max_pending_requests = optional(number) + max_requests = optional(number) + max_requests_per_connection = optional(number) + max_retries = optional(number) + connect_timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + consistent_hash = optional(object({ + http_header_name = optional(string) + minimum_ring_size = optional(number) + http_cookie = optional(object({ + name = optional(string) + path = optional(string) + ttl = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + })) + iap_config = optional(object({ + oauth2_client_id = string + oauth2_client_secret = string + oauth2_client_secret_sha256 = optional(string) + })) + outlier_detection = optional(object({ + consecutive_errors = optional(number) + consecutive_gateway_failure = optional(number) + enforcing_consecutive_errors = optional(number) + enforcing_consecutive_gateway_failure = optional(number) + enforcing_success_rate = optional(number) + max_ejection_percent = optional(number) + success_rate_minimum_hosts = optional(number) + success_rate_request_volume = optional(number) + success_rate_stdev_factor = optional(number) + base_ejection_time = optional(object({ + seconds = number + nanos = optional(number) + })) + interval = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + security_settings = optional(object({ + client_tls_policy = string + subject_alt_names = list(string) + })) + })) + default = {} + nullable = false + validation { + condition = contains( + [ + "-", "ROUND_ROBIN", "LEAST_REQUEST", "RING_HASH", + "RANDOM", "ORIGINAL_DESTINATION", "MAGLEV" + ], + try(var.backend_service_configs.locality_lb_policy, "-") + ) + error_message = "Invalid locality lb policy value." + } + validation { + condition = contains( + [ + "NONE", "CLIENT_IP", "CLIENT_IP_NO_DESTINATION", + "CLIENT_IP_PORT_PROTO", "CLIENT_IP_PROTO" + ], + try(var.backend_service_configs.session_affinity, "NONE") + ) + error_message = "Invalid session affinity value." + } +} diff --git a/modules/net-glb/variables-health-check.tf b/modules/net-glb/variables-health-check.tf new file mode 100644 index 00000000..bfb8b67b --- /dev/null +++ b/modules/net-glb/variables-health-check.tf @@ -0,0 +1,106 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description Health check variable. + +variable "health_check_configs" { + description = "Optional auto-created health check configurations, use the output self-link to set it in the auto healing policy. Refer to examples for usage." + type = map(object({ + check_interval_sec = optional(number) + description = optional(string, "Terraform managed.") + enable_logging = optional(bool, false) + healthy_threshold = optional(number) + project_id = optional(string) + timeout_sec = optional(number) + unhealthy_threshold = optional(number) + grpc = optional(object({ + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + service_name = optional(string) + })) + http = optional(object({ + host = optional(string) + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request_path = optional(string) + response = optional(string) + })) + http2 = optional(object({ + host = optional(string) + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request_path = optional(string) + response = optional(string) + })) + https = optional(object({ + host = optional(string) + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request_path = optional(string) + response = optional(string) + })) + tcp = optional(object({ + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request = optional(string) + response = optional(string) + })) + ssl = optional(object({ + port = optional(number) + port_name = optional(string) + port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT + proxy_header = optional(string) + request = optional(string) + response = optional(string) + })) + })) + default = { + default = { + http = { + port_specification = "USE_SERVING_PORT" + } + } + } + validation { + condition = alltrue([ + for k, v in var.health_check_configs : ( + (try(v.grpc, null) == null ? 0 : 1) + + (try(v.http, null) == null ? 0 : 1) + + (try(v.tcp, null) == null ? 0 : 1) <= 1 + ) + ]) + error_message = "At most one health check type can be configured at a time." + } + validation { + condition = alltrue(flatten([ + for k, v in var.health_check_configs : [ + for kk, vv in v : contains([ + "-", "USE_FIXED_PORT", "USE_NAMED_PORT", "USE_SERVING_PORT" + ], coalesce(try(vv.port_specification, null), "-")) + ] + ])) + error_message = "Invalid 'port_specification' value. Supported values are 'USE_FIXED_PORT', 'USE_NAMED_PORT', 'USE_SERVING_PORT'." + } +} diff --git a/modules/net-glb/variables-urlmap.tf b/modules/net-glb/variables-urlmap.tf new file mode 100644 index 00000000..e4b72dfe --- /dev/null +++ b/modules/net-glb/variables-urlmap.tf @@ -0,0 +1,372 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description URLmap variable. + +variable "urlmap_config" { + description = "The URL map configuration." + type = object({ + default_route_action = optional(object({ + request_mirror_backend = optional(string) + cors_policy = optional(object({ + allow_credentials = optional(bool) + allow_headers = optional(string) + allow_methods = optional(string) + allow_origin_regexes = list(string) + allow_origins = list(string) + disabled = optional(bool) + expose_headers = optional(string) + max_age = optional(string) + })) + fault_injection_policy = optional(object({ + abort = optional(object({ + percentage = number + status = number + })) + delay = optional(object({ + fixed = object({ + seconds = number + nanos = number + }) + percentage = number + })) + })) + retry_policy = optional(object({ + num_retries = number + retry_conditions = optional(list(string)) + per_try_timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + url_rewrite = optional(object({ + host = optional(string) + path_prefix = optional(string) + })) + weighted_backend_services = optional(map(object({ + weight = number + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + }))) + })) + default_service = optional(string) + default_url_redirect = optional(object({ + host = optional(string) + https = optional(bool) + path = optional(string) + prefix = optional(string) + response_code = optional(string) + strip_query = optional(bool) + })) + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + host_rules = optional(list(object({ + hosts = list(string) + path_matcher = string + description = optional(string) + }))) + path_matchers = optional(map(object({ + description = optional(string) + default_route_action = optional(object({ + request_mirror_backend = optional(string) + cors_policy = optional(object({ + allow_credentials = optional(bool) + allow_headers = optional(string) + allow_methods = optional(string) + allow_origin_regexes = list(string) + allow_origins = list(string) + disabled = optional(bool) + expose_headers = optional(string) + max_age = optional(string) + })) + fault_injection_policy = optional(object({ + abort = optional(object({ + percentage = number + status = number + })) + delay = optional(object({ + fixed = object({ + seconds = number + nanos = number + }) + percentage = number + })) + })) + retry_policy = optional(object({ + num_retries = number + retry_conditions = optional(list(string)) + per_try_timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + url_rewrite = optional(object({ + host = optional(string) + path_prefix = optional(string) + })) + weighted_backend_services = optional(map(object({ + weight = number + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + }))) + })) + default_service = optional(string) + default_url_redirect = optional(object({ + host = optional(string) + https = optional(bool) + path = optional(string) + prefix = optional(string) + response_code = optional(string) + strip_query = optional(bool) + })) + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + path_rules = optional(list(object({ + paths = list(string) + service = optional(string) + route_action = optional(object({ + request_mirror_backend = optional(string) + cors_policy = optional(object({ + allow_credentials = optional(bool) + allow_headers = optional(string) + allow_methods = optional(string) + allow_origin_regexes = list(string) + allow_origins = list(string) + disabled = optional(bool) + expose_headers = optional(string) + max_age = optional(string) + })) + fault_injection_policy = optional(object({ + abort = optional(object({ + percentage = number + status = number + })) + delay = optional(object({ + fixed = object({ + seconds = number + nanos = number + }) + percentage = number + })) + })) + retry_policy = optional(object({ + num_retries = number + retry_conditions = optional(list(string)) + per_try_timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + url_rewrite = optional(object({ + host = optional(string) + path_prefix = optional(string) + })) + weighted_backend_services = optional(map(object({ + weight = number + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + }))) + })) + url_redirect = optional(object({ + host = optional(string) + https = optional(bool) + path = optional(string) + prefix = optional(string) + response_code = optional(string) + strip_query = optional(bool) + })) + }))) + route_rules = optional(list(object({ + priority = number + service = optional(string) + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + match_rules = optional(list(object({ + ignore_case = optional(bool, false) + headers = optional(list(object({ + name = string + invert_match = optional(bool, false) + type = optional(string, "present") # exact, prefix, suffix, regex, present, range + value = optional(string) + range_value = optional(object({ + end = string + start = string + })) + }))) + metadata_filters = optional(list(object({ + labels = map(string) + match_all = bool # MATCH_ANY, MATCH_ALL + }))) + path = optional(object({ + value = string + type = optional(string, "prefix") # full, prefix, regex + })) + query_params = optional(list(object({ + name = string + value = string + type = optional(string, "present") # exact, present, regex + }))) + }))) + route_action = optional(object({ + request_mirror_backend = optional(string) + cors_policy = optional(object({ + allow_credentials = optional(bool) + allow_headers = optional(string) + allow_methods = optional(string) + allow_origin_regexes = list(string) + allow_origins = list(string) + disabled = optional(bool) + expose_headers = optional(string) + max_age = optional(string) + })) + fault_injection_policy = optional(object({ + abort = optional(object({ + percentage = number + status = number + })) + delay = optional(object({ + fixed = object({ + seconds = number + nanos = number + }) + percentage = number + })) + })) + retry_policy = optional(object({ + num_retries = number + retry_conditions = optional(list(string)) + per_try_timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + })) + timeout = optional(object({ + seconds = number + nanos = optional(number) + })) + url_rewrite = optional(object({ + host = optional(string) + path_prefix = optional(string) + })) + weighted_backend_services = optional(map(object({ + weight = number + header_action = optional(object({ + request_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + request_remove = optional(list(string)) + response_add = optional(map(object({ + value = string + replace = optional(bool, true) + }))) + response_remove = optional(list(string)) + })) + }))) + })) + url_redirect = optional(object({ + host = optional(string) + https = optional(bool) + path = optional(string) + prefix = optional(string) + response_code = optional(string) + strip_query = optional(bool) + })) + }))) + }))) + test = optional(list(object({ + host = string + path = string + service = string + description = optional(string) + }))) + }) + default = { + default_service = "default" + } +} diff --git a/modules/net-glb/variables.tf b/modules/net-glb/variables.tf index 69427a3f..523b8f5f 100644 --- a/modules/net-glb/variables.tf +++ b/modules/net-glb/variables.tf @@ -14,173 +14,78 @@ * limitations under the License. */ -variable "backend_services_config" { - description = "The backends services configuration." +variable "address" { + description = "Optional IP address used for the forwarding rule." + type = string + default = null +} + +variable "backend_buckets_config" { + description = "Backend buckets configuration." type = map(object({ - enable_cdn = bool - - cdn_config = object({ - cache_mode = string - client_ttl = number - default_ttl = number - max_ttl = number - negative_caching = bool - negative_caching_policy = map(number) - serve_while_stale = bool - signed_url_cache_max_age_sec = string - }) - - bucket_config = object({ - bucket_name = string - options = object({ - custom_response_headers = list(string) - }) - }) - - group_config = object({ - backends = list(object({ - group = string # IG or NEG FQDN address - options = object({ - balancing_mode = string # Can be UTILIZATION, RATE, CONNECTION - capacity_scaler = number # Valid range is [0.0,1.0] - max_connections = number - max_connections_per_instance = number - max_connections_per_endpoint = number - max_rate = number - max_rate_per_instance = number - max_rate_per_endpoint = number - max_utilization = number - }) + bucket_name = string + compression_mode = optional(string) + custom_response_headers = optional(list(string)) + description = optional(string) + edge_security_policy = optional(string) + enable_cdn = optional(bool) + cdn_policy = optional(object({ + bypass_cache_on_request_headers = optional(list(string)) + cache_mode = optional(string) + client_ttl = optional(number) + default_ttl = optional(number) + max_ttl = optional(number) + negative_caching = optional(bool) + request_coalescing = optional(bool) + serve_while_stale = optional(bool) + signed_url_cache_max_age_sec = optional(number) + cache_key_policy = optional(object({ + include_http_headers = optional(list(string)) + query_string_whitelist = optional(list(string)) })) - - # Optional health check ids for backend service groups. - # Will lookup for ids in health_chacks_config first, - # then will use the id as is. If no ids are defined - # at all (null, []) health_checks_config_defaults is used - health_checks = list(string) - - log_config = object({ - enable = bool - sample_rate = number # must be in [0, 1] - }) - - options = object({ - affinity_cookie_ttl_sec = number - custom_request_headers = list(string) - custom_response_headers = list(string) - connection_draining_timeout_sec = number - load_balancing_scheme = string # only EXTERNAL (default) makes sense here - locality_lb_policy = string - port_name = string - protocol = string - security_policy = string - session_affinity = string - timeout_sec = number - - circuits_breakers = object({ - max_requests_per_connection = number # Set to 1 to disable keep-alive - max_connections = number # Defaults to 1024 - max_pending_requests = number # Defaults to 1024 - max_requests = number # Defaults to 1024 - max_retries = number # Defaults to 3 - }) - - consistent_hash = object({ - http_header_name = string - minimum_ring_size = string - http_cookie = object({ - name = string - path = string - ttl = object({ - seconds = number - nanos = number - }) - }) - }) - - iap = object({ - oauth2_client_id = string - oauth2_client_secret = string - oauth2_client_secret_sha256 = string - }) - }) - }) + negative_caching_policy = optional(object({ + code = optional(number) + ttl = optional(number) + })) + })) })) - default = {} + default = {} + nullable = true } -variable "forwarding_rule_config" { - description = "Regional forwarding rule configurations." - type = object({ - ip_protocol = string - ip_version = string - load_balancing_scheme = string - port_range = string - network_tier = string - network = string - }) - default = { - load_balancing_scheme = "EXTERNAL_MANAGED" - ip_protocol = "TCP" - ip_version = "IPV4" - network_tier = "STANDARD" - network = "default" - # If not specified, 80 for https = false, 443 otherwise - port_range = null - } +variable "description" { + description = "Optional description used for resources." + type = string + default = "Terraform managed." } -variable "global_forwarding_rule_config" { - description = "Global forwarding rule configurations." - type = object({ - ip_protocol = string - ip_version = string - load_balancing_scheme = string - port_range = string - - }) - default = { - load_balancing_scheme = "EXTERNAL" - ip_protocol = "TCP" - ip_version = "IPV4" - # If not specified, 80 for https = false, 443 otherwise - port_range = null - } -} - -variable "health_checks_config" { - description = "Custom health checks configuration." +variable "group_configs" { + description = "Optional unmanaged groups to create. Can be referenced in backends via key or outputs." type = map(object({ - type = string # http https tcp ssl http2 - check = map(any) # actual health check block attributes - options = map(number) # interval, thresholds, timeout - logging = bool + zone = string + instances = optional(list(string), []) + named_ports = optional(map(number), {}) + project_id = optional(string) })) - default = {} + default = {} + nullable = false } -variable "health_checks_config_defaults" { - description = "Auto-created health check default configuration." +variable "https_proxy_config" { + description = "HTTPS proxy connfiguration." type = object({ - type = string # http https tcp ssl http2 - check = map(any) # actual health check block attributes - options = map(number) # interval, thresholds, timeout - logging = bool + certificate_map = optional(string) + quic_override = optional(string) + ssl_policy = optional(string) }) - default = { - type = "http" - logging = false - options = {} - check = { - port_specification = "USE_SERVING_PORT" - } - } + default = {} + nullable = false } -variable "https" { - description = "Whether to enable HTTPS." - type = bool - default = false +variable "labels" { + description = "Labels set on resources." + type = map(string) + default = {} } variable "name" { @@ -188,70 +93,140 @@ variable "name" { type = string } +variable "neg_configs" { + description = "Optional network endpoint groups to create. Can be referenced in backends via key or outputs." + type = map(object({ + description = optional(string) + cloudfunction = optional(object({ + region = string + target_function = optional(string) + target_urlmask = optional(string) + })) + cloudrun = optional(object({ + region = string + target_service = optional(object({ + name = string + tag = optional(string) + })) + target_urlmask = optional(string) + })) + gce = optional(object({ + network = string + subnetwork = string + zone = string + # default_port = optional(number) + endpoints = optional(list(object({ + instance = string + ip_address = string + port = number + }))) + })) + hybrid = optional(object({ + network = string + zone = string + # re-enable once provider properly support this + # default_port = optional(number) + endpoints = optional(list(object({ + ip_address = string + port = number + }))) + })) + internet = optional(object({ + use_fqdn = optional(bool, true) + # re-enable once provider properly support this + # default_port = optional(number) + endpoints = optional(list(object({ + destination = string + port = number + }))) + })) + psc = optional(object({ + region = string + target_service = string + network = optional(string) + subnetwork = optional(string) + })) + })) + default = {} + nullable = false + validation { + condition = alltrue([ + for k, v in var.neg_configs : ( + (try(v.cloudfunction, null) == null ? 0 : 1) + + (try(v.cloudrun, null) == null ? 0 : 1) + + (try(v.gce, null) == null ? 0 : 1) + + (try(v.hybrid, null) == null ? 0 : 1) + + (try(v.internet, null) == null ? 0 : 1) + + (try(v.psc, null) == null ? 0 : 1) == 1 + ) + ]) + error_message = "Only one type of NEG can be configured at a time." + } + validation { + condition = alltrue([ + for k, v in var.neg_configs : ( + v.cloudrun == null + ? true + : v.cloudrun.target_urlmask != null || v.cloudrun.target_service != null + ) + ]) + error_message = "Cloud Run NEGs need either target service or target urlmask defined." + } + validation { + condition = alltrue([ + for k, v in var.neg_configs : ( + v.cloudfunction == null + ? true + : v.cloudfunction.target_urlmask != null || v.cloudfunction.target_function != null + ) + ]) + error_message = "Cloud Function NEGs need either target function or target urlmask defined." + } +} + +variable "ports" { + description = "Optional ports for HTTP load balancer, valid ports are 80 and 8080." + type = list(string) + default = null +} + variable "project_id" { description = "Project id." type = string } -variable "region" { - description = "Create a regional load balancer in this region." +variable "protocol" { + description = "Protocol supported by this load balancer." type = string - default = null -} - -variable "reserve_ip_address" { - description = "Whether to reserve a static global IP address." - type = bool - default = false -} - -variable "ssl_certificates_config" { - description = "The SSL certificate configuration." - type = map(object({ - domains = list(string) - # If unmanaged_config is null, the certificate will be managed - unmanaged_config = object({ - tls_private_key = string - tls_self_signed_cert = string - }) - })) - default = {} -} - -variable "ssl_certificates_config_defaults" { - description = "The SSL certificate default configuration." - type = object({ - domains = list(string) - # If unmanaged_config is null, the certificate will be managed - unmanaged_config = object({ - tls_private_key = string - tls_self_signed_cert = string - }) - }) - default = { - domains = ["example.com"], - unmanaged_config = null + default = "HTTP" + nullable = false + validation { + condition = ( + var.protocol == null || var.protocol == "HTTP" || var.protocol == "HTTPS" + ) + error_message = "Protocol must be HTTP or HTTPS" } } -variable "target_proxy_https_config" { - description = "The HTTPS target proxy configuration." +variable "ssl_certificates" { + description = "SSL target proxy certificates (only if protocol is HTTPS) for existing, custom, and managed certificates." type = object({ - ssl_certificates = list(string) + certificate_ids = optional(list(string), []) + create_configs = optional(map(object({ + certificate = string + private_key = string + })), {}) + managed_configs = optional(map(object({ + domains = list(string) + description = optional(string) + })), {}) }) - default = null + default = {} + nullable = false } -variable "url_map_config" { - description = "The url-map configuration." - type = object({ - default_service = string - default_route_action = any - default_url_redirect = map(any) - header_action = any - host_rules = list(any) - path_matchers = list(any) - tests = list(map(string)) - }) - default = null +variable "use_classic_version" { + description = "Use classic Global Load Balancer." + type = bool + default = true } diff --git a/tests/blueprints/cloud_operations/adfs/test_plan.py b/tests/blueprints/cloud_operations/adfs/test_plan.py index 7aeafc1d..c9682f2d 100644 --- a/tests/blueprints/cloud_operations/adfs/test_plan.py +++ b/tests/blueprints/cloud_operations/adfs/test_plan.py @@ -16,4 +16,4 @@ def test_resources(e2e_plan_runner): "Test that plan works and the numbers of resources is as expected." modules, resources = e2e_plan_runner() assert len(modules) == 4 - assert len(resources) == 16 + assert len(resources) == 15 diff --git a/tests/blueprints/cloud_operations/apigee/test_plan.py b/tests/blueprints/cloud_operations/apigee/test_plan.py index aac6f684..34ad83a8 100644 --- a/tests/blueprints/cloud_operations/apigee/test_plan.py +++ b/tests/blueprints/cloud_operations/apigee/test_plan.py @@ -18,4 +18,4 @@ def test_blueprint(recursive_e2e_plan_runner): "Test that all blueprint resources are created." count_modules, count_resources = recursive_e2e_plan_runner(tf_var_file='test.regular.tfvars') assert count_modules == 10 - assert count_resources == 60 \ No newline at end of file + assert count_resources == 59 diff --git a/tests/modules/net_glb/fixture/main.tf b/tests/modules/net_glb/fixture/main.tf deleted file mode 100644 index b2c68e03..00000000 --- a/tests/modules/net_glb/fixture/main.tf +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2022 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. - */ - -module "test" { - source = "../../../../modules/net-glb" - project_id = "my-project" - name = "glb-test" - region = var.region - health_checks_config_defaults = var.health_checks_config_defaults - health_checks_config = var.health_checks_config - backend_services_config = var.backend_services_config - url_map_config = var.url_map_config - https = var.https - ssl_certificates_config = var.ssl_certificates_config - ssl_certificates_config_defaults = var.ssl_certificates_config_defaults - target_proxy_https_config = var.target_proxy_https_config - reserve_ip_address = var.reserve_ip_address - global_forwarding_rule_config = var.global_forwarding_rule_config - forwarding_rule_config = var.forwarding_rule_config -} - diff --git a/tests/modules/net_glb/fixture/variables.tf b/tests/modules/net_glb/fixture/variables.tf deleted file mode 100644 index 6b818405..00000000 --- a/tests/modules/net_glb/fixture/variables.tf +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright 2022 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. - */ - -variable "region" { - description = "Create a regional load balancer." - type = string - default = null -} - -variable "health_checks_config_defaults" { - description = "Auto-created health check default configuration." - type = object({ - type = string # http https tcp ssl http2 - check = map(any) # actual health check block attributes - options = map(number) # interval, thresholds, timeout - logging = bool - }) - default = { - type = "http" - logging = false - options = {} - check = { - port_specification = "USE_SERVING_PORT" - } - } -} - -variable "health_checks_config" { - description = "Custom health checks configuration." - type = map(object({ - type = string # http https tcp ssl http2 - check = map(any) # actual health check block attributes - options = map(number) # interval, thresholds, timeout - logging = bool - })) - default = {} -} - -variable "backend_services_config" { - description = "The backends services configuration." - type = map(object({ - enable_cdn = bool - - cdn_config = object({ - cache_mode = string - client_ttl = number - default_ttl = number - max_ttl = number - negative_caching = bool - negative_caching_policy = map(number) - serve_while_stale = bool - signed_url_cache_max_age_sec = string - }) - - bucket_config = object({ - bucket_name = string - options = object({ - custom_response_headers = list(string) - }) - }) - - group_config = object({ - backends = list(object({ - group = string # IG or NEG FQDN address - options = object({ - balancing_mode = string # Can be UTILIZATION, RATE, CONNECTION - capacity_scaler = number # Valid range is [0.0,1.0] - max_connections = number - max_connections_per_instance = number - max_connections_per_endpoint = number - max_rate = number - max_rate_per_instance = number - max_rate_per_endpoint = number - max_utilization = number - }) - })) - - # Optional health check ids for backend service groups. - # Will lookup for ids in health_chacks_config first, - # then will use the id as is. If no ids are defined - # at all (null, []) health_checks_config_defaults is used - health_checks = list(string) - - log_config = object({ - enable = bool - sample_rate = number # must be in [0, 1] - }) - - options = object({ - affinity_cookie_ttl_sec = number - custom_request_headers = list(string) - custom_response_headers = list(string) - connection_draining_timeout_sec = number - load_balancing_scheme = string # only EXTERNAL (default) makes sense here - locality_lb_policy = string - port_name = string - protocol = string - security_policy = string - session_affinity = string - timeout_sec = number - - circuits_breakers = object({ - max_requests_per_connection = number # Set to 1 to disable keep-alive - max_connections = number # Defaults to 1024 - max_pending_requests = number # Defaults to 1024 - max_requests = number # Defaults to 1024 - max_retries = number # Defaults to 3 - }) - - consistent_hash = object({ - http_header_name = string - minimum_ring_size = string - http_cookie = object({ - name = string - path = string - ttl = object({ - seconds = number - nanos = number - }) - }) - }) - - iap = object({ - oauth2_client_id = string - oauth2_client_secret = string - oauth2_client_secret_sha256 = string - }) - }) - }) - })) - default = {} -} - -variable "url_map_config" { - description = "The url-map configuration." - type = object({ - default_service = string - default_route_action = any - default_url_redirect = map(any) - header_action = any - host_rules = list(any) - path_matchers = list(any) - tests = list(map(string)) - }) - default = null -} - -variable "ssl_certificates_config" { - description = "The SSL certificate configuration." - type = map(object({ - domains = list(string) - # If unmanaged_config is null, the certificate will be managed - unmanaged_config = object({ - tls_private_key = string - tls_self_signed_cert = string - }) - })) - default = {} -} - -variable "ssl_certificates_config_defaults" { - description = "The SSL certificate default configuration." - type = object({ - domains = list(string) - # If unmanaged_config is null, the certificate will be managed - unmanaged_config = object({ - tls_private_key = string - tls_self_signed_cert = string - }) - }) - default = { - domains = ["example.com"], - unmanaged_config = null - } -} - -variable "target_proxy_https_config" { - description = "The HTTPS target proxy configuration." - type = object({ - ssl_certificates = list(string) - }) - default = null -} - -variable "global_forwarding_rule_config" { - description = "Global forwarding rule configurations." - type = object({ - ip_protocol = string - ip_version = string - load_balancing_scheme = string - port_range = string - - }) - default = { - load_balancing_scheme = "EXTERNAL" - ip_protocol = "TCP" - ip_version = "IPV4" - # If not specified, 80 for https = false, 443 otherwise - port_range = null - } -} - -variable "forwarding_rule_config" { - description = "Global forwarding rule configurations." - type = object({ - ip_protocol = string - ip_version = string - load_balancing_scheme = string - port_range = string - network_tier = string - network = string - }) - default = { - load_balancing_scheme = "EXTERNAL_MANAGED" - ip_protocol = "TCP" - ip_version = "IPV4" - # If not specified, 80 for https = false, 443 otherwise - port_range = null - network_tier = "STANDARD" - network = "default" - } -} - -variable "https" { - description = "Whether to enable HTTPS." - type = bool - default = false -} - -variable "reserve_ip_address" { - description = "Whether to reserve a static global IP address." - type = bool - default = false -} diff --git a/tests/modules/net_glb/test-plan.tfvars b/tests/modules/net_glb/test-plan.tfvars new file mode 100644 index 00000000..f10667f1 --- /dev/null +++ b/tests/modules/net_glb/test-plan.tfvars @@ -0,0 +1,109 @@ +name = "glb-test-0" +project_id = "my-project" +backend_buckets_config = { + default-gcs = { + bucket_name = "my-bucket" + } +} +backend_service_configs = { + default = { + backends = [ + { backend = "projects/my-project/zones/europe-west8-b/instanceGroups/ig-b" }, + { backend = "ig-c" } + ] + } + neg-cloudrun = { + backends = [{ backend = "neg-cloudrun" }] + health_checks = [] + } + neg-gce = { + backends = [{ backend = "neg-gce" }] + balancing_mode = "RATE" + max_rate = { per_endpoint = 10 } + } + neg-hybrid = { + backends = [{ backend = "neg-hybrid" }] + balancing_mode = "RATE" + max_rate = { per_endpoint = 10 } + } + neg-internet = { + backends = [{ backend = "neg-internet" }] + health_checks = [] + } +} +group_configs = { + ig-c = { + zone = "europe-west8-c" + instances = [ + "projects/my-project/zones/europe-west8-c/instances/vm-c" + ] + named_ports = { http = 80 } + } +} +health_check_configs = { + default = { + http = { + host = "hello.example.org" + port_specification = "USE_SERVING_PORT" + } + } +} +neg_configs = { + neg-cloudrun = { + cloudrun = { + region = "europe-west8" + target_service = { + name = "hello" + } + } + } + neg-gce = { + gce = { + network = "projects/my-project/global/networks/shared-vpc" + subnetwork = "projects/my-project/regions/europe-west8/subnetworks/gce" + zone = "europe-west8-b" + endpoints = [{ + instance = "nginx-ew8-b" + ip_address = "10.24.32.25" + port = 80 + }] + } + } + neg-hybrid = { + hybrid = { + network = "projects/my-project/global/networks/shared-vpc" + zone = "europe-west8-b" + endpoints = [{ + ip_address = "192.168.0.3" + port = 80 + }] + } + } + neg-internet = { + internet = { + use_fqdn = true + endpoints = [{ + destination = "hello.example.org" + port = 80 + }] + } + } +} +urlmap_config = { + default_service = "default" + host_rules = [{ + hosts = ["*"] + path_matcher = "pathmap" + }] + path_matchers = { + pathmap = { + default_service = "default" + path_rules = [ + { paths = ["/cloudrun", "/cloudrun/*"], service = "neg-cloudrun" }, + { paths = ["/gce", "/gce/*"], service = "neg-gce" }, + { paths = ["/hybrid", "/hybrid/*"], service = "neg-hybrid" }, + { paths = ["/internet", "/internet/*"], service = "neg-internet" }, + ] + } + } +} diff --git a/tests/modules/net_glb/test-plan.yaml b/tests/modules/net_glb/test-plan.yaml new file mode 100644 index 00000000..e05939e1 --- /dev/null +++ b/tests/modules/net_glb/test-plan.yaml @@ -0,0 +1,34 @@ +# Copyright 2022 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. + +counts: + google_compute_backend_bucket: 1 + google_compute_backend_service: 5 + google_compute_global_forwarding_rule: 1 + google_compute_global_network_endpoint: 1 + google_compute_global_network_endpoint_group: 1 + google_compute_health_check: 1 + google_compute_instance_group: 1 + google_compute_network_endpoint: 2 + google_compute_network_endpoint_group: 2 + google_compute_region_network_endpoint_group: 1 + google_compute_target_http_proxy: 1 + google_compute_url_map: 1 +outputs: + address: __missing__ + backend_service_ids: __missing__ + forwarding_rule: __missing__ + group_ids: __missing__ + health_check_ids: __missing__ + neg_ids: __missing__ diff --git a/tests/modules/net_glb/test_plan.py b/tests/modules/net_glb/test_plan.py deleted file mode 100644 index 758ea508..00000000 --- a/tests/modules/net_glb/test_plan.py +++ /dev/null @@ -1,303 +0,0 @@ -# Copyright 2022 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. - -_BACKEND_BUCKET = '''{ - my-bucket = { - bucket_config = { - bucket_name = "my_bucket" - options = null - } - group_config = null - enable_cdn = false - cdn_config = null - } -}''' - -_BACKEND_GROUP = '''{ - my-group = { - bucket_config = null, - enable_cdn = false, - cdn_config = null, - group_config = { - backends = [ - { - group = "my_group", - options = null - } - ], - health_checks = [] - log_config = null - options = null - } - } -}''' - -_BACKEND_GROUP_HC = '''{ - my-group = { - bucket_config = null, - enable_cdn = false, - cdn_config = null, - group_config = { - backends = [ - { - group = "my_group", - options = null - } - ], - health_checks = ["hc_1"] - log_config = null - options = null - } - } -}''' - -_NAME = 'glb-test' - -_SSL_CERTIFICATES_CONFIG_MANAGED = '''{ - my-domain = { - domains = [ - "my-domain.com" - ] - unmanaged_config = null - } -}''' - -_SSL_CERTIFICATES_CONFIG_UNMANAGED = '''{ - my-domain = { - domains = [ - "my-domain.com" - ], - unmanaged_config = { - tls_private_key = "my-key" - tls_self_signed_cert = "my-cert" - } - } -}''' - -_TARGET_PROXY_HTTPS_CONFIG = '''{ - ssl_certificates = [ - "my-domain" - ] -}''' - - -def test_bucket(plan_runner): - "Tests a bucket backend service." - _, resources = plan_runner(backend_services_config=_BACKEND_BUCKET) - assert len(resources) == 4 - resources = dict((r['type'], r['values']) for r in resources) - - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['load_balancing_scheme'] == 'EXTERNAL' - assert fwd_rule['port_range'] == '80' - assert fwd_rule['ip_protocol'] == 'TCP' - - bucket = resources['google_compute_backend_bucket'] - assert bucket['name'] == _NAME + '-my-bucket' - assert bucket['enable_cdn'] is False - - assert 'google_compute_health_check' not in resources - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_url_map' in resources - - -def test_group_default_hc(plan_runner): - "Tests a group backend service with no HC specified." - _, resources = plan_runner(backend_services_config=_BACKEND_GROUP) - assert len(resources) == 5 - resources = dict((r['type'], r['values']) for r in resources) - - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['load_balancing_scheme'] == 'EXTERNAL' - assert fwd_rule['port_range'] == '80' - assert fwd_rule['ip_protocol'] == 'TCP' - - group = resources['google_compute_backend_service'] - assert len(group['backend']) == 1 - assert group['backend'][0]['group'] == 'my_group' - - health_check = resources['google_compute_health_check'] - assert health_check['name'] == _NAME + '-default' - assert len(health_check['http_health_check']) > 0 - assert len(health_check['https_health_check']) == 0 - assert len(health_check['http2_health_check']) == 0 - assert len(health_check['tcp_health_check']) == 0 - assert health_check['http_health_check'][0][ - 'port_specification'] == 'USE_SERVING_PORT' - assert health_check['http_health_check'][0]['proxy_header'] == 'NONE' - assert health_check['http_health_check'][0]['request_path'] == '/' - - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_target_https_proxy' not in resources - assert 'google_compute_url_map' in resources - - -def test_group_default_hc_regional(plan_runner): - "Tests a group backend service with no HC specified." - _, resources = plan_runner(backend_services_config=_BACKEND_GROUP, - region="europe-west4") - assert len(resources) == 5 - resources = dict((r['type'], r['values']) for r in resources) - - fwd_rule = resources['google_compute_forwarding_rule'] - assert fwd_rule['load_balancing_scheme'] == 'EXTERNAL_MANAGED' - assert fwd_rule['port_range'] == '80' - assert fwd_rule['ip_protocol'] == 'TCP' - - group = resources['google_compute_region_backend_service'] - assert len(group['backend']) == 1 - assert group['backend'][0]['group'] == 'my_group' - - health_check = resources['google_compute_region_health_check'] - assert health_check['name'] == _NAME + '-default' - assert len(health_check['http_health_check']) > 0 - assert len(health_check['https_health_check']) == 0 - assert len(health_check['http2_health_check']) == 0 - assert len(health_check['tcp_health_check']) == 0 - assert health_check['http_health_check'][0][ - 'port_specification'] == 'USE_SERVING_PORT' - assert health_check['http_health_check'][0]['proxy_header'] == 'NONE' - assert health_check['http_health_check'][0]['request_path'] == '/' - - assert 'google_compute_region_target_http_proxy' in resources - assert 'google_compute_region_target_https_proxy' not in resources - assert 'google_compute_region_url_map' in resources - - -def test_group_no_hc(plan_runner): - "Tests a group backend service without HCs (including no default HC)." - _, resources = plan_runner(backend_services_config=_BACKEND_GROUP, - health_checks_config_defaults='null') - - assert len(resources) == 4 - resources = dict((r['type'], r['values']) for r in resources) - - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_health_check' not in resources - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_target_https_proxy' not in resources - assert 'google_compute_url_map' in resources - - -def test_group_existing_hc(plan_runner): - "Tests a group backend service with referencing an existing HC." - _, resources = plan_runner(backend_services_config=_BACKEND_GROUP_HC) - assert len(resources) == 4 - resources = dict((r['type'], r['values']) for r in resources) - - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_health_check' not in resources - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_target_https_proxy' not in resources - assert 'google_compute_url_map' in resources - - -def test_group_existing_hc_regional(plan_runner): - "Tests a group backend service with referencing an existing HC." - _, resources = plan_runner(backend_services_config=_BACKEND_GROUP_HC, - region="europe-west4") - assert len(resources) == 4 - resources = dict((r['type'], r['values']) for r in resources) - - assert 'google_compute_region_backend_service' in resources - assert 'google_compute_forwarding_rule' in resources - assert 'google_compute_region_health_check' not in resources - assert 'google_compute_region_target_http_proxy' in resources - assert 'google_compute_region_target_https_proxy' not in resources - assert 'google_compute_region_url_map' in resources - - -def test_reserved_ip(plan_runner): - "Tests an IP reservation with a group backend service." - _, resources = plan_runner(backend_services_config=_BACKEND_GROUP, - reserve_ip_address="true") - assert len(resources) == 6 - resources = dict((r['type'], r['values']) for r in resources) - - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_address' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_target_http_proxy' in resources - assert 'google_compute_target_https_proxy' not in resources - assert 'google_compute_url_map' in resources - - -def test_ssl_managed(plan_runner): - "Tests HTTPS and SSL managed certificates." - _, resources = plan_runner( - backend_services_config=_BACKEND_GROUP, - https='true', - ssl_certificates_config=_SSL_CERTIFICATES_CONFIG_MANAGED, - target_proxy_https_config=_TARGET_PROXY_HTTPS_CONFIG) - assert len(resources) == 6 - resources = dict((r['type'], r['values']) for r in resources) - - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['port_range'] == '443' - - ssl_cert = resources['google_compute_managed_ssl_certificate'] - assert ssl_cert['type'] == "MANAGED" - assert ssl_cert['managed'][0]['domains'][0] == 'my-domain.com' - - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_ssl_certificate' not in resources - assert 'google_compute_target_http_proxy' not in resources - assert 'google_compute_target_https_proxy' in resources - assert 'google_compute_url_map' in resources - - -def test_ssl_unmanaged(plan_runner): - "Tests HTTPS and SSL unmanaged certificates." - _, resources = plan_runner( - backend_services_config=_BACKEND_GROUP, - https="true", - ssl_certificates_config=_SSL_CERTIFICATES_CONFIG_UNMANAGED, - target_proxy_https_config=_TARGET_PROXY_HTTPS_CONFIG) - assert len(resources) == 6 - resources = dict((r['type'], r['values']) for r in resources) - - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['port_range'] == '443' - - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_managed_ssl_certificate' not in resources - assert 'google_compute_ssl_certificate' in resources - assert 'google_compute_target_http_proxy' not in resources - assert 'google_compute_target_https_proxy' in resources - assert 'google_compute_url_map' in resources - - -def test_ssl_existing_cert(plan_runner): - "Tests HTTPS and SSL existing certificate." - _, resources = plan_runner( - backend_services_config=_BACKEND_GROUP, - https="true", - target_proxy_https_config=_TARGET_PROXY_HTTPS_CONFIG) - assert len(resources) == 5 - resources = dict((r['type'], r['values']) for r in resources) - - fwd_rule = resources['google_compute_global_forwarding_rule'] - assert fwd_rule['port_range'] == '443' - - assert 'google_compute_backend_service' in resources - assert 'google_compute_global_forwarding_rule' in resources - assert 'google_compute_managed_ssl_certificate' not in resources - assert 'google_compute_ssl_certificate' not in resources - assert 'google_compute_target_http_proxy' not in resources - assert 'google_compute_target_https_proxy' in resources - assert 'google_compute_url_map' in resources diff --git a/tests/modules/net_glb/tftest.yaml b/tests/modules/net_glb/tftest.yaml new file mode 100644 index 00000000..4130f2a5 --- /dev/null +++ b/tests/modules/net_glb/tftest.yaml @@ -0,0 +1,17 @@ +# Copyright 2022 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. + +module: modules/net-glb +tests: + test-plan: diff --git a/tests/modules/organization/validate_policies.py b/tests/modules/organization/validate_policies.py deleted file mode 100644 index 51844b15..00000000 --- a/tests/modules/organization/validate_policies.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright 2022 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. - - -def validate_policy_boolean(resources): - assert len(resources) == 2 - assert all( - r['values']['parent'] == 'organizations/1234567890' for r in resources) - policies = { - r['index']: r['values']['spec'][0] - for r in resources - if r['type'] == 'google_org_policy_policy' - } - assert len(policies) == 2 - p1 = policies['iam.disableServiceAccountKeyCreation'] - assert p1['inherit_from_parent'] is None - assert p1['reset'] is None - assert p1['rules'] == [{ - 'allow_all': None, - 'condition': [], - 'deny_all': None, - 'enforce': 'TRUE', - 'values': [] - }] - p2 = policies['iam.disableServiceAccountKeyUpload'] - assert p2['inherit_from_parent'] is None - assert p2['reset'] is None - assert len(p2['rules']) == 2 - assert p2['rules'][0] == { - 'allow_all': None, - 'condition': [], - 'deny_all': None, - 'enforce': 'FALSE', - 'values': [] - } - assert p2['rules'][1] == { - 'allow_all': None, - 'condition': [{ - 'description': 'test condition', - 'expression': 'resource.matchTagId(aa, bb)', - 'location': 'xxx', - 'title': 'condition' - }], - 'deny_all': None, - 'enforce': 'TRUE', - 'values': [] - } - - -def validate_policy_list(resources): - assert len(resources) == 3 - assert all( - r['values']['parent'] == 'organizations/1234567890' for r in resources) - policies = { - r['index']: r['values']['spec'][0] - for r in resources - if r['type'] == 'google_org_policy_policy' - } - assert len(policies) == 3 - p1 = policies['compute.vmExternalIpAccess'] - assert p1['inherit_from_parent'] is None - assert p1['reset'] is None - assert p1['rules'] == [{ - 'allow_all': None, - 'condition': [], - 'deny_all': 'TRUE', - 'enforce': None, - 'values': [] - }] - p2 = policies['iam.allowedPolicyMemberDomains'] - assert p2['inherit_from_parent'] is None - assert p2['reset'] is None - assert p2['rules'] == [{ - 'allow_all': - None, - 'condition': [], - 'deny_all': - None, - 'enforce': - None, - 'values': [{ - 'allowed_values': [ - 'C0xxxxxxx', - 'C0yyyyyyy', - ], - 'denied_values': None - }] - }] - p3 = policies['compute.restrictLoadBalancerCreationForTypes'] - assert p3['inherit_from_parent'] is None - assert p3['reset'] is None - assert len(p3['rules']) == 3 - assert p3['rules'][0] == { - 'allow_all': None, - 'condition': [], - 'deny_all': None, - 'enforce': None, - 'values': [{ - 'allowed_values': None, - 'denied_values': ['in:EXTERNAL'] - }] - } - assert p3['rules'][1] == { - 'allow_all': None, - 'condition': [{ - 'description': 'test condition', - 'expression': 'resource.matchTagId(aa, bb)', - 'location': 'xxx', - 'title': 'condition' - }], - 'deny_all': None, - 'enforce': None, - 'values': [{ - 'allowed_values': ['EXTERNAL_1'], - 'denied_values': None - }] - } - assert p3['rules'][2] == { - 'allow_all': 'TRUE', - 'condition': [{ - 'description': 'test condition2', - 'expression': 'resource.matchTagId(cc, dd)', - 'location': 'xxx', - 'title': 'condition2' - }], - 'deny_all': None, - 'enforce': None, - 'values': [] - } - -def validate_policy_custom_constraints(resources): - assert len(resources) == 2 - assert all( - r['values']['parent'] == 'organizations/1234567890' for r in resources) - constraints = { - r['index']: r['values'] - for r in resources - if r['type'] == 'google_org_policy_custom_constraint' - } - assert len(constraints) == 2 - c1 = constraints['custom.gkeEnableAutoUpgrade'] - assert c1['resource_types'][0] == 'container.googleapis.com/NodePool' - assert c1['method_types'] == ['CREATE'] - assert c1['condition'] == 'resource.management.autoUpgrade == true' - assert c1['action_type'] == 'ALLOW' - - c2 = constraints['custom.dataprocNoMoreThan10Workers'] - assert c2['resource_types'][0] == 'dataproc.googleapis.com/Cluster' - assert c2['method_types'] == ['CREATE', 'UPDATE'] - assert c2['condition'] == 'resource.config.workerConfig.numInstances + resource.config.secondaryWorkerConfig.numInstances > 10' - assert c2['action_type'] == 'DENY'