From a8ddf5d1bae9fb2c2e3001e9a026380f2bf803bb Mon Sep 17 00:00:00 2001 From: Luca Prete Date: Sun, 16 Oct 2022 10:18:40 +0200 Subject: [PATCH] Add PSC hybrid blueprint --- blueprints/README.md | 2 +- blueprints/networking/README.md | 9 +- blueprints/networking/psc-hybrid/README.md | 55 +++++++ blueprints/networking/psc-hybrid/diagram.png | Bin 0 -> 58239 bytes blueprints/networking/psc-hybrid/main.tf | 136 ++++++++++++++++++ .../psc-hybrid/psc-consumer/README.md | 15 ++ .../psc-hybrid/psc-consumer/main.tf | 33 +++++ .../psc-hybrid/psc-consumer/variables.tf | 45 ++++++ .../psc-hybrid/psc-producer/README.md | 26 ++++ .../psc-hybrid/psc-producer/main.tf | 107 ++++++++++++++ .../psc-hybrid/psc-producer/outputs.tf | 20 +++ .../psc-hybrid/psc-producer/variables.tf | 71 +++++++++ blueprints/networking/psc-hybrid/variables.tf | 101 +++++++++++++ 13 files changed, 618 insertions(+), 2 deletions(-) create mode 100644 blueprints/networking/psc-hybrid/README.md create mode 100644 blueprints/networking/psc-hybrid/diagram.png create mode 100644 blueprints/networking/psc-hybrid/main.tf create mode 100644 blueprints/networking/psc-hybrid/psc-consumer/README.md create mode 100644 blueprints/networking/psc-hybrid/psc-consumer/main.tf create mode 100644 blueprints/networking/psc-hybrid/psc-consumer/variables.tf create mode 100644 blueprints/networking/psc-hybrid/psc-producer/README.md create mode 100644 blueprints/networking/psc-hybrid/psc-producer/main.tf create mode 100644 blueprints/networking/psc-hybrid/psc-producer/outputs.tf create mode 100644 blueprints/networking/psc-hybrid/psc-producer/variables.tf create mode 100644 blueprints/networking/psc-hybrid/variables.tf diff --git a/blueprints/README.md b/blueprints/README.md index 61323dab..60055b18 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -8,7 +8,7 @@ Currently available blueprints: - **data solutions** - [GCE/GCS CMEK via centralized Cloud KMS](./data-solutions/gcs-to-bq-with-least-privileges/), [Cloud Storage to Bigquery with Cloud Dataflow with least privileges](./data-solutions/gcs-to-bq-with-least-privileges/), [Data Platform Foundations](./data-solutions/data-platform-foundations/), [SQL Server AlwaysOn availability groups blueprint](./data-solutions/sqlserver-alwayson), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion/), [Cloud Composer version 2 private instance, supporting Shared VPC and external CMEK key](./data-solutions/composer-2/) - **factories** - [The why and the how of resource factories](./factories/README.md) - **GKE** - [GKE multitenant fleet](./gke/multitenant-fleet/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [Binary Authorization Pipeline](./gke/binauthz/), [Multi-cluster mesh on GKE (fleet API)](./gke/multi-cluster-mesh-gke-fleet-api/) -- **networking** - [hub and spoke via peering](./networking/hub-and-spoke-peering/), [hub and spoke via VPN](./networking/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./networking/onprem-google-access-dns/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [ILB as next hop](./networking/ilb-next-hop), [PSC for on-premises Cloud Function invocation](./networking/private-cloud-function-from-onprem/), [decentralized firewall](./networking/decentralized-firewall) +- **networking** - [hub and spoke via peering](./networking/hub-and-spoke-peering/), [hub and spoke via VPN](./networking/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./networking/onprem-google-access-dns/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [ILB as next hop](./networking/ilb-next-hop), [Connecting to on-premise services leveraging PSC and hybrid NEGs](./networking/psc-hybrid/), [decentralized firewall](./networking/decentralized-firewall) - **serverless** - [Multi-region deployments for API Gateway](./serverless/api-gateway/) - **third party solutions** - [OpenShift cluster on Shared VPC](./third-party-solutions/openshift) diff --git a/blueprints/networking/README.md b/blueprints/networking/README.md index b35ec057..981fccc0 100644 --- a/blueprints/networking/README.md +++ b/blueprints/networking/README.md @@ -39,11 +39,18 @@ It is meant to be used as a starting point for most Shared VPC configurations, a This [blueprint](./ilb-next-hop/) allows testing [ILB as next hop](https://cloud.google.com/load-balancing/docs/internal/ilb-next-hop-overview) using simple Linux gateway VMS between two VPCs, to emulate virtual appliances. An optional additional ILB can be enabled to test multiple load balancer configurations and hashing.
-### Calling a private Cloud Function from On-premises +### Calling a private Cloud Function from on-premises This [blueprint](./private-cloud-function-from-onprem/) shows how to invoke a [private Google Cloud Function](https://cloud.google.com/functions/docs/networking/network-settings) from the on-prem environment via a [Private Service Connect endpoint](https://cloud.google.com/vpc/docs/private-service-connect#benefits-apis).
+### Calling on-premise services through PSC and hybrid NEGs + + This [blueprint](./psc-hybrid/) shows how to privately connect to on-premise services (IP + port) from GCP, leveraging [Private Service Connect (PSC)](https://cloud.google.com/vpc/docs/private-service-connect) and [Hybrid Network Endpoint Groups](https://cloud.google.com/load-balancing/docs/negs/hybrid-neg-concepts). +
+ +![High-level diagram](diagram.png "High-level diagram") + ### Decentralized firewall management This [blueprint](./decentralized-firewall/) shows how a decentralized firewall management can be organized using the [firewall factory](../factories/net-vpc-firewall-yaml/). diff --git a/blueprints/networking/psc-hybrid/README.md b/blueprints/networking/psc-hybrid/README.md new file mode 100644 index 00000000..1b67596b --- /dev/null +++ b/blueprints/networking/psc-hybrid/README.md @@ -0,0 +1,55 @@ +# Hybrid connectivity to on-premise services through PSC + +The sample allows to connect to an on-prem service leveraging Private Service Connect (PSC). + +It creates: + +* A [producer](./psc-producer/README.md): a VPC exposing a PSC Service Attachment (SA), connecting to an internal regional TCP proxy load balancer, using a hybrid NEG backend that connects to an on-premises service (IP address + port) + +* A [consumer](./psc-consumer/README.md): a VPC with a PSC endpoint pointing to the PSC SA exposed by the producer. The endpoint is accessible by clients through a local IP address on the consumer VPC. + +![High-level diagram](diagram.png "High-level diagram") + +## Sample modules + +The blueprint makes use of the modules [psc-producer](psc-producer) and [psc-consumer](psc-consumer) contained in this folder. This is done so you can build on top of these building blocks, in order to support more complex scenarios. + +## Prerequisites + +Before applying this Terraform + +- On-premises + - Allow ingress from *35.191.0.0/16* and *130.211.0.0/22* CIDRs (for HCs) + - Allow ingress from the proxy-only subnet CIDR +- GCP + - Advertise from GCP to on-prem *35.191.0.0/16* and *130.211.0.0/22* CIDRs + - Advertise from GCP to on-prem the proxy-only subnet CIDRs + +## Relevant Links + +* [Private Service Connect](https://cloud.google.com/vpc/docs/private-service-connect) + +* [Hybrid connectivity Network Endpoint Groups](https://cloud.google.com/load-balancing/docs/negs/hybrid-neg-concepts) + +* [Regional TCP Proxy with Hybrid NEGs](https://cloud.google.com/load-balancing/docs/tcp/set-up-int-tcp-proxy-hybrid) + +* [PSC approval](https://cloud.google.com/vpc/docs/configure-private-service-connect-producer#publish-service-explicit) + + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [dest_ip_address](variables.tf#L37) | On-prem service destination IP address. | string | ✓ | | +| [prefix](variables.tf#L17) | Prefix to use for resource names. | string | ✓ | | +| [producer](variables.tf#L88) | Producer configuration. | object({…}) | ✓ | | +| [project_id](variables.tf#L22) | When referncing existing projects, the id of the project where resources will be created. | string | ✓ | | +| [region](variables.tf#L27) | Region where resources will be created. | string | ✓ | | +| [subnet_consumer](variables.tf#L98) | Consumer subnet CIDR. | string # CIDR | ✓ | | +| [zone](variables.tf#L32) | Zone where resources will be created. | string | ✓ | | +| [dest_port](variables.tf#L42) | On-prem service destination port. | string | | "80" | +| [project_create](variables.tf#L48) | Whether to automatically create a project. | bool | | false | +| [vpc_config](variables.tf#L60) | VPC and subnet ids, in case existing VPCs are used. | object({…}) | | {…} | +| [vpc_create](variables.tf#L54) | Whether to automatically create VPCs. | bool | | true | + + diff --git a/blueprints/networking/psc-hybrid/diagram.png b/blueprints/networking/psc-hybrid/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..acb36be91d4b086f054f58dd42adc94348984a44 GIT binary patch literal 58239 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 Swe3*wOIuwJU9M^o^8W$q*(6W^ literal 0 HcmV?d00001 diff --git a/blueprints/networking/psc-hybrid/main.tf b/blueprints/networking/psc-hybrid/main.tf new file mode 100644 index 00000000..21d297f0 --- /dev/null +++ b/blueprints/networking/psc-hybrid/main.tf @@ -0,0 +1,136 @@ +/** + * 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 { + prefix = coalesce(var.prefix, "") == "" ? "" : "${var.prefix}-" + project_id = ( + var.project_create + ? module.project.project_id + : var.project_id + ) + vpc_producer_id = ( + var.vpc_create + ? module.vpc_producer.network.id + : var.vpc_config["producer"]["id"] + ) + vpc_producer_main = ( + var.vpc_create + ? module.vpc_producer.subnets["${var.region}/${var.prefix}-main"].id + : var.vpc_config["producer"]["subnet_main_id"] + ) + vpc_producer_proxy = ( + var.vpc_create + ? module.vpc_producer.subnets_proxy_only["${var.region}/${var.prefix}-proxy"].id + : var.vpc_config["producer"]["subnet_proxy_id"] + ) + vpc_producer_psc = ( + var.vpc_create + ? module.vpc_producer.subnets_psc["${var.region}/${var.prefix}-psc"].id + : var.vpc_config["producer"]["subnet_psc_id"] + ) + vpc_consumer_id = ( + var.vpc_create + ? module.vpc_consumer.network.id + : var.vpc_config["consumer"]["id"] + ) + vpc_consumer_main = ( + var.vpc_create + ? module.vpc_consumer.subnets["${var.region}/${var.prefix}-consumer"].id + : var.vpc_config["consumer"]["subnet_main_id"] + ) +} + +module "project" { + source = "../../../modules/project" + name = var.project_id + project_create = var.project_create + services = [ + "compute.googleapis.com" + ] +} + +# Producer +module "vpc_producer" { + source = "../../../modules/net-vpc" + project_id = local.project_id + name = "${local.prefix}producer" + subnets = [ + { + ip_cidr_range = var.producer["subnet_main"] + name = "${var.prefix}-main" + region = var.region + secondary_ip_range = {} + } + ] + subnets_proxy_only = [ + { + ip_cidr_range = var.producer["subnet_proxy"] + name = "${local.prefix}proxy" + region = var.region + active = true + } + ] + subnets_psc = [ + { + ip_cidr_range = var.producer["subnet_psc"] + name = "${local.prefix}psc" + region = var.region + } + ] +} + +module "psc_producer" { + source = "./psc-producer" + project_id = local.project_id + name = var.prefix + dest_ip_address = var.dest_ip_address + dest_port = var.dest_port + network = local.vpc_producer_id + region = var.region + zone = var.zone + subnet = local.vpc_producer_main + subnet_proxy = local.vpc_producer_proxy + subnets_psc = [ + local.vpc_producer_psc + ] + accepted_limits = var.producer["accepted_limits"] +} + +# Consumer + +module "vpc_consumer" { + source = "../../../modules/net-vpc" + project_id = local.project_id + name = "${local.prefix}consumer" + subnets = [ + { + ip_cidr_range = var.subnet_consumer + name = "${local.prefix}consumer" + region = var.region + secondary_ip_range = {} + } + ] +} + +module "psc_consumer" { + source = "./psc-consumer" + project_id = local.project_id + name = "${local.prefix}consumer" + region = var.region + network = local.vpc_consumer_id + subnet = local.vpc_consumer_main + sa_id = module.psc_producer.service_attachment.id +} diff --git a/blueprints/networking/psc-hybrid/psc-consumer/README.md b/blueprints/networking/psc-hybrid/psc-consumer/README.md new file mode 100644 index 00000000..919e8aaf --- /dev/null +++ b/blueprints/networking/psc-hybrid/psc-consumer/README.md @@ -0,0 +1,15 @@ +# PSC Consumer + + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [name](variables.tf#L22) | Name of the resources created. | string | ✓ | | +| [network](variables.tf#L32) | Consumer network id. | string | ✓ | | +| [project_id](variables.tf#L17) | The ID of the project where this VPC will be created. | string | ✓ | | +| [region](variables.tf#L27) | Region where resources will be created. | string | ✓ | | +| [sa_id](variables.tf#L42) | PSC producer service attachment id. | string | ✓ | | +| [subnet](variables.tf#L37) | Subnetwork id where resources will be associated. | string | ✓ | | + + diff --git a/blueprints/networking/psc-hybrid/psc-consumer/main.tf b/blueprints/networking/psc-hybrid/psc-consumer/main.tf new file mode 100644 index 00000000..7967aa7a --- /dev/null +++ b/blueprints/networking/psc-hybrid/psc-consumer/main.tf @@ -0,0 +1,33 @@ +/** + * 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. + */ + +resource "google_compute_address" "psc_endpoint_address" { + name = var.name + project = var.project_id + address_type = "INTERNAL" + subnetwork = var.subnet + region = var.region +} + +resource "google_compute_forwarding_rule" "psc_ilb_consumer" { + name = var.name + project = var.project_id + region = var.region + target = var.sa_id + load_balancing_scheme = "" + network = var.network + ip_address = google_compute_address.psc_endpoint_address.id +} diff --git a/blueprints/networking/psc-hybrid/psc-consumer/variables.tf b/blueprints/networking/psc-hybrid/psc-consumer/variables.tf new file mode 100644 index 00000000..47a0f9a6 --- /dev/null +++ b/blueprints/networking/psc-hybrid/psc-consumer/variables.tf @@ -0,0 +1,45 @@ +/** + * 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 "project_id" { + description = "The ID of the project where this VPC will be created." + type = string +} + +variable "name" { + description = "Name of the resources created." + type = string +} + +variable "region" { + description = "Region where resources will be created." + type = string +} + +variable "network" { + description = "Consumer network id." + type = string +} + +variable "subnet" { + description = "Subnetwork id where resources will be associated." + type = string +} + +variable "sa_id" { + description = "PSC producer service attachment id." + type = string +} diff --git a/blueprints/networking/psc-hybrid/psc-producer/README.md b/blueprints/networking/psc-hybrid/psc-producer/README.md new file mode 100644 index 00000000..12e208e5 --- /dev/null +++ b/blueprints/networking/psc-hybrid/psc-producer/README.md @@ -0,0 +1,26 @@ +# PSC Producer + + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [accepted_limits](variables.tf#L68) | Incoming accepted projects with endpoints limit. | map(number) | ✓ | | +| [dest_ip_address](variables.tf#L57) | On-prem service destination IP address. | string | ✓ | | +| [name](variables.tf#L22) | Name of the resources created. | string | ✓ | | +| [network](variables.tf#L37) | Producer network id. | string | ✓ | | +| [project_id](variables.tf#L17) | The ID of the project where this VPC will be created. | string | ✓ | | +| [region](variables.tf#L27) | Region where resources will be created. | string | ✓ | | +| [subnet](variables.tf#L42) | Subnetwork id where resources will be associated. | string | ✓ | | +| [subnet_proxy](variables.tf#L47) | L7 Regional load balancing subnet id. | string | ✓ | | +| [subnets_psc](variables.tf#L52) | PSC NAT subnets. | list(string) | ✓ | | +| [zone](variables.tf#L32) | Zone where resources will be created. | string | ✓ | | +| [dest_port](variables.tf#L62) | On-prem service destination port. | string | | "80" | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [service_attachment](outputs.tf#L17) | The service attachment resource. | | + + diff --git a/blueprints/networking/psc-hybrid/psc-producer/main.tf b/blueprints/networking/psc-hybrid/psc-producer/main.tf new file mode 100644 index 00000000..01212bad --- /dev/null +++ b/blueprints/networking/psc-hybrid/psc-producer/main.tf @@ -0,0 +1,107 @@ +/** + * 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. + */ + +# Hybrid NEG + +resource "google_compute_network_endpoint_group" "neg" { + name = var.name + project = var.project_id + network = var.network + default_port = var.dest_port + zone = "${var.region}-${var.zone}" + network_endpoint_type = "NON_GCP_PRIVATE_IP_PORT" +} + +resource "google_compute_network_endpoint" "endpoint" { + project = var.project_id + network_endpoint_group = google_compute_network_endpoint_group.neg.name + port = var.dest_port + ip_address = var.dest_ip_address + zone = "${var.region}-${var.zone}" +} + +# TCP Proxy ILB + +resource "google_compute_region_health_check" "health_check" { + name = var.name + project = var.project_id + region = var.region + timeout_sec = 1 + check_interval_sec = 1 + + tcp_health_check { + port = var.dest_port + } +} + +resource "google_compute_region_backend_service" "backend_service" { + name = var.name + project = var.project_id + region = var.region + health_checks = [google_compute_region_health_check.health_check.id] + load_balancing_scheme = "INTERNAL_MANAGED" + protocol = "TCP" + + backend { + group = google_compute_network_endpoint_group.neg.self_link + balancing_mode = "CONNECTION" + failover = false + capacity_scaler = 1.0 + max_connections = 100 + } +} + +resource "google_compute_region_target_tcp_proxy" "target_proxy" { + provider = google-beta + name = var.name + region = var.region + project = var.project_id + backend_service = google_compute_region_backend_service.backend_service.id +} + +resource "google_compute_forwarding_rule" "forwarding_rule" { + provider = google-beta + name = var.name + project = var.project_id + region = var.region + ip_protocol = "TCP" + load_balancing_scheme = "INTERNAL_MANAGED" + port_range = var.dest_port + target = google_compute_region_target_tcp_proxy.target_proxy.id + network = var.network + subnetwork = var.subnet + network_tier = "PREMIUM" +} + +# PSC Service Attachment + +resource "google_compute_service_attachment" "service_attachment" { + name = var.name + project = var.project_id + region = var.region + enable_proxy_protocol = false + connection_preference = "ACCEPT_MANUAL" + nat_subnets = var.subnets_psc + target_service = google_compute_forwarding_rule.forwarding_rule.id + + dynamic "consumer_accept_lists" { + for_each = var.accepted_limits + content { + project_id_or_num = consumer_accept_lists.key + connection_limit = consumer_accept_lists.value + } + } +} diff --git a/blueprints/networking/psc-hybrid/psc-producer/outputs.tf b/blueprints/networking/psc-hybrid/psc-producer/outputs.tf new file mode 100644 index 00000000..6539bcd7 --- /dev/null +++ b/blueprints/networking/psc-hybrid/psc-producer/outputs.tf @@ -0,0 +1,20 @@ +/** + * 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. + */ + +output "service_attachment" { + description = "The service attachment resource." + value = google_compute_service_attachment.service_attachment +} diff --git a/blueprints/networking/psc-hybrid/psc-producer/variables.tf b/blueprints/networking/psc-hybrid/psc-producer/variables.tf new file mode 100644 index 00000000..c085ecdc --- /dev/null +++ b/blueprints/networking/psc-hybrid/psc-producer/variables.tf @@ -0,0 +1,71 @@ +/** + * 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 "project_id" { + description = "The ID of the project where this VPC will be created." + type = string +} + +variable "name" { + description = "Name of the resources created." + type = string +} + +variable "region" { + description = "Region where resources will be created." + type = string +} + +variable "zone" { + description = "Zone where resources will be created." + type = string +} + +variable "network" { + description = "Producer network id." + type = string +} + +variable "subnet" { + description = "Subnetwork id where resources will be associated." + type = string +} + +variable "subnet_proxy" { + description = "L7 Regional load balancing subnet id." + type = string +} + +variable "subnets_psc" { + description = "PSC NAT subnets." + type = list(string) +} + +variable "dest_ip_address" { + description = "On-prem service destination IP address." + type = string +} + +variable "dest_port" { + description = "On-prem service destination port." + type = string + default = "80" +} + +variable "accepted_limits" { + description = "Incoming accepted projects with endpoints limit." + type = map(number) +} diff --git a/blueprints/networking/psc-hybrid/variables.tf b/blueprints/networking/psc-hybrid/variables.tf new file mode 100644 index 00000000..012e26d0 --- /dev/null +++ b/blueprints/networking/psc-hybrid/variables.tf @@ -0,0 +1,101 @@ +/** + * 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 "prefix" { + description = "Prefix to use for resource names." + type = string +} + +variable "project_id" { + description = "When referncing existing projects, the id of the project where resources will be created." + type = string +} + +variable "region" { + description = "Region where resources will be created." + type = string +} + +variable "zone" { + description = "Zone where resources will be created." + type = string +} + +variable "dest_ip_address" { + description = "On-prem service destination IP address." + type = string +} + +variable "dest_port" { + description = "On-prem service destination port." + type = string + default = "80" +} + +variable "project_create" { + description = "Whether to automatically create a project." + type = bool + default = false +} + +variable "vpc_create" { + description = "Whether to automatically create VPCs." + type = bool + default = true +} + +variable "vpc_config" { + description = "VPC and subnet ids, in case existing VPCs are used." + type = object({ + producer = object({ + id = string + subnet_main_id = string + subnet_proxy_id = string + subnet_psc_id = string + }) + consumer = object({ + id = string + subnet_main_id = string + }) + }) + default = { + producer = { + id = "xxx" + subnet_main_id = "xxx" + subnet_proxy_id = "xxx" + subnet_psc_id = "xxx" + } + consumer = { + id = "xxx" + subnet_main_id = "xxx" + } + } +} + +variable "producer" { + description = "Producer configuration." + type = object({ + subnet_main = string # CIDR + subnet_proxy = string # CIDR + subnet_psc = string # CIDR + accepted_limits = map(number) # Accepted project ids => PSC endpoint limit + }) +} + +variable "subnet_consumer" { + description = "Consumer subnet CIDR." + type = string # CIDR +}