From 2ab64446a96702ff05230fd164c273ff261fdc32 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 4 Feb 2021 12:12:56 +0100 Subject: [PATCH] Update hub and spoke peering to optionally create project (#195) * optionally create project, add vm in hub, export routes to gke peering * fix typo in unrelated module * update README * update README * update unrelated README for lint * fix test --- .../README.md | 2 +- .../variables.tf | 2 +- networking/hub-and-spoke-peering/README.md | 7 +- networking/hub-and-spoke-peering/main.tf | 127 +++++++++++++----- networking/hub-and-spoke-peering/outputs.tf | 5 + networking/hub-and-spoke-peering/variables.tf | 26 +++- .../hub_and_spoke_peering/fixture/main.tf | 5 + .../hub_and_spoke_peering/test_plan.py | 4 +- 8 files changed, 135 insertions(+), 43 deletions(-) diff --git a/cloud-operations/asset-inventory-feed-remediation/README.md b/cloud-operations/asset-inventory-feed-remediation/README.md index bad14a31..5d584935 100644 --- a/cloud-operations/asset-inventory-feed-remediation/README.md +++ b/cloud-operations/asset-inventory-feed-remediation/README.md @@ -62,7 +62,7 @@ Run the `subscription_pull` command until it returns nothing, then run the follo | project_id | Project id that references existing project. | string | ✓ | | | *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | | *name* | Arbitrary string used to name created resources. | string | | asset-feed | -| *project_create* | Create project instead ofusing an existing one. | bool | | false | +| *project_create* | Create project instead of using an existing one. | bool | | false | | *region* | Compute region used in the example. | string | | europe-west1 | ## Outputs diff --git a/cloud-operations/asset-inventory-feed-remediation/variables.tf b/cloud-operations/asset-inventory-feed-remediation/variables.tf index 93b2163d..a764da76 100644 --- a/cloud-operations/asset-inventory-feed-remediation/variables.tf +++ b/cloud-operations/asset-inventory-feed-remediation/variables.tf @@ -27,7 +27,7 @@ variable "name" { } variable "project_create" { - description = "Create project instead ofusing an existing one." + description = "Create project instead of using an existing one." type = bool default = false } diff --git a/networking/hub-and-spoke-peering/README.md b/networking/hub-and-spoke-peering/README.md index 85599bf5..058063f9 100644 --- a/networking/hub-and-spoke-peering/README.md +++ b/networking/hub-and-spoke-peering/README.md @@ -41,7 +41,7 @@ gcloud container clusters get-credentials cluster-1 --zone europe-west1-b kubectl get all ``` -The next step is to edit the peering towards the GKE master tenant VPC, and enable export routes. You can do it directly in Terraform with the GKE module `peering_config' variable, via gcloud, or on the cloud ccnsole. We're leaving it as an option, since one of the goals of this example is to allow testing both working and non-working configurations. +The example configures the peering with the GKE master VPC to export routes for you, so that VPN routes are passed through the peering. You can diable by hand in the console or by editing the `peering_config' variable in the cluster module, to test non-working configurations or switch to using the [GKE proxy](https://cloud.google.com/solutions/creating-kubernetes-engine-private-clusters-with-net-proxies). ### Export routes via Terraform @@ -82,15 +82,18 @@ The VPN used to connect the GKE masters VPC does not account for HA, upgrading t | name | description | type | required | default | |---|---|:---: |:---:|:---:| -| project_id | Project id for all resources. | string | ✓ | | +| project_id | Project id used for all resources. | string | ✓ | | | *ip_ranges* | IP CIDR ranges. | map(string) | | ... | | *ip_secondary_ranges* | Secondary IP CIDR ranges. | map(string) | | ... | +| *prefix* | Arbitrary string used to prefix resource names. | string | | null | | *private_service_ranges* | Private service IP CIDR ranges. | map(string) | | ... | +| *project_create* | Set to non null if project needs to be created. | object({...}) | | ... | | *region* | VPC region. | string | | europe-west1 | ## Outputs | name | description | sensitive | |---|---|:---:| +| project | Project id. | | | vms | GCE VMs. | | diff --git a/networking/hub-and-spoke-peering/main.tf b/networking/hub-and-spoke-peering/main.tf index a84ddef6..569abfcf 100644 --- a/networking/hub-and-spoke-peering/main.tf +++ b/networking/hub-and-spoke-peering/main.tf @@ -13,7 +13,9 @@ # limitations under the License. locals { + prefix = var.prefix != null && var.prefix != "" ? "${var.prefix}-" : "" vm-instances = concat( + module.vm-hub.instances, module.vm-spoke-1.instances, module.vm-spoke-2.instances ) @@ -23,24 +25,54 @@ locals { ]) } +############################################################################### +# project # +############################################################################### + +module "project" { + source = "../../modules/project" + project_create = var.project_create != null + billing_account = try(var.project_create.billing_account, null) + oslogin = try(var.project_create.oslogin, null) + parent = try(var.project_create.parent, null) + name = var.project_id + services = [ + "compute.googleapis.com", + "container.googleapis.com" + ] + service_config = { + disable_on_destroy = false, + disable_dependent_services = false + } +} + ################################################################################ # Hub networking # ################################################################################ module "vpc-hub" { source = "../../modules/net-vpc" - project_id = var.project_id - name = "hub" + project_id = module.project.project_id + name = "${local.prefix}hub" subnets = [ { ip_cidr_range = var.ip_ranges.hub - name = "hub-default" + name = "${local.prefix}hub-1" region = var.region secondary_ip_range = {} } ] } +module "nat-hub" { + source = "../../modules/net-cloudnat" + project_id = module.project.project_id + region = var.region + name = "${local.prefix}hub" + router_name = "${local.prefix}hub" + router_network = module.vpc-hub.self_link +} + module "vpc-hub-firewall" { source = "../../modules/net-vpc-firewall" project_id = var.project_id @@ -55,12 +87,12 @@ module "vpc-hub-firewall" { module "vpc-spoke-1" { source = "../../modules/net-vpc" - project_id = var.project_id - name = "spoke-1" + project_id = module.project.project_id + name = "${local.prefix}spoke-1" subnets = [ { ip_cidr_range = var.ip_ranges.spoke-1 - name = "spoke-1-default" + name = "${local.prefix}spoke-1-1" region = var.region secondary_ip_range = {} } @@ -69,7 +101,7 @@ module "vpc-spoke-1" { module "vpc-spoke-1-firewall" { source = "../../modules/net-vpc-firewall" - project_id = var.project_id + project_id = module.project.project_id network = module.vpc-spoke-1.name admin_ranges_enabled = true admin_ranges = values(var.ip_ranges) @@ -77,10 +109,10 @@ module "vpc-spoke-1-firewall" { module "nat-spoke-1" { source = "../../modules/net-cloudnat" - project_id = var.project_id + project_id = module.project.project_id region = var.region - name = "spoke-1" - router_name = "spoke-1" + name = "${local.prefix}spoke-1" + router_name = "${local.prefix}spoke-1" router_network = module.vpc-spoke-1.self_link } @@ -98,12 +130,12 @@ module "hub-to-spoke-1-peering" { module "vpc-spoke-2" { source = "../../modules/net-vpc" - project_id = var.project_id - name = "spoke-2" + project_id = module.project.project_id + name = "${local.prefix}spoke-2" subnets = [ { ip_cidr_range = var.ip_ranges.spoke-2 - name = "spoke-2-default" + name = "${local.prefix}spoke-2-1" region = var.region secondary_ip_range = { pods = var.ip_secondary_ranges.spoke-2-pods @@ -115,7 +147,7 @@ module "vpc-spoke-2" { module "vpc-spoke-2-firewall" { source = "../../modules/net-vpc-firewall" - project_id = var.project_id + project_id = module.project.project_id network = module.vpc-spoke-2.name admin_ranges_enabled = true admin_ranges = values(var.ip_ranges) @@ -123,10 +155,10 @@ module "vpc-spoke-2-firewall" { module "nat-spoke-2" { source = "../../modules/net-cloudnat" - project_id = var.project_id + project_id = module.project.project_id region = var.region - name = "spoke-2" - router_name = "spoke-2" + name = "${local.prefix}spoke-2" + router_name = "${local.prefix}spoke-2" router_network = module.vpc-spoke-2.self_link } @@ -143,14 +175,32 @@ module "hub-to-spoke-2-peering" { # Test VMs # ################################################################################ +module "vm-hub" { + source = "../../modules/compute-vm" + project_id = module.project.project_id + region = var.region + name = "${local.prefix}hub" + network_interfaces = [{ + network = module.vpc-hub.self_link + subnetwork = module.vpc-hub.subnet_self_links["${var.region}/${local.prefix}hub-1"] + nat = false + addresses = null + alias_ips = null + }] + metadata = { startup-script = local.vm-startup-script } + service_account = module.service-account-gce.email + service_account_scopes = ["https://www.googleapis.com/auth/cloud-platform"] + tags = ["ssh"] +} + module "vm-spoke-1" { source = "../../modules/compute-vm" - project_id = var.project_id + project_id = module.project.project_id region = var.region - name = "spoke-1-test" + name = "${local.prefix}spoke-1" network_interfaces = [{ network = module.vpc-spoke-1.self_link - subnetwork = module.vpc-spoke-1.subnet_self_links["${var.region}/spoke-1-default"] + subnetwork = module.vpc-spoke-1.subnet_self_links["${var.region}/${local.prefix}spoke-1-1"] nat = false addresses = null alias_ips = null @@ -163,12 +213,12 @@ module "vm-spoke-1" { module "vm-spoke-2" { source = "../../modules/compute-vm" - project_id = var.project_id + project_id = module.project.project_id region = var.region - name = "spoke-2-test" + name = "${local.prefix}spoke-2" network_interfaces = [{ network = module.vpc-spoke-2.self_link - subnetwork = module.vpc-spoke-2.subnet_self_links["${var.region}/spoke-2-default"] + subnetwork = module.vpc-spoke-2.subnet_self_links["${var.region}/${local.prefix}spoke-2-1"] nat = false addresses = null alias_ips = null @@ -181,8 +231,8 @@ module "vm-spoke-2" { module "service-account-gce" { source = "../../modules/iam-service-account" - project_id = var.project_id - name = "gce-test" + project_id = module.project.project_id + name = "${local.prefix}gce-test" iam_project_roles = { (var.project_id) = [ "roles/container.developer", @@ -198,11 +248,11 @@ module "service-account-gce" { module "cluster-1" { source = "../../modules/gke-cluster" - name = "cluster-1" - project_id = var.project_id + name = "${local.prefix}cluster-1" + project_id = module.project.project_id location = "${var.region}-b" network = module.vpc-spoke-2.self_link - subnetwork = module.vpc-spoke-2.subnet_self_links["${var.region}/spoke-2-default"] + subnetwork = module.vpc-spoke-2.subnet_self_links["${var.region}/${local.prefix}spoke-2-1"] secondary_range_pods = "pods" secondary_range_services = "services" default_max_pods_per_node = 32 @@ -217,12 +267,17 @@ module "cluster-1" { enable_private_endpoint = true master_ipv4_cidr_block = var.private_service_ranges.spoke-2-cluster-1 } + peering_config = { + export_routes = true + import_routes = false + project_id = null + } } module "cluster-1-nodepool-1" { source = "../../modules/gke-nodepool" - name = "nodepool-1" - project_id = var.project_id + name = "${local.prefix}nodepool-1" + project_id = module.project.project_id location = module.cluster-1.location cluster_name = module.cluster-1.name node_service_account = module.service-account-gke-node.email @@ -233,8 +288,8 @@ module "cluster-1-nodepool-1" { module "service-account-gke-node" { source = "../../modules/iam-service-account" - project_id = var.project_id - name = "gke-node" + project_id = module.project.project_id + name = "${local.prefix}gke-node" iam_project_roles = { (var.project_id) = [ "roles/logging.logWriter", "roles/monitoring.metricWriter", @@ -248,10 +303,10 @@ module "service-account-gke-node" { module "vpn-hub" { source = "../../modules/net-vpn-static" - project_id = var.project_id + project_id = module.project.project_id region = var.region network = module.vpc-hub.name - name = "hub" + name = "${local.prefix}hub" remote_ranges = values(var.private_service_ranges) tunnels = { spoke-2 = { @@ -265,10 +320,10 @@ module "vpn-hub" { module "vpn-spoke-2" { source = "../../modules/net-vpn-static" - project_id = var.project_id + project_id = module.project.project_id region = var.region network = module.vpc-spoke-2.name - name = "spoke-2" + name = "${local.prefix}spoke-2" # use an aggregate of the remote ranges, so as to be less specific than the # routes exchanged via peering remote_ranges = ["10.0.0.0/8"] diff --git a/networking/hub-and-spoke-peering/outputs.tf b/networking/hub-and-spoke-peering/outputs.tf index 933d03cf..1a0a9098 100644 --- a/networking/hub-and-spoke-peering/outputs.tf +++ b/networking/hub-and-spoke-peering/outputs.tf @@ -12,6 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +output "project" { + description = "Project id." + value = module.project.project_id +} + output "vms" { description = "GCE VMs." value = { diff --git a/networking/hub-and-spoke-peering/variables.tf b/networking/hub-and-spoke-peering/variables.tf index 3f1375b2..a8cebba5 100644 --- a/networking/hub-and-spoke-peering/variables.tf +++ b/networking/hub-and-spoke-peering/variables.tf @@ -31,6 +31,12 @@ variable "ip_secondary_ranges" { } } +variable "prefix" { + description = "Arbitrary string used to prefix resource names." + type = string + default = null +} + variable "private_service_ranges" { description = "Private service IP CIDR ranges." type = map(string) @@ -39,8 +45,26 @@ variable "private_service_ranges" { } } +variable "project_create" { + description = "Set to non null if project needs to be created." + type = object({ + billing_account = string + oslogin = bool + parent = string + }) + default = null + validation { + condition = ( + var.project_create == null + ? true + : can(regex("(organizations|folders)/[0-9]+", var.project_create.parent)) + ) + error_message = "Project parent must be of the form folders/folder_id or organizations/organization_id." + } +} + variable "project_id" { - description = "Project id for all resources." + description = "Project id used for all resources." type = string } diff --git a/tests/networking/hub_and_spoke_peering/fixture/main.tf b/tests/networking/hub_and_spoke_peering/fixture/main.tf index a1eb2a84..97820423 100644 --- a/tests/networking/hub_and_spoke_peering/fixture/main.tf +++ b/tests/networking/hub_and_spoke_peering/fixture/main.tf @@ -16,5 +16,10 @@ module "test" { source = "../../../../networking/hub-and-spoke-peering" + project_create = { + billing_account = "123456-123456-123456" + oslogin = true + parent = "folders/123456789" + } project_id = var.project_id } diff --git a/tests/networking/hub_and_spoke_peering/test_plan.py b/tests/networking/hub_and_spoke_peering/test_plan.py index 1ce41422..52ce2674 100644 --- a/tests/networking/hub_and_spoke_peering/test_plan.py +++ b/tests/networking/hub_and_spoke_peering/test_plan.py @@ -23,5 +23,5 @@ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') def test_resources(e2e_plan_runner): "Test that plan works and the numbers of resources is as expected." modules, resources = e2e_plan_runner(FIXTURES_DIR) - assert len(modules) == 18 - assert len(resources) == 53 + assert len(modules) == 21 + assert len(resources) == 61