modules/network/vpc/main.tf (191 lines of code) (raw):

/** * 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 "terraform_data" "secondary_ranges_validation" { lifecycle { precondition { condition = !(length(var.secondary_ranges) > 0 && length(var.secondary_ranges_list) > 0) error_message = "Only one of var.secondary_ranges or var.secondary_ranges_list should be specified" } } } locals { # This label allows for billing report tracking based on module. labels = merge(var.labels, { ghpc_module = "vpc", ghpc_role = "network" }) } locals { autoname = replace(var.deployment_name, "_", "-") network_name = var.network_name == null ? "${local.autoname}-net" : var.network_name subnetwork_name = var.subnetwork_name == null ? "${local.autoname}-primary-subnet" : var.subnetwork_name # define a default subnetwork for cases in which no explicit subnetworks are # defined in var.subnetworks default_primary_subnetwork_cidr_block = cidrsubnet(var.network_address_range, var.default_primary_subnetwork_size, 0) default_primary_subnetwork = { subnet_name = local.subnetwork_name subnet_ip = local.default_primary_subnetwork_cidr_block subnet_region = var.region subnet_private_access = true subnet_flow_logs = false description = "primary subnetwork in ${local.network_name}" purpose = null role = null } # Identify user-supplied primary subnetwork # (1) explicit var.subnetworks[0] # (2) implicit local default subnetwork input_primary_subnetwork = coalesce(try(var.subnetworks[0], null), local.default_primary_subnetwork) # Identify user-supplied additional subnetworks # (1) explicit var.subnetworks[1:end] # (2) empty list input_additional_subnetworks = try(slice(var.subnetworks, 1, length(var.subnetworks)), []) # at this point we have constructed a list of subnetworks but need to extract # user-provided CIDR blocks or calculate them from user-provided new_bits # after we complete deprecation, local.all_subnetworks can be replaced with # var.subnetworks (or local.default_primary_subnetwork if that is null) input_subnetworks = concat([local.input_primary_subnetwork], local.input_additional_subnetworks) subnetworks_cidr_blocks = try( local.input_subnetworks[*]["subnet_ip"], cidrsubnets(var.network_address_range, local.input_subnetworks[*]["new_bits"]...) ) # merge in the CIDR blocks (even when already there) and remove new_bits subnetworks = [for i, subnet in local.input_subnetworks : merge({ for k, v in subnet : k => v if k != "new_bits" }, { "subnet_ip" = local.subnetworks_cidr_blocks[i] }) ] # gather the unique regions for purposes of creating Router/NAT cloud_router_regions = var.enable_cloud_router ? distinct([for subnet in local.subnetworks : subnet.subnet_region]) : [] cloud_nat_regions = var.enable_cloud_nat ? local.cloud_router_regions : [] # this comprehension should have 1 and only 1 match output_primary_subnetwork = one([for k, v in module.vpc.subnets : v if k == "${local.subnetworks[0].subnet_region}/${local.subnetworks[0].subnet_name}"]) output_primary_subnetwork_name = local.output_primary_subnetwork.name output_primary_subnetwork_self_link = local.output_primary_subnetwork.self_link output_primary_subnetwork_ip_cidr_range = local.output_primary_subnetwork.ip_cidr_range iap_ports = distinct(concat(compact([ var.enable_iap_rdp_ingress ? "3389" : "", var.enable_iap_ssh_ingress ? "22" : "", var.enable_iap_winrm_ingress ? "5986" : "", ]), var.extra_iap_ports)) firewall_log_api_values = { "DISABLE_LOGGING" = null "INCLUDE_ALL_METADATA" = { metadata = "INCLUDE_ALL_METADATA" }, "EXCLUDE_ALL_METADATA" = { metadata = "EXCLUDE_ALL_METADATA" }, } firewall_log_config = lookup(local.firewall_log_api_values, var.firewall_log_config, null) allow_iap_ingress = { name = "${local.network_name}-fw-allow-iap-ingress" description = "allow TCP access via Identity-Aware Proxy" direction = "INGRESS" priority = null ranges = ["35.235.240.0/20"] source_tags = null source_service_accounts = null target_tags = null target_service_accounts = null allow = [{ protocol = "tcp" ports = local.iap_ports }] deny = [] log_config = local.firewall_log_config } allow_ssh_ingress = { name = "${local.network_name}-fw-allow-ssh-ingress" description = "allow SSH access" direction = "INGRESS" priority = null ranges = var.allowed_ssh_ip_ranges source_tags = null source_service_accounts = null target_tags = null target_service_accounts = null allow = [{ protocol = "tcp" ports = ["22"] }] deny = [] log_config = local.firewall_log_config } allow_internal_traffic = { name = "${local.network_name}-fw-allow-internal-traffic" priority = null description = "allow traffic between nodes of this VPC" direction = "INGRESS" ranges = [var.network_address_range] source_tags = null source_service_accounts = null target_tags = null target_service_accounts = null allow = [{ protocol = "tcp" ports = ["0-65535"] }, { protocol = "udp" ports = ["0-65535"] }, { protocol = "icmp" ports = null }, ] deny = [] log_config = local.firewall_log_config } firewall_rules = concat( var.firewall_rules, length(var.allowed_ssh_ip_ranges) > 0 ? [local.allow_ssh_ingress] : [], var.enable_internal_traffic ? [local.allow_internal_traffic] : [], length(local.iap_ports) > 0 ? [local.allow_iap_ingress] : [] ) secondary_ranges_map = { for secondary_range in var.secondary_ranges_list : secondary_range.subnetwork_name => secondary_range.ranges } } module "vpc" { source = "terraform-google-modules/network/google" version = "~> 10.0" network_name = local.network_name project_id = var.project_id auto_create_subnetworks = false subnets = local.subnetworks secondary_ranges = length(local.secondary_ranges_map) > 0 ? local.secondary_ranges_map : var.secondary_ranges routing_mode = var.network_routing_mode mtu = var.mtu description = var.network_description shared_vpc_host = var.shared_vpc_host delete_default_internet_gateway_routes = var.delete_default_internet_gateway_routes firewall_rules = local.firewall_rules network_profile = var.network_profile } resource "terraform_data" "cloud_nat_validation" { lifecycle { precondition { condition = var.enable_cloud_router == true || var.enable_cloud_nat == false error_message = <<-EOD "Cannot have Cloud NAT without a Cloud Router. If you desire Cloud NAT functionality please set `enable_cloud_router` to true." EOD } } } # This use of the module may appear odd when var.ips_per_nat = 0. The module # will be called for all regions with subnetworks but names will be set to the # empty list. This is a perfectly valid value (the default!). In this scenario, # no IP addresses are created and all module outputs are empty lists. # # https://github.com/terraform-google-modules/terraform-google-address/blob/v3.1.1/variables.tf#L27 # https://github.com/terraform-google-modules/terraform-google-address/blob/v3.1.1/outputs.tf module "nat_ip_addresses" { source = "terraform-google-modules/address/google" version = "~> 4.1" depends_on = [terraform_data.cloud_nat_validation] for_each = toset(local.cloud_nat_regions) project_id = var.project_id region = each.value # an external, regional (not global) IP address is suited for a regional NAT address_type = "EXTERNAL" global = false labels = local.labels names = [for idx in range(var.ips_per_nat) : "${local.network_name}-nat-ips-${each.value}-${idx}"] } module "cloud_router" { source = "terraform-google-modules/cloud-router/google" version = "~> 6.0" depends_on = [terraform_data.cloud_nat_validation] for_each = toset(local.cloud_router_regions) project = var.project_id name = "${local.network_name}-router" region = each.value network = module.vpc.network_name # in scenario with no NAT IPs, no NAT is created even if router is created # https://github.com/terraform-google-modules/terraform-google-cloud-router/blob/v2.0.0/nat.tf#L18-L20 nats = length(module.nat_ip_addresses[each.value].self_links) == 0 ? [] : [ { name : "cloud-nat-${each.value}", nat_ips : module.nat_ip_addresses[each.value].self_links }, ] }