Merge branch 'master' into nstrelkova-fast-fix-ew3
This commit is contained in:
commit
77736c13e3
|
@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
### BLUEPRINTS
|
||||
|
||||
- [[#1024](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1024)] Fix Apigee PAYG environment node config ([g-greatdevaks](https://github.com/g-greatdevaks)) <!-- 2022-11-29 13:08:12+00:00 -->
|
||||
- [[#1019](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1019)] Added endpoint attachments to Apigee module ([apichick](https://github.com/apichick)) <!-- 2022-11-28 16:53:27+00:00 -->
|
||||
- [[#1000](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1000)] ADFS blueprint fixes ([apichick](https://github.com/apichick)) <!-- 2022-11-28 12:43:33+00:00 -->
|
||||
- [[#1001](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1001)] Binauthz blueprint fixes related to project creation ([apichick](https://github.com/apichick)) <!-- 2022-11-28 11:45:11+00:00 -->
|
||||
|
@ -114,6 +115,12 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
### MODULES
|
||||
|
||||
- [[#1026](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1026)] add lifecycle ignore_changes for apigee PAYG env ([g-greatdevaks](https://github.com/g-greatdevaks)) <!-- 2022-12-01 10:38:19+00:00 -->
|
||||
- [[#1031](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1031)] Fix default_rules_config description in firewall module ([ludoo](https://github.com/ludoo)) <!-- 2022-12-01 09:04:13+00:00 -->
|
||||
- [[#1028](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1028)] **incompatible change:** Align rest of vpn modules with #1027 ([juliocc](https://github.com/juliocc)) <!-- 2022-11-30 15:37:24+00:00 -->
|
||||
- [[#1027](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1027)] **incompatible change:** Update VPN-HA module to tf1.3 ([juliocc](https://github.com/juliocc)) <!-- 2022-11-30 10:50:06+00:00 -->
|
||||
- [[#1025](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1025)] fix apigee PAYG env node config dynamic block ([g-greatdevaks](https://github.com/g-greatdevaks)) <!-- 2022-11-30 04:53:43+00:00 -->
|
||||
- [[#1024](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1024)] Fix Apigee PAYG environment node config ([g-greatdevaks](https://github.com/g-greatdevaks)) <!-- 2022-11-29 13:08:12+00:00 -->
|
||||
- [[#1019](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1019)] Added endpoint attachments to Apigee module ([apichick](https://github.com/apichick)) <!-- 2022-11-28 16:53:27+00:00 -->
|
||||
- [[#1018](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1018)] Apigee instance doc examples ([danistrebel](https://github.com/danistrebel)) <!-- 2022-11-28 11:10:12+00:00 -->
|
||||
- [[#1016](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1016)] Fix memory/cpu typo in gke cluster module ([joeheaton](https://github.com/joeheaton)) <!-- 2022-11-27 17:29:26+00:00 -->
|
||||
|
@ -191,6 +198,8 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
### TOOLS
|
||||
|
||||
- [[#1022](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1022)] Replace `set-output` with env variable and remove single quotes on labels ([kunzese](https://github.com/kunzese)) <!-- 2022-11-29 08:57:44+00:00 -->
|
||||
- [[#1021](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1021)] Add OpenContainers annotations to published container images ([kunzese](https://github.com/kunzese)) <!-- 2022-11-29 08:11:53+00:00 -->
|
||||
- [[#1017](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1017)] Fix auto-labeling ([ludoo](https://github.com/ludoo)) <!-- 2022-11-28 14:00:32+00:00 -->
|
||||
- [[#1013](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1013)] Update labeler.yml ([ludoo](https://github.com/ludoo)) <!-- 2022-11-25 13:27:47+00:00 -->
|
||||
- [[#1010](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/1010)] Enforce nonempty descriptions ending in a dot ([juliocc](https://github.com/juliocc)) <!-- 2022-11-25 09:15:29+00:00 -->
|
||||
|
|
|
@ -59,15 +59,15 @@ Do the following to verify that everything works as expected.
|
|||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [envgroups](variables.tf#L24) | Environment groups (NAME => [HOSTNAMES]). | <code>map(list(string))</code> | ✓ | |
|
||||
| [environments](variables.tf#L30) | Environments. | <code title="map(object({ display_name = optional(string) description = optional(string) node_config = optional(object({ min_node_count = optional(number) max_node_count = optional(number) current_aggregate_node_count = number })) iam = optional(map(list(string))) envgroups = list(string) }))">map(object({…}))</code> | ✓ | |
|
||||
| [instances](variables.tf#L46) | Instance. | <code title="map(object({ display_name = optional(string) description = optional(string) region = string environments = list(string) psa_ip_cidr_range = string disk_encryption_key = optional(string) consumer_accept_list = optional(list(string)) }))">map(object({…}))</code> | ✓ | |
|
||||
| [project_id](variables.tf#L92) | Project ID. | <code>string</code> | ✓ | |
|
||||
| [psc_config](variables.tf#L98) | PSC configuration. | <code>map(string)</code> | ✓ | |
|
||||
| [environments](variables.tf#L30) | Environments. | <code title="map(object({ display_name = optional(string) description = optional(string) node_config = optional(object({ min_node_count = optional(number) max_node_count = optional(number) })) iam = optional(map(list(string))) envgroups = list(string) }))">map(object({…}))</code> | ✓ | |
|
||||
| [instances](variables.tf#L45) | Instance. | <code title="map(object({ display_name = optional(string) description = optional(string) region = string environments = list(string) psa_ip_cidr_range = string disk_encryption_key = optional(string) consumer_accept_list = optional(list(string)) }))">map(object({…}))</code> | ✓ | |
|
||||
| [project_id](variables.tf#L91) | Project ID. | <code>string</code> | ✓ | |
|
||||
| [psc_config](variables.tf#L97) | PSC configuration. | <code>map(string)</code> | ✓ | |
|
||||
| [datastore_name](variables.tf#L17) | Datastore. | <code>string</code> | | <code>"gcs"</code> |
|
||||
| [organization](variables.tf#L60) | Apigee organization. | <code title="object({ display_name = optional(string, "Apigee organization created by tf module") description = optional(string, "Apigee organization created by tf module") authorized_network = optional(string, "vpc") runtime_type = optional(string, "CLOUD") billing_type = optional(string) database_encryption_key = optional(string) analytics_region = optional(string, "europe-west1") })">object({…})</code> | | <code title="{ }">{…}</code> |
|
||||
| [path](variables.tf#L76) | Bucket path. | <code>string</code> | | <code>"/analytics"</code> |
|
||||
| [project_create](variables.tf#L83) | Parameters for the creation of the new project. | <code title="object({ billing_account_id = string parent = string })">object({…})</code> | | <code>null</code> |
|
||||
| [vpc_create](variables.tf#L104) | Boolean flag indicating whether the VPC should be created or not. | <code>bool</code> | | <code>true</code> |
|
||||
| [organization](variables.tf#L59) | Apigee organization. | <code title="object({ display_name = optional(string, "Apigee organization created by tf module") description = optional(string, "Apigee organization created by tf module") authorized_network = optional(string, "vpc") runtime_type = optional(string, "CLOUD") billing_type = optional(string) database_encryption_key = optional(string) analytics_region = optional(string, "europe-west1") })">object({…})</code> | | <code title="{ }">{…}</code> |
|
||||
| [path](variables.tf#L75) | Bucket path. | <code>string</code> | | <code>"/analytics"</code> |
|
||||
| [project_create](variables.tf#L82) | Parameters for the creation of the new project. | <code title="object({ billing_account_id = string parent = string })">object({…})</code> | | <code>null</code> |
|
||||
| [vpc_create](variables.tf#L103) | Boolean flag indicating whether the VPC should be created or not. | <code>bool</code> | | <code>true</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -869,9 +869,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/dezalgo": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
|
||||
"integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
|
||||
"integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
|
||||
"dependencies": {
|
||||
"asap": "^2.0.0",
|
||||
"wrappy": "1"
|
||||
|
@ -1224,30 +1224,19 @@
|
|||
}
|
||||
},
|
||||
"node_modules/formidable": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz",
|
||||
"integrity": "sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz",
|
||||
"integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==",
|
||||
"dependencies": {
|
||||
"dezalgo": "1.0.3",
|
||||
"hexoid": "1.0.0",
|
||||
"once": "1.4.0",
|
||||
"qs": "6.9.3"
|
||||
"dezalgo": "^1.0.4",
|
||||
"hexoid": "^1.0.0",
|
||||
"once": "^1.4.0",
|
||||
"qs": "^6.11.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://ko-fi.com/tunnckoCore/commissions"
|
||||
}
|
||||
},
|
||||
"node_modules/formidable/node_modules/qs": {
|
||||
"version": "6.9.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz",
|
||||
"integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
|
@ -3827,9 +3816,9 @@
|
|||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
|
||||
},
|
||||
"dezalgo": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
|
||||
"integrity": "sha512-K7i4zNfT2kgQz3GylDw40ot9GAE47sFZ9EXHFSPP6zONLgH6kWXE0KWJchkbQJLBkRazq4APwZ4OwiFFlT95OQ==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
|
||||
"integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
|
||||
"requires": {
|
||||
"asap": "^2.0.0",
|
||||
"wrappy": "1"
|
||||
|
@ -4106,21 +4095,14 @@
|
|||
}
|
||||
},
|
||||
"formidable": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz",
|
||||
"integrity": "sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz",
|
||||
"integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==",
|
||||
"requires": {
|
||||
"dezalgo": "1.0.3",
|
||||
"hexoid": "1.0.0",
|
||||
"once": "1.4.0",
|
||||
"qs": "6.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"qs": {
|
||||
"version": "6.9.3",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz",
|
||||
"integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw=="
|
||||
}
|
||||
"dezalgo": "^1.0.4",
|
||||
"hexoid": "^1.0.0",
|
||||
"once": "^1.4.0",
|
||||
"qs": "^6.11.0"
|
||||
}
|
||||
},
|
||||
"forwarded": {
|
||||
|
|
|
@ -33,9 +33,8 @@ variable "environments" {
|
|||
display_name = optional(string)
|
||||
description = optional(string)
|
||||
node_config = optional(object({
|
||||
min_node_count = optional(number)
|
||||
max_node_count = optional(number)
|
||||
current_aggregate_node_count = number
|
||||
min_node_count = optional(number)
|
||||
max_node_count = optional(number)
|
||||
}))
|
||||
iam = optional(map(list(string)))
|
||||
envgroups = list(string)
|
||||
|
|
|
@ -21,7 +21,7 @@ import time
|
|||
from google.cloud import monitoring_v3, asset_v1
|
||||
from google.protobuf import field_mask_pb2
|
||||
from googleapiclient import discovery
|
||||
from metrics import ilb_fwrules, firewall_policies, instances, networks, metrics, limits, peerings, routes, subnets, vpc_firewalls
|
||||
from metrics import ilb_fwrules, firewall_policies, instances, networks, metrics, limits, peerings, routes, subnets, vpc_firewalls, secondarys
|
||||
|
||||
CF_VERSION = os.environ.get("CF_VERSION")
|
||||
|
||||
|
@ -158,6 +158,9 @@ def main(event, context=None):
|
|||
# IP utilization subnet level metrics
|
||||
subnets.get_subnets(config, metrics_dict)
|
||||
|
||||
# IP utilization secondary range metrics
|
||||
secondarys.get_secondaries(config, metrics_dict)
|
||||
|
||||
# Asset inventory queries
|
||||
gce_instance_dict = instances.get_gce_instance_dict(config)
|
||||
l4_forwarding_rules_dict = ilb_fwrules.get_forwarding_rules_dict(config, "L4")
|
||||
|
|
|
@ -25,6 +25,16 @@ metrics_per_subnet:
|
|||
limit:
|
||||
name: number_of_max_ip
|
||||
description: Number of available IP addresses in the subnet.
|
||||
ip_usage_per_secondaryRange:
|
||||
usage:
|
||||
name: number_of_sr_ip_used
|
||||
description: Number of used IP addresses in the secondary range.
|
||||
utilization:
|
||||
name: ip_addresses_per_sr_utilization
|
||||
description: Percentage of IP used in the secondary range.
|
||||
limit:
|
||||
name: number_of_max_sr_ip
|
||||
description: Number of available IP addresses in the secondary range.
|
||||
metrics_per_network:
|
||||
instance_per_network:
|
||||
usage:
|
||||
|
|
|
@ -53,7 +53,9 @@ def create_metrics(monitoring_project, config):
|
|||
monitoring_project, config)
|
||||
# Parse limits for network and peering group metrics
|
||||
# Subnet level metrics have a different limit: the subnet IP range size
|
||||
if sub_metric_key == "limit" and metric_name != "ip_usage_per_subnet":
|
||||
if sub_metric_key == "limit" and (
|
||||
metric_name != "ip_usage_per_subnet" and
|
||||
metric_name != "ip_usage_per_secondaryRange"):
|
||||
limits_dict_for_metric = {}
|
||||
if "values" in sub_metric:
|
||||
for network_link, limit_value in sub_metric["values"].items():
|
||||
|
@ -262,4 +264,4 @@ def customize_quota_view(quota_results):
|
|||
for val in result.points:
|
||||
quotaViewJson.update({'value': val.value.int64_value})
|
||||
quotaViewList.append(quotaViewJson)
|
||||
return quotaViewList
|
||||
return quotaViewList
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import time
|
||||
|
||||
from . import metrics
|
||||
from google.protobuf import field_mask_pb2
|
||||
from google.protobuf.json_format import MessageToDict
|
||||
import ipaddress
|
||||
|
||||
|
||||
def get_all_secondaryRange(config):
|
||||
'''
|
||||
Returns a dictionary with secondary range informations
|
||||
Parameters:
|
||||
config (dict): The dict containing config like clients and limits
|
||||
Returns:
|
||||
secondary_dict (dictionary of String: dictionary): Key is the project_id,
|
||||
value is a nested dictionary with subnet_name/secondary_range_name as the key.
|
||||
'''
|
||||
secondary_dict = {}
|
||||
read_mask = field_mask_pb2.FieldMask()
|
||||
read_mask.FromJsonString('name,versionedResources')
|
||||
|
||||
response = config["clients"]["asset_client"].search_all_resources(
|
||||
request={
|
||||
"scope": f"organizations/{config['organization']}",
|
||||
"asset_types": ['compute.googleapis.com/Subnetwork'],
|
||||
"read_mask": read_mask,
|
||||
"page_size": config["page_size"],
|
||||
})
|
||||
|
||||
for asset in response:
|
||||
for versioned in asset.versioned_resources:
|
||||
subnet_name = versioned.resource.get('name')
|
||||
# Network self link format:
|
||||
# "https://www.googleapis.com/compute/v1/projects/<PROJECT_ID>/global/networks/<NETWORK_NAME>"
|
||||
project_id = versioned.resource.get('network').split('/')[6]
|
||||
network_name = versioned.resource.get('network').split('/')[-1]
|
||||
subnet_region = versioned.resource.get('region').split('/')[-1]
|
||||
|
||||
# Check first if the subnet has any secondary ranges to begin with
|
||||
if versioned.resource.get('secondaryIpRanges'):
|
||||
for items in versioned.resource.get('secondaryIpRanges'):
|
||||
# Each subnet can have multiple secondary ranges
|
||||
secondaryRange_name = items.get('rangeName')
|
||||
secondaryCidrBlock = items.get('ipCidrRange')
|
||||
|
||||
net = ipaddress.ip_network(secondaryCidrBlock)
|
||||
total_ip_addresses = int(net.num_addresses)
|
||||
|
||||
if project_id not in secondary_dict:
|
||||
secondary_dict[project_id] = {}
|
||||
secondary_dict[project_id][f"{subnet_name}/{secondaryRange_name}"] = {
|
||||
'name': secondaryRange_name,
|
||||
'region': subnet_region,
|
||||
'subnetName': subnet_name,
|
||||
'ip_cidr_range': secondaryCidrBlock,
|
||||
'total_ip_addresses': total_ip_addresses,
|
||||
'used_ip_addresses': 0,
|
||||
'network_name': network_name
|
||||
}
|
||||
return secondary_dict
|
||||
|
||||
|
||||
def compute_GKE_secondaryIP_utilization(config, read_mask, all_secondary_dict):
|
||||
'''
|
||||
Counts the IP Addresses used by GKE (Pods and Services)
|
||||
Parameters:
|
||||
config (dict): The dict containing config like clients and limits
|
||||
read_mask (FieldMask): read_mask to get additional metadata from Cloud Asset Inventory
|
||||
all_secondary_dict (dict): Dict containing the secondary IP Range information for each subnets in the GCP organization
|
||||
Returns:
|
||||
all_secondary_dict (dict): Same dict but populated with GKE IP utilization information
|
||||
'''
|
||||
cluster_secondary_dict = {}
|
||||
node_secondary_dict = {}
|
||||
|
||||
# Creating cluster dict
|
||||
# Cluster dict has subnet information
|
||||
response_cluster = config["clients"]["asset_client"].list_assets(
|
||||
request={
|
||||
"parent": f"organizations/{config['organization']}",
|
||||
"asset_types": ['container.googleapis.com/Cluster'],
|
||||
"content_type": 'RESOURCE',
|
||||
"page_size": config["page_size"],
|
||||
})
|
||||
|
||||
for asset in response_cluster:
|
||||
cluster_project = asset.resource.data['selfLink'].split('/')[5]
|
||||
cluster_parent = "/".join(asset.resource.data['selfLink'].split('/')[5:10])
|
||||
cluster_subnetwork = asset.resource.data['subnetwork']
|
||||
cluster_service_rangeName = asset.resource.data['ipAllocationPolicy'][
|
||||
'servicesSecondaryRangeName']
|
||||
|
||||
cluster_secondary_dict[f"{cluster_parent}/Service"] = {
|
||||
"project": cluster_project,
|
||||
"subnet": cluster_subnetwork,
|
||||
"secondaryRange_name": cluster_service_rangeName,
|
||||
'used_ip_addresses': 0,
|
||||
}
|
||||
|
||||
for node_pool in asset.resource.data['nodePools']:
|
||||
nodepool_name = node_pool['name']
|
||||
node_IPrange = node_pool['networkConfig']['podRange']
|
||||
cluster_secondary_dict[f"{cluster_parent}/{nodepool_name}"] = {
|
||||
"project": cluster_project,
|
||||
"subnet": cluster_subnetwork,
|
||||
"secondaryRange_name": node_IPrange,
|
||||
'used_ip_addresses': 0,
|
||||
}
|
||||
|
||||
# Creating node dict
|
||||
# Node dict allows 1:1 mapping of pod IP utilization, and which secondary Range it is using
|
||||
response_node = config["clients"]["asset_client"].search_all_resources(
|
||||
request={
|
||||
"scope": f"organizations/{config['organization']}",
|
||||
"asset_types": ['k8s.io/Node'],
|
||||
"read_mask": read_mask,
|
||||
"page_size": config["page_size"],
|
||||
})
|
||||
|
||||
for asset in response_node:
|
||||
# Node name link format:
|
||||
# "//container.googleapis.com/projects/<PROJECT_ID>/<zones/region>/<LOCATION>/clusters/<CLUSTER_NAME>/k8s/nodes/<NODE_NAME>"
|
||||
node_parent = "/".join(asset.name.split('/')[4:9])
|
||||
node_name = asset.name.split('/')[-1]
|
||||
node_full_name = f"{node_parent}/{node_name}"
|
||||
|
||||
for versioned in asset.versioned_resources:
|
||||
node_secondary_dict[node_full_name] = {
|
||||
'node_parent':
|
||||
node_parent,
|
||||
'this_node_pool':
|
||||
versioned.resource['metadata']['labels']
|
||||
['cloud.google.com/gke-nodepool'],
|
||||
'used_ip_addresses':
|
||||
0
|
||||
}
|
||||
|
||||
# Counting IP addresses used by pods in GKE
|
||||
response_pods = config["clients"]["asset_client"].search_all_resources(
|
||||
request={
|
||||
"scope": f"organizations/{config['organization']}",
|
||||
"asset_types": ['k8s.io/Pod'],
|
||||
"read_mask": read_mask,
|
||||
"page_size": config["page_size"],
|
||||
})
|
||||
|
||||
for asset in response_pods:
|
||||
# Pod name link format:
|
||||
# "//container.googleapis.com/projects/<PROJECT_ID>/<zones/region>/<LOCATION>/clusters/<CLUSTER_NAME>/k8s/namespaces/<NAMESPACE>/pods/<POD_NAME>"
|
||||
pod_parent = "/".join(asset.name.split('/')[4:9])
|
||||
|
||||
for versioned in asset.versioned_resources:
|
||||
cur_PodIP = versioned.resource['status']['podIP']
|
||||
cur_HostIP = versioned.resource['status']['hostIP']
|
||||
host_node_name = versioned.resource['spec']['nodeName']
|
||||
pod_full_path = f"{pod_parent}/{host_node_name}"
|
||||
|
||||
# A check to make sure pod is not using node IP
|
||||
if cur_PodIP != cur_HostIP:
|
||||
node_secondary_dict[pod_full_path]['used_ip_addresses'] += 1
|
||||
|
||||
# Counting IP addresses used by Service in GKE
|
||||
response_service = config["clients"]["asset_client"].search_all_resources(
|
||||
request={
|
||||
"scope": f"organizations/{config['organization']}",
|
||||
"asset_types": ['k8s.io/Service'],
|
||||
"read_mask": read_mask,
|
||||
"page_size": config["page_size"],
|
||||
})
|
||||
|
||||
for asset in response_service:
|
||||
service_parent = "/".join(asset.name.split('/')[4:9])
|
||||
service_fullpath = f"{service_parent}/Service"
|
||||
cluster_secondary_dict[service_fullpath]['used_ip_addresses'] += 1
|
||||
|
||||
for item in node_secondary_dict.values():
|
||||
itemKey = f"{item['node_parent']}/{item['this_node_pool']}"
|
||||
cluster_secondary_dict[itemKey]['used_ip_addresses'] += item['used_ip_addresses']
|
||||
|
||||
for item in cluster_secondary_dict.values():
|
||||
itemKey = f"{item['subnet']}/{item['secondaryRange_name']}"
|
||||
all_secondary_dict[item['project']][itemKey]['used_ip_addresses'] += item[
|
||||
'used_ip_addresses']
|
||||
|
||||
|
||||
def compute_secondary_utilization(config, all_secondary_dict):
|
||||
'''
|
||||
Counts resources (GKE, GCE) using IPs in secondary ranges.
|
||||
Parameters:
|
||||
config (dict): Dict containing config like clients and limits
|
||||
all_secondary_dict (dict): Dict containing the secondary IP Range information for each subnets in the GCP organization
|
||||
Returns:
|
||||
None
|
||||
'''
|
||||
read_mask = field_mask_pb2.FieldMask()
|
||||
read_mask.FromJsonString('name,versionedResources')
|
||||
|
||||
compute_GKE_secondaryIP_utilization(config, read_mask, all_secondary_dict)
|
||||
# TODO: Other Secondary IP like GCE VM using alias IPs
|
||||
|
||||
|
||||
def get_secondaries(config, metrics_dict):
|
||||
'''
|
||||
Writes all secondary rang IP address usage metrics to custom metrics.
|
||||
Parameters:
|
||||
config (dict): The dict containing config like clients and limits
|
||||
Returns:
|
||||
None
|
||||
'''
|
||||
|
||||
secondaryRange_dict = get_all_secondaryRange(config)
|
||||
# Updates all_subnets_dict with the IP utilization info
|
||||
compute_secondary_utilization(config, secondaryRange_dict)
|
||||
|
||||
timestamp = time.time()
|
||||
for project_id in config["monitored_projects"]:
|
||||
if project_id not in secondaryRange_dict:
|
||||
continue
|
||||
for secondary_dict in secondaryRange_dict[project_id].values():
|
||||
ip_utilization = 0
|
||||
if secondary_dict['used_ip_addresses'] > 0:
|
||||
ip_utilization = secondary_dict['used_ip_addresses'] / secondary_dict[
|
||||
'total_ip_addresses']
|
||||
|
||||
# Building unique identifier with subnet region/name
|
||||
subnet_id = f"{secondary_dict['region']}/{secondary_dict['name']}"
|
||||
metric_labels = {
|
||||
'project': project_id,
|
||||
'network_name': secondary_dict['network_name'],
|
||||
'region' : secondary_dict['region'],
|
||||
'subnet' : secondary_dict['subnetName'],
|
||||
'secondary_range' : secondary_dict['name']
|
||||
}
|
||||
metrics.append_data_to_series_buffer(
|
||||
config, metrics_dict["metrics_per_subnet"]
|
||||
["ip_usage_per_secondaryRange"]["usage"]["name"],
|
||||
secondary_dict['used_ip_addresses'], metric_labels,
|
||||
timestamp=timestamp)
|
||||
metrics.append_data_to_series_buffer(
|
||||
config, metrics_dict["metrics_per_subnet"]
|
||||
["ip_usage_per_secondaryRange"]["limit"]["name"],
|
||||
secondary_dict['total_ip_addresses'], metric_labels,
|
||||
timestamp=timestamp)
|
||||
metrics.append_data_to_series_buffer(
|
||||
config, metrics_dict["metrics_per_subnet"]
|
||||
["ip_usage_per_secondaryRange"]["utilization"]["name"],
|
||||
ip_utilization, metric_labels, timestamp=timestamp)
|
||||
|
||||
print("Buffered metrics for secondary ip utilization for VPCs in project",
|
||||
project_id)
|
|
@ -149,7 +149,8 @@ def compute_subnet_utilization_ilbs(config, read_mask, all_subnets_dict):
|
|||
for versioned in asset.versioned_resources:
|
||||
for field_name, field_value in versioned.resource.items():
|
||||
if 'loadBalancingScheme' in field_name and field_value in [
|
||||
'INTERNAL', 'INTERNAL_MANAGED']:
|
||||
'INTERNAL', 'INTERNAL_MANAGED'
|
||||
]:
|
||||
internal = True
|
||||
# We want to count only accepted PSC endpoint Forwarding Rule
|
||||
# If the PSC endpoint Forwarding Rule is pending, we will count it in the reserved IP addresses
|
||||
|
|
|
@ -701,6 +701,49 @@
|
|||
"width": 6,
|
||||
"xPos": 6,
|
||||
"yPos": 24
|
||||
},
|
||||
{
|
||||
"height": 4,
|
||||
"widget": {
|
||||
"title": "secondary_ip_address_utilization",
|
||||
"xyChart": {
|
||||
"chartOptions": {
|
||||
"mode": "COLOR"
|
||||
},
|
||||
"dataSets": [
|
||||
{
|
||||
"minAlignmentPeriod": "60s",
|
||||
"plotType": "LINE",
|
||||
"targetAxis": "Y1",
|
||||
"timeSeriesQuery": {
|
||||
"apiSource": "DEFAULT_CLOUD",
|
||||
"timeSeriesFilter": {
|
||||
"aggregation": {
|
||||
"alignmentPeriod": "60s",
|
||||
"crossSeriesReducer": "REDUCE_NONE",
|
||||
"perSeriesAligner": "ALIGN_MEAN"
|
||||
},
|
||||
"filter": "metric.type=\"custom.googleapis.com/ip_addresses_per_sr_utilization\" resource.type=\"global\"",
|
||||
"secondaryAggregation": {
|
||||
"alignmentPeriod": "60s",
|
||||
"crossSeriesReducer": "REDUCE_NONE",
|
||||
"perSeriesAligner": "ALIGN_NONE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeshiftDuration": "0s",
|
||||
"yAxis": {
|
||||
"label": "y1Axis",
|
||||
"scale": "LINEAR"
|
||||
}
|
||||
}
|
||||
},
|
||||
"width": 6,
|
||||
"xPos": 0,
|
||||
"yPos": 36
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -13,6 +13,5 @@ The codebase provisions the following list of resources:
|
|||
|---|---|:---:|:---:|:---:|
|
||||
| [impersonate_service_account_email](variables.tf#L16) | Service account to be impersonated by workload identity. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L21) | GCP project ID. | <code>string</code> | ✓ | |
|
||||
| [workload_identity_pool_provider_id](variables.tf#L26) | GCP workload identity pool provider ID. | <code>string</code> | ✓ | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
module "tfe_oidc" {
|
||||
source = "./tfc-oidc"
|
||||
|
||||
workload_identity_pool_provider_id = var.workload_identity_pool_provider_id
|
||||
impersonate_service_account_email = var.impersonate_service_account_email
|
||||
impersonate_service_account_email = var.impersonate_service_account_email
|
||||
}
|
||||
|
||||
provider "google" {
|
||||
|
|
|
@ -13,5 +13,4 @@
|
|||
# limitations under the License.
|
||||
|
||||
project_id = "tfe-oidc-workflow"
|
||||
workload_identity_pool_provider_id = "projects/683987109094/locations/global/workloadIdentityPools/tfe-pool/providers/tfe-provider"
|
||||
impersonate_service_account_email = "sa-tfe@tfe-oidc-workflow2.iam.gserviceaccount.com"
|
||||
|
|
|
@ -5,9 +5,8 @@ This is a helper module to prepare GCP Credentials from Terraform Enterprise wor
|
|||
## Example
|
||||
```hcl
|
||||
module "tfe_oidc" {
|
||||
source = "./tfe_oidc"
|
||||
source = "./tfc-oidc"
|
||||
|
||||
workload_identity_pool_provider_id = "projects/683987109094/locations/global/workloadIdentityPools/tfe-pool/providers/tfe-provider"
|
||||
impersonate_service_account_email = "tfe-test@tfe-test-wif.iam.gserviceaccount.com"
|
||||
}
|
||||
|
||||
|
@ -28,7 +27,6 @@ provider "google-beta" {
|
|||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [impersonate_service_account_email](variables.tf#L17) | Service account to be impersonated by workload identity federation. | <code>string</code> | ✓ | |
|
||||
| [workload_identity_pool_provider_id](variables.tf#L28) | GCP workload identity pool provider ID. | <code>string</code> | ✓ | |
|
||||
| [tmp_oidc_token_path](variables.tf#L22) | Name of the temporary file where TFC OIDC token will be stored to authentificate terraform provider google. | <code>string</code> | | <code>".oidc_token"</code> |
|
||||
|
||||
## Outputs
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#!/bin/bash
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
# Exit if any of the intermediate steps fail
|
||||
set -e
|
||||
|
||||
cat <<EOF
|
||||
{
|
||||
"audience": "$TFC_WORKLOAD_IDENTITY_AUDIENCE"
|
||||
}
|
||||
EOF
|
|
@ -14,10 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
locals {
|
||||
audience = "//iam.googleapis.com/${var.workload_identity_pool_provider_id}"
|
||||
}
|
||||
|
||||
data "external" "oidc_token_file" {
|
||||
program = ["bash", "${path.module}/write_token.sh", "${var.tmp_oidc_token_path}"]
|
||||
}
|
||||
|
||||
data "external" "workload_identity_pool" {
|
||||
program = ["bash", "${path.module}/get_audience.sh"]
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ output "credentials" {
|
|||
description = "Credentials in format to pass the to gcp provider."
|
||||
value = jsonencode({
|
||||
"type" : "external_account",
|
||||
"audience" : "${local.audience}",
|
||||
"audience" : data.external.workload_identity_pool.result.audience,
|
||||
"subject_token_type" : "urn:ietf:params:oauth:token-type:jwt",
|
||||
"token_url" : "https://sts.googleapis.com/v1/token",
|
||||
"credential_source" : data.external.oidc_token_file.result
|
||||
|
|
|
@ -24,8 +24,3 @@ variable "tmp_oidc_token_path" {
|
|||
type = string
|
||||
default = ".oidc_token"
|
||||
}
|
||||
|
||||
variable "workload_identity_pool_provider_id" {
|
||||
description = "GCP workload identity pool provider ID."
|
||||
type = string
|
||||
}
|
||||
|
|
|
@ -22,8 +22,3 @@ variable "project_id" {
|
|||
description = "GCP project ID."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "workload_identity_pool_provider_id" {
|
||||
description = "GCP workload identity pool provider ID."
|
||||
type = string
|
||||
}
|
||||
|
|
|
@ -304,7 +304,6 @@ module "vpn-hub" {
|
|||
remote_ranges = values(var.private_service_ranges)
|
||||
tunnels = {
|
||||
spoke-2 = {
|
||||
ike_version = 2
|
||||
peer_ip = module.vpn-spoke-2.address
|
||||
shared_secret = ""
|
||||
traffic_selectors = { local = ["0.0.0.0/0"], remote = null }
|
||||
|
@ -323,7 +322,6 @@ module "vpn-spoke-2" {
|
|||
remote_ranges = ["10.0.0.0/8"]
|
||||
tunnels = {
|
||||
hub = {
|
||||
ike_version = 2
|
||||
peer_ip = module.vpn-hub.address
|
||||
shared_secret = module.vpn-hub.random_secret
|
||||
traffic_selectors = { local = ["0.0.0.0/0"], remote = null }
|
||||
|
|
|
@ -15,93 +15,73 @@
|
|||
# tfdoc:file:description Landing to Development VPN for region 1.
|
||||
|
||||
module "landing-to-dev-vpn-r1" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = var.regions.r1
|
||||
name = "${var.prefix}-lnd-to-dev-r1"
|
||||
router_create = false
|
||||
router_name = "${var.prefix}-lnd-vpn-r1"
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = var.regions.r1
|
||||
name = "${var.prefix}-lnd-to-dev-r1"
|
||||
# router is created and managed by the production VPN module
|
||||
# so we don't configure advertisements here
|
||||
peer_gcp_gateway = module.dev-to-landing-vpn-r1.self_link
|
||||
router_config = {
|
||||
create = false
|
||||
name = "${var.prefix}-lnd-vpn-r1"
|
||||
asn = 64514
|
||||
}
|
||||
peer_gateway = { gcp = module.dev-to-landing-vpn-r1.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.2.2"
|
||||
asn = var.vpn_configs.dev-r1.asn
|
||||
}
|
||||
# use this attribute to configure different advertisements for dev
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.2.1/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 0
|
||||
bgp_session_range = "169.254.2.1/30"
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.2.6"
|
||||
asn = var.vpn_configs.dev-r1.asn
|
||||
}
|
||||
# use this attribute to configure different advertisements for dev
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.2.5/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 1
|
||||
bgp_session_range = "169.254.2.5/30"
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "dev-to-landing-vpn-r1" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
network = module.dev-vpc.self_link
|
||||
region = var.regions.r1
|
||||
name = "${var.prefix}-dev-to-lnd-r1"
|
||||
router_create = true
|
||||
router_name = "${var.prefix}-dev-vpn-r1"
|
||||
router_asn = var.vpn_configs.dev-r1.asn
|
||||
router_advertise_config = (
|
||||
var.vpn_configs.dev-r1.custom_ranges == null
|
||||
? null
|
||||
: {
|
||||
groups = null
|
||||
ip_ranges = coalesce(var.vpn_configs.dev-r1.custom_ranges, {})
|
||||
mode = "CUSTOM"
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
network = module.dev-vpc.self_link
|
||||
region = var.regions.r1
|
||||
name = "${var.prefix}-dev-to-lnd-r1"
|
||||
router_config = {
|
||||
name = "${var.prefix}-dev-vpn-r1"
|
||||
asn = var.vpn_configs.dev-r1.asn
|
||||
custom_advertise = {
|
||||
all_subnets = false
|
||||
ip_ranges = coalesce(var.vpn_configs.dev-r1.custom_ranges, {})
|
||||
mode = "CUSTOM"
|
||||
}
|
||||
)
|
||||
peer_gcp_gateway = module.landing-to-dev-vpn-r1.self_link
|
||||
}
|
||||
peer_gateway = { gcp = module.landing-to-dev-vpn-r1.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.2.1"
|
||||
asn = var.vpn_configs.land-r1.asn
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.2.2/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-dev-vpn-r1.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
bgp_session_range = "169.254.2.2/30"
|
||||
shared_secret = module.landing-to-dev-vpn-r1.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.2.5"
|
||||
asn = var.vpn_configs.land-r1.asn
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.2.6/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-dev-vpn-r1.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
bgp_session_range = "169.254.2.6/30"
|
||||
shared_secret = module.landing-to-dev-vpn-r1.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,102 +15,74 @@
|
|||
# tfdoc:file:description Landing to Production VPN for region 1.
|
||||
|
||||
module "landing-to-prod-vpn-r1" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = var.regions.r1
|
||||
name = "${var.prefix}-lnd-to-prd-r1"
|
||||
router_create = true
|
||||
router_name = "${var.prefix}-lnd-vpn-r1"
|
||||
router_asn = var.vpn_configs.land-r1.asn
|
||||
router_advertise_config = (
|
||||
var.vpn_configs.land-r1.custom_ranges == null
|
||||
? null
|
||||
: {
|
||||
groups = null
|
||||
ip_ranges = coalesce(var.vpn_configs.land-r1.custom_ranges, {})
|
||||
mode = "CUSTOM"
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = var.regions.r1
|
||||
name = "${var.prefix}-lnd-to-prd-r1"
|
||||
router_config = {
|
||||
name = "${var.prefix}-lnd-vpn-r1"
|
||||
asn = var.vpn_configs.land-r1.asn
|
||||
custom_advertise = {
|
||||
all_subnets = false
|
||||
ip_ranges = coalesce(var.vpn_configs.land-r1.custom_ranges, {})
|
||||
}
|
||||
)
|
||||
peer_gcp_gateway = module.prod-to-landing-vpn-r1.self_link
|
||||
}
|
||||
peer_gateway = { gcp = module.prod-to-landing-vpn-r1.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.0.2"
|
||||
asn = var.vpn_configs.prod-r1.asn
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.0.1/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 0
|
||||
bgp_session_range = "169.254.0.1/30"
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.0.6"
|
||||
asn = var.vpn_configs.prod-r1.asn
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.0.5/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 1
|
||||
bgp_session_range = "169.254.0.5/30"
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "prod-to-landing-vpn-r1" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
network = module.prod-vpc.self_link
|
||||
region = var.regions.r1
|
||||
name = "${var.prefix}-prd-to-lnd-r1"
|
||||
router_create = true
|
||||
router_name = "${var.prefix}-prd-vpn-r1"
|
||||
router_asn = var.vpn_configs.prod-r1.asn
|
||||
# the router is managed here but shared with the dev VPN
|
||||
router_advertise_config = (
|
||||
var.vpn_configs.prod-r1.custom_ranges == null
|
||||
? null
|
||||
: {
|
||||
groups = null
|
||||
ip_ranges = coalesce(var.vpn_configs.prod-r1.custom_ranges, {})
|
||||
mode = "CUSTOM"
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
network = module.prod-vpc.self_link
|
||||
region = var.regions.r1
|
||||
name = "${var.prefix}-prd-to-lnd-r1"
|
||||
router_config = {
|
||||
name = "${var.prefix}-prd-vpn-r1"
|
||||
asn = var.vpn_configs.prod-r1.asn
|
||||
# the router is managed here but shared with the dev VPN
|
||||
custom_advertise = {
|
||||
all_subnets = false
|
||||
ip_ranges = coalesce(var.vpn_configs.prod-r1.custom_ranges, {})
|
||||
}
|
||||
)
|
||||
peer_gcp_gateway = module.landing-to-prod-vpn-r1.self_link
|
||||
}
|
||||
peer_gateway = { gcp = module.landing-to-prod-vpn-r1.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.0.1"
|
||||
asn = var.vpn_configs.land-r1.asn
|
||||
}
|
||||
# use this attribute to configure different advertisements for prod
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.0.2/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-prod-vpn-r1.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
bgp_session_range = "169.254.0.2/30"
|
||||
shared_secret = module.landing-to-prod-vpn-r1.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.0.5"
|
||||
asn = var.vpn_configs.land-r1.asn
|
||||
}
|
||||
# use this attribute to configure different advertisements for prod
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.0.6/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-prod-vpn-r1.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
bgp_session_range = "169.254.0.6/30"
|
||||
shared_secret = module.landing-to-prod-vpn-r1.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,65 +79,58 @@ module "vpc-firewall" {
|
|||
}
|
||||
|
||||
module "vpn1" {
|
||||
source = "../../../modules/net-vpn-dynamic"
|
||||
project_id = var.project_id
|
||||
region = var.region.gcp1
|
||||
network = module.vpc.name
|
||||
name = "to-onprem1"
|
||||
router_asn = var.bgp_asn.gcp1
|
||||
source = "../../../modules/net-vpn-dynamic"
|
||||
project_id = var.project_id
|
||||
region = var.region.gcp1
|
||||
network = module.vpc.name
|
||||
name = "to-onprem1"
|
||||
router_config = { asn = var.bgp_asn.gcp1 }
|
||||
tunnels = {
|
||||
onprem = {
|
||||
bgp_peer = {
|
||||
address = local.bgp_interface_onprem1
|
||||
asn = var.bgp_asn.onprem1
|
||||
}
|
||||
bgp_peer_options = {
|
||||
advertise_groups = ["ALL_SUBNETS"]
|
||||
advertise_ip_ranges = {
|
||||
(local.netblocks.dns) = "DNS resolvers"
|
||||
(local.netblocks.private) = "private.gooogleapis.com"
|
||||
(local.netblocks.restricted) = "restricted.gooogleapis.com"
|
||||
}
|
||||
advertise_mode = "CUSTOM"
|
||||
route_priority = 1000
|
||||
custom_advertise = {
|
||||
all_subnets = true
|
||||
all_vpc_subnets = false
|
||||
all_peer_vpc_subnets = false
|
||||
ip_ranges = {
|
||||
(local.netblocks.dns) = "DNS resolvers"
|
||||
(local.netblocks.private) = "private.gooogleapis.com"
|
||||
(local.netblocks.restricted) = "restricted.gooogleapis.com"
|
||||
} }
|
||||
}
|
||||
bgp_session_range = "${local.bgp_interface_gcp1}/30"
|
||||
ike_version = 2
|
||||
peer_ip = module.vm-onprem.external_ip
|
||||
router = null
|
||||
shared_secret = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "vpn2" {
|
||||
source = "../../../modules/net-vpn-dynamic"
|
||||
project_id = var.project_id
|
||||
region = var.region.gcp2
|
||||
network = module.vpc.name
|
||||
name = "to-onprem2"
|
||||
router_asn = var.bgp_asn.gcp2
|
||||
source = "../../../modules/net-vpn-dynamic"
|
||||
project_id = var.project_id
|
||||
region = var.region.gcp2
|
||||
network = module.vpc.name
|
||||
name = "to-onprem2"
|
||||
router_config = { asn = var.bgp_asn.gcp2 }
|
||||
tunnels = {
|
||||
onprem = {
|
||||
bgp_peer = {
|
||||
address = local.bgp_interface_onprem2
|
||||
asn = var.bgp_asn.onprem2
|
||||
}
|
||||
bgp_peer_options = {
|
||||
advertise_groups = ["ALL_SUBNETS"]
|
||||
advertise_ip_ranges = {
|
||||
(local.netblocks.dns) = "DNS resolvers"
|
||||
(local.netblocks.private) = "private.gooogleapis.com"
|
||||
(local.netblocks.restricted) = "restricted.gooogleapis.com"
|
||||
custom_advertise = {
|
||||
all_subnets = true
|
||||
all_vpc_subnets = false
|
||||
all_peer_vpc_subnets = false
|
||||
ip_ranges = {
|
||||
(local.netblocks.dns) = "DNS resolvers"
|
||||
(local.netblocks.private) = "private.gooogleapis.com"
|
||||
(local.netblocks.restricted) = "restricted.gooogleapis.com"
|
||||
}
|
||||
}
|
||||
advertise_mode = "CUSTOM"
|
||||
route_priority = 1000
|
||||
}
|
||||
bgp_session_range = "${local.bgp_interface_gcp2}/30"
|
||||
ike_version = 2
|
||||
peer_ip = module.vm-onprem.external_ip
|
||||
router = null
|
||||
shared_secret = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,85 +79,69 @@ module "vpn-onprem" {
|
|||
region = var.region
|
||||
network = module.vpc-onprem.self_link
|
||||
name = "${var.name}-onprem-to-hub"
|
||||
router_asn = 65001
|
||||
router_advertise_config = {
|
||||
groups = ["ALL_SUBNETS"]
|
||||
ip_ranges = {
|
||||
router_config = {
|
||||
asn = 65001
|
||||
custom_advertise = {
|
||||
all_subnets = true
|
||||
ip_ranges = {}
|
||||
}
|
||||
mode = "CUSTOM"
|
||||
}
|
||||
peer_gcp_gateway = module.vpn-hub.self_link
|
||||
peer_gateway = { gcp = module.vpn-hub.self_link }
|
||||
tunnels = {
|
||||
tunnel-0 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.0.2"
|
||||
asn = 65002
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.0.1/30"
|
||||
ike_version = 2
|
||||
vpn_gateway_interface = 0
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = ""
|
||||
bgp_session_range = "169.254.0.1/30"
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
tunnel-1 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.0.6"
|
||||
asn = 65002
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.0.5/30"
|
||||
ike_version = 2
|
||||
vpn_gateway_interface = 1
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = ""
|
||||
bgp_session_range = "169.254.0.5/30"
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "vpn-hub" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.project.project_id
|
||||
region = var.region
|
||||
network = module.vpc-hub.name
|
||||
name = "${var.name}-hub-to-onprem"
|
||||
router_asn = 65002
|
||||
peer_gcp_gateway = module.vpn-onprem.self_link
|
||||
router_advertise_config = {
|
||||
groups = ["ALL_SUBNETS"]
|
||||
ip_ranges = {
|
||||
(var.psc_endpoint) = "to-psc-endpoint"
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.project.project_id
|
||||
region = var.region
|
||||
network = module.vpc-hub.name
|
||||
name = "${var.name}-hub-to-onprem"
|
||||
router_config = {
|
||||
asn = 65002
|
||||
custom_advertise = {
|
||||
all_subnets = true
|
||||
ip_ranges = {
|
||||
(var.psc_endpoint) = "to-psc-endpoint"
|
||||
}
|
||||
}
|
||||
mode = "CUSTOM"
|
||||
}
|
||||
peer_gateway = { gcp = module.vpn-onprem.self_link }
|
||||
|
||||
tunnels = {
|
||||
tunnel-0 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.0.1"
|
||||
asn = 65001
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.0.2/30"
|
||||
ike_version = 2
|
||||
vpn_gateway_interface = 0
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.vpn-onprem.random_secret
|
||||
bgp_session_range = "169.254.0.2/30"
|
||||
vpn_gateway_interface = 0
|
||||
shared_secret = module.vpn-onprem.random_secret
|
||||
}
|
||||
tunnel-1 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.0.5"
|
||||
asn = 65001
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.0.6/30"
|
||||
ike_version = 2
|
||||
vpn_gateway_interface = 1
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.vpn-onprem.random_secret
|
||||
bgp_session_range = "169.254.0.6/30"
|
||||
vpn_gateway_interface = 1
|
||||
shared_secret = module.vpn-onprem.random_secret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -387,7 +387,7 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
|
|||
| [region_trigram](variables.tf#L183) | Short names for GCP regions. | <code>map(string)</code> | | <code title="{ europe-west1 = "ew1" europe-west4 = "ew4" }">{…}</code> | |
|
||||
| [router_configs](variables.tf#L192) | Configurations for CRs and onprem routers. | <code title="map(object({ adv = object({ custom = list(string) default = bool }) asn = number }))">map(object({…}))</code> | | <code title="{ landing-trusted-ew1 = { asn = "64512" adv = null } landing-trusted-ew4 = { asn = "64512" adv = null } }">{…}</code> | |
|
||||
| [service_accounts](variables.tf#L215) | Automation service accounts in name => email format. | <code title="object({ data-platform-dev = string data-platform-prod = string gke-dev = string gke-prod = string project-factory-dev = string project-factory-prod = string })">object({…})</code> | | <code>null</code> | <code>01-resman</code> |
|
||||
| [vpn_onprem_configs](variables.tf#L229) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(object({ id = number ip_address = string })) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-trusted-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } landing-trusted-ew4 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
||||
| [vpn_onprem_configs](variables.tf#L229) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(string) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-trusted-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = ["8.8.8.8"] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } landing-trusted-ew4 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = ["8.8.8.8"] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -235,10 +235,7 @@ variable "vpn_onprem_configs" {
|
|||
})
|
||||
peer_external_gateway = object({
|
||||
redundancy_type = string
|
||||
interfaces = list(object({
|
||||
id = number
|
||||
ip_address = string
|
||||
}))
|
||||
interfaces = list(string)
|
||||
})
|
||||
tunnels = list(object({
|
||||
peer_asn = number
|
||||
|
@ -258,9 +255,7 @@ variable "vpn_onprem_configs" {
|
|||
}
|
||||
peer_external_gateway = {
|
||||
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
|
||||
interfaces = [
|
||||
{ id = 0, ip_address = "8.8.8.8" },
|
||||
]
|
||||
interfaces = ["8.8.8.8"]
|
||||
}
|
||||
tunnels = [
|
||||
{
|
||||
|
@ -288,9 +283,7 @@ variable "vpn_onprem_configs" {
|
|||
}
|
||||
peer_external_gateway = {
|
||||
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
|
||||
interfaces = [
|
||||
{ id = 0, ip_address = "8.8.8.8" },
|
||||
]
|
||||
interfaces = ["8.8.8.8"]
|
||||
}
|
||||
tunnels = [
|
||||
{
|
||||
|
|
|
@ -33,16 +33,19 @@ locals {
|
|||
}
|
||||
|
||||
module "landing-to-onprem-ew1-vpn" {
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-trusted-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_create = true
|
||||
router_name = "landing-onprem-vpn-ew1"
|
||||
router_asn = var.router_configs.landing-trusted-ew1.asn
|
||||
peer_external_gateway = var.vpn_onprem_configs.landing-trusted-ew1.peer_external_gateway
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-trusted-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_config = {
|
||||
name = "landing-onprem-vpn-ew1"
|
||||
asn = var.router_configs.landing-trusted-ew1.asn
|
||||
}
|
||||
peer_gateway = {
|
||||
external = var.vpn_onprem_configs.landing-trusted-ew1.peer_external_gateway
|
||||
}
|
||||
tunnels = {
|
||||
for t in var.vpn_onprem_configs.landing-trusted-ew1.tunnels :
|
||||
"remote-${t.vpn_gateway_interface}-${t.peer_external_gateway_interface}" => {
|
||||
|
@ -52,9 +55,7 @@ module "landing-to-onprem-ew1-vpn" {
|
|||
}
|
||||
bgp_peer_options = local.bgp_peer_options_onprem.landing-trusted-ew1
|
||||
bgp_session_range = "${cidrhost(t.session_range, 2)}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = t.peer_external_gateway_interface
|
||||
router = null
|
||||
shared_secret = t.secret
|
||||
vpn_gateway_interface = t.vpn_gateway_interface
|
||||
}
|
||||
|
@ -62,16 +63,19 @@ module "landing-to-onprem-ew1-vpn" {
|
|||
}
|
||||
|
||||
module "landing-to-onprem-ew4-vpn" {
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-trusted-vpc.self_link
|
||||
region = "europe-west4"
|
||||
name = "vpn-to-onprem-ew4"
|
||||
router_create = true
|
||||
router_name = "landing-onprem-vpn-ew4"
|
||||
router_asn = var.router_configs.landing-trusted-ew4.asn
|
||||
peer_external_gateway = var.vpn_onprem_configs.landing-trusted-ew4.peer_external_gateway
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-trusted-vpc.self_link
|
||||
region = "europe-west4"
|
||||
name = "vpn-to-onprem-ew4"
|
||||
router_config = {
|
||||
name = "landing-onprem-vpn-ew4"
|
||||
asn = var.router_configs.landing-trusted-ew4.asn
|
||||
}
|
||||
peer_gateway = {
|
||||
external = var.vpn_onprem_configs.landing-trusted-ew4.peer_external_gateway
|
||||
}
|
||||
tunnels = {
|
||||
for t in var.vpn_onprem_configs.landing-trusted-ew4.tunnels :
|
||||
"remote-${t.vpn_gateway_interface}-${t.peer_external_gateway_interface}" => {
|
||||
|
@ -81,9 +85,7 @@ module "landing-to-onprem-ew4-vpn" {
|
|||
}
|
||||
bgp_peer_options = local.bgp_peer_options_onprem.landing-trusted-ew4
|
||||
bgp_session_range = "${cidrhost(t.session_range, 2)}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = t.peer_external_gateway_interface
|
||||
router = null
|
||||
shared_secret = t.secret
|
||||
vpn_gateway_interface = t.vpn_gateway_interface
|
||||
}
|
||||
|
|
|
@ -311,7 +311,7 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
|
|||
| [region_trigram](variables.tf#L166) | Short names for GCP regions. | <code>map(string)</code> | | <code title="{ europe-west1 = "ew1" europe-west3 = "ew3" }">{…}</code> | |
|
||||
| [router_onprem_configs](variables.tf#L175) | Configurations for routers used for onprem connectivity. | <code title="map(object({ adv = object({ custom = list(string) default = bool }) asn = number }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { asn = "65533" adv = null } }">{…}</code> | |
|
||||
| [service_accounts](variables.tf#L193) | Automation service accounts in name => email format. | <code title="object({ data-platform-dev = string data-platform-prod = string gke-dev = string gke-prod = string project-factory-dev = string project-factory-prod = string })">object({…})</code> | | <code>null</code> | <code>01-resman</code> |
|
||||
| [vpn_onprem_configs](variables.tf#L207) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(object({ id = number ip_address = string })) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
||||
| [vpn_onprem_configs](variables.tf#L207) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(string) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = ["8.8.8.8"] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -213,10 +213,7 @@ variable "vpn_onprem_configs" {
|
|||
})
|
||||
peer_external_gateway = object({
|
||||
redundancy_type = string
|
||||
interfaces = list(object({
|
||||
id = number
|
||||
ip_address = string
|
||||
}))
|
||||
interfaces = list(string)
|
||||
})
|
||||
tunnels = list(object({
|
||||
peer_asn = number
|
||||
|
@ -236,9 +233,7 @@ variable "vpn_onprem_configs" {
|
|||
}
|
||||
peer_external_gateway = {
|
||||
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
|
||||
interfaces = [
|
||||
{ id = 0, ip_address = "8.8.8.8" },
|
||||
]
|
||||
interfaces = ["8.8.8.8"]
|
||||
}
|
||||
tunnels = [
|
||||
{
|
||||
|
|
|
@ -33,16 +33,19 @@ locals {
|
|||
}
|
||||
|
||||
module "landing-to-onprem-ew1-vpn" {
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_create = true
|
||||
router_name = "landing-onprem-vpn-ew1"
|
||||
router_asn = var.router_onprem_configs.landing-ew1.asn
|
||||
peer_external_gateway = var.vpn_onprem_configs.landing-ew1.peer_external_gateway
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_config = {
|
||||
name = "landing-onprem-vpn-ew1"
|
||||
asn = var.router_onprem_configs.landing-ew1.asn
|
||||
}
|
||||
peer_gateway = {
|
||||
external = var.vpn_onprem_configs.landing-ew1.peer_external_gateway
|
||||
}
|
||||
tunnels = {
|
||||
for t in var.vpn_onprem_configs.landing-ew1.tunnels :
|
||||
"remote-${t.vpn_gateway_interface}-${t.peer_external_gateway_interface}" => {
|
||||
|
@ -52,9 +55,7 @@ module "landing-to-onprem-ew1-vpn" {
|
|||
}
|
||||
bgp_peer_options = local.bgp_peer_options_onprem.landing-ew1
|
||||
bgp_session_range = "${cidrhost(t.session_range, 2)}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = t.peer_external_gateway_interface
|
||||
router = null
|
||||
shared_secret = t.secret
|
||||
vpn_gateway_interface = t.vpn_gateway_interface
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ You're now ready to run `terraform init` and `apply`.
|
|||
| [psa_ranges](variables.tf#L129) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code title="object({ dev = object({ ranges = map(string) routes = object({ export = bool import = bool }) }) prod = object({ ranges = map(string) routes = object({ export = bool import = bool }) }) })">object({…})</code> | | <code>null</code> | |
|
||||
| [router_onprem_configs](variables.tf#L166) | Configurations for routers used for onprem connectivity. | <code title="map(object({ adv = object({ custom = list(string) default = bool }) asn = number }))">map(object({…}))</code> | | <code title="{ prod-ew1 = { asn = "65533" adv = null } dev-ew1 = { asn = "65534" adv = null } }">{…}</code> | |
|
||||
| [service_accounts](variables.tf#L189) | Automation service accounts in name => email format. | <code title="object({ data-platform-dev = string data-platform-prod = string project-factory-dev = string project-factory-prod = string })">object({…})</code> | | <code>null</code> | <code>01-resman</code> |
|
||||
| [vpn_onprem_configs](variables.tf#L201) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(object({ id = number ip_address = string })) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ dev-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_dev" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65544 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65544 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } prod-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_prod" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65543 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65543 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
||||
| [vpn_onprem_configs](variables.tf#L201) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(string) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ dev-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_dev" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = ["8.8.8.8"] } tunnels = [ { peer_asn = 65544 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65544 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } prod-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_prod" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = ["8.8.8.8"] } tunnels = [ { peer_asn = 65543 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65543 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -207,10 +207,7 @@ variable "vpn_onprem_configs" {
|
|||
})
|
||||
peer_external_gateway = object({
|
||||
redundancy_type = string
|
||||
interfaces = list(object({
|
||||
id = number
|
||||
ip_address = string
|
||||
}))
|
||||
interfaces = list(string)
|
||||
})
|
||||
tunnels = list(object({
|
||||
peer_asn = number
|
||||
|
@ -230,9 +227,8 @@ variable "vpn_onprem_configs" {
|
|||
}
|
||||
peer_external_gateway = {
|
||||
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
|
||||
interfaces = [
|
||||
{ id = 0, ip_address = "8.8.8.8" },
|
||||
]
|
||||
interfaces = ["8.8.8.8"]
|
||||
|
||||
}
|
||||
tunnels = [
|
||||
{
|
||||
|
@ -260,9 +256,7 @@ variable "vpn_onprem_configs" {
|
|||
}
|
||||
peer_external_gateway = {
|
||||
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
|
||||
interfaces = [
|
||||
{ id = 0, ip_address = "8.8.8.8" },
|
||||
]
|
||||
interfaces = ["8.8.8.8"]
|
||||
}
|
||||
tunnels = [
|
||||
{
|
||||
|
|
|
@ -33,16 +33,19 @@ locals {
|
|||
}
|
||||
|
||||
module "dev-to-onprem-ew1-vpn" {
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.dev-spoke-project.project_id
|
||||
network = module.dev-spoke-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_create = true
|
||||
router_name = "dev-onprem-vpn-ew1"
|
||||
router_asn = var.router_onprem_configs.dev-ew1.asn
|
||||
peer_external_gateway = var.vpn_onprem_configs.dev-ew1.peer_external_gateway
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.dev-spoke-project.project_id
|
||||
network = module.dev-spoke-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_config = {
|
||||
name = "dev-onprem-vpn-ew1"
|
||||
asn = var.router_onprem_configs.dev-ew1.asn
|
||||
}
|
||||
peer_gateway = {
|
||||
external = var.vpn_onprem_configs.dev-ew1.peer_external_gateway
|
||||
}
|
||||
tunnels = {
|
||||
for t in var.vpn_onprem_configs.dev-ew1.tunnels :
|
||||
"remote-${t.vpn_gateway_interface}-${t.peer_external_gateway_interface}" => {
|
||||
|
@ -52,9 +55,7 @@ module "dev-to-onprem-ew1-vpn" {
|
|||
}
|
||||
bgp_peer_options = local.bgp_peer_options_onprem.dev-ew1
|
||||
bgp_session_range = "${cidrhost(t.session_range, 2)}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = t.peer_external_gateway_interface
|
||||
router = null
|
||||
shared_secret = t.secret
|
||||
vpn_gateway_interface = t.vpn_gateway_interface
|
||||
}
|
||||
|
|
|
@ -17,16 +17,19 @@
|
|||
# tfdoc:file:description VPN between prod and onprem.
|
||||
|
||||
module "prod-to-onprem-ew1-vpn" {
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.prod-spoke-project.project_id
|
||||
network = module.prod-spoke-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_create = true
|
||||
router_name = "prod-onprem-vpn-ew1"
|
||||
router_asn = var.router_onprem_configs.prod-ew1.asn
|
||||
peer_external_gateway = var.vpn_onprem_configs.prod-ew1.peer_external_gateway
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.prod-spoke-project.project_id
|
||||
network = module.prod-spoke-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_config = {
|
||||
name = "prod-onprem-vpn-ew1"
|
||||
asn = var.router_onprem_configs.prod-ew1.asn
|
||||
}
|
||||
peer_gateway = {
|
||||
external = var.vpn_onprem_configs.prod-ew1.peer_external_gateway
|
||||
}
|
||||
tunnels = {
|
||||
for t in var.vpn_onprem_configs.prod-ew1.tunnels :
|
||||
"remote-${t.vpn_gateway_interface}-${t.peer_external_gateway_interface}" => {
|
||||
|
@ -36,9 +39,7 @@ module "prod-to-onprem-ew1-vpn" {
|
|||
}
|
||||
bgp_peer_options = local.bgp_peer_options_onprem.prod-ew1
|
||||
bgp_session_range = "${cidrhost(t.session_range, 2)}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = t.peer_external_gateway_interface
|
||||
router = null
|
||||
shared_secret = t.secret
|
||||
vpn_gateway_interface = t.vpn_gateway_interface
|
||||
}
|
||||
|
|
|
@ -336,7 +336,7 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS
|
|||
| [router_onprem_configs](variables.tf#L175) | Configurations for routers used for onprem connectivity. | <code title="map(object({ adv = object({ custom = list(string) default = bool }) asn = number }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { asn = "65533" adv = null } }">{…}</code> | |
|
||||
| [router_spoke_configs](variables-vpn.tf#L18) | Configurations for routers used for internal connectivity. | <code title="map(object({ adv = object({ custom = list(string) default = bool }) asn = number }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { asn = "64512", adv = null } landing-ew4 = { asn = "64512", adv = null } spoke-dev-ew1 = { asn = "64513", adv = null } spoke-dev-ew4 = { asn = "64513", adv = null } spoke-prod-ew1 = { asn = "64514", adv = null } spoke-prod-ew4 = { asn = "64514", adv = null } }">{…}</code> | |
|
||||
| [service_accounts](variables.tf#L193) | Automation service accounts in name => email format. | <code title="object({ data-platform-dev = string data-platform-prod = string gke-dev = string gke-prod = string project-factory-dev = string project-factory-prod = string })">object({…})</code> | | <code>null</code> | <code>01-resman</code> |
|
||||
| [vpn_onprem_configs](variables.tf#L207) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(object({ id = number ip_address = string })) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
||||
| [vpn_onprem_configs](variables.tf#L207) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(string) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = ["8.8.8.8"] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
||||
| [vpn_spoke_configs](variables-vpn.tf#L37) | VPN gateway configuration for spokes. | <code title="map(object({ default = bool custom = list(string) }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { default = false custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] } landing-ew4 = { default = false custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] } dev-ew1 = { default = false custom = ["gcp_dev"] } prod-ew1 = { default = false custom = ["gcp_prod"] } prod-ew4 = { default = false custom = ["gcp_prod"] } }">{…}</code> | |
|
||||
|
||||
## Outputs
|
||||
|
|
|
@ -213,10 +213,7 @@ variable "vpn_onprem_configs" {
|
|||
})
|
||||
peer_external_gateway = object({
|
||||
redundancy_type = string
|
||||
interfaces = list(object({
|
||||
id = number
|
||||
ip_address = string
|
||||
}))
|
||||
interfaces = list(string)
|
||||
})
|
||||
tunnels = list(object({
|
||||
peer_asn = number
|
||||
|
@ -236,9 +233,7 @@ variable "vpn_onprem_configs" {
|
|||
}
|
||||
peer_external_gateway = {
|
||||
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
|
||||
interfaces = [
|
||||
{ id = 0, ip_address = "8.8.8.8" },
|
||||
]
|
||||
interfaces = ["8.8.8.8"]
|
||||
}
|
||||
tunnels = [
|
||||
{
|
||||
|
|
|
@ -33,16 +33,19 @@ locals {
|
|||
}
|
||||
|
||||
module "landing-to-onprem-ew1-vpn" {
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_create = true
|
||||
router_name = "landing-onprem-vpn-ew1"
|
||||
router_asn = var.router_onprem_configs.landing-ew1.asn
|
||||
peer_external_gateway = var.vpn_onprem_configs.landing-ew1.peer_external_gateway
|
||||
count = local.enable_onprem_vpn ? 1 : 0
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-onprem-ew1"
|
||||
router_config = {
|
||||
name = "landing-onprem-vpn-ew1"
|
||||
asn = var.router_onprem_configs.landing-ew1.asn
|
||||
}
|
||||
peer_gateway = {
|
||||
external = var.vpn_onprem_configs.landing-ew1.peer_external_gateway
|
||||
}
|
||||
tunnels = {
|
||||
for t in var.vpn_onprem_configs.landing-ew1.tunnels :
|
||||
"remote-${t.vpn_gateway_interface}-${t.peer_external_gateway_interface}" => {
|
||||
|
@ -52,9 +55,7 @@ module "landing-to-onprem-ew1-vpn" {
|
|||
}
|
||||
bgp_peer_options = local.bgp_peer_options_onprem.landing-ew1
|
||||
bgp_session_range = "${cidrhost(t.session_range, 2)}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = t.peer_external_gateway_interface
|
||||
router = null
|
||||
shared_secret = t.secret
|
||||
vpn_gateway_interface = t.vpn_gateway_interface
|
||||
}
|
||||
|
|
|
@ -39,11 +39,13 @@ module "landing-to-dev-ew1-vpn" {
|
|||
network = module.landing-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-dev-ew1"
|
||||
# The router used for this VPN is managed in vpn-prod.tf
|
||||
router_create = false
|
||||
router_name = "landing-vpn-ew1"
|
||||
router_asn = var.router_spoke_configs.landing-ew1.asn
|
||||
peer_gcp_gateway = module.dev-to-landing-ew1-vpn.self_link
|
||||
router_config = {
|
||||
# The router used for this VPN is managed in vpn-prod.tf
|
||||
create = false
|
||||
name = "landing-vpn-ew1"
|
||||
asn = var.router_spoke_configs.landing-ew1.asn
|
||||
}
|
||||
peer_gateway = { gcp = module.dev-to-landing-ew1-vpn.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
|
@ -54,11 +56,7 @@ module "landing-to-dev-ew1-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.0/27", 2)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 0
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
|
@ -69,11 +67,7 @@ module "landing-to-dev-ew1-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.0/27", 6)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 1
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
depends_on = [
|
||||
|
@ -82,15 +76,16 @@ module "landing-to-dev-ew1-vpn" {
|
|||
}
|
||||
|
||||
module "dev-to-landing-ew1-vpn" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.dev-spoke-project.project_id
|
||||
network = module.dev-spoke-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-landing-ew1"
|
||||
router_create = true
|
||||
router_name = "dev-spoke-vpn-ew1"
|
||||
router_asn = var.router_spoke_configs.spoke-dev-ew1.asn
|
||||
peer_gcp_gateway = module.landing-to-dev-ew1-vpn.self_link
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.dev-spoke-project.project_id
|
||||
network = module.dev-spoke-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-landing-ew1"
|
||||
router_config = {
|
||||
name = "dev-spoke-vpn-ew1"
|
||||
asn = var.router_spoke_configs.spoke-dev-ew1.asn
|
||||
}
|
||||
peer_gateway = { gcp = module.landing-to-dev-ew1-vpn.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
|
@ -101,11 +96,8 @@ module "dev-to-landing-ew1-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.0/27", 1)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-dev-ew1-vpn.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
shared_secret = module.landing-to-dev-ew1-vpn.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
|
@ -116,11 +108,8 @@ module "dev-to-landing-ew1-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.0/27", 5)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-dev-ew1-vpn.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
shared_secret = module.landing-to-dev-ew1-vpn.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,15 +19,16 @@
|
|||
# local.vpn_spoke_bgp_peer_options is defined in the dev VPN file
|
||||
|
||||
module "landing-to-prod-ew1-vpn" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-prod-ew1"
|
||||
router_create = true
|
||||
router_name = "landing-vpn-ew1"
|
||||
router_asn = var.router_spoke_configs.landing-ew1.asn
|
||||
peer_gcp_gateway = module.prod-to-landing-ew1-vpn.self_link
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-prod-ew1"
|
||||
router_config = {
|
||||
name = "landing-vpn-ew1"
|
||||
asn = var.router_spoke_configs.landing-ew1.asn
|
||||
}
|
||||
peer_gateway = { gcp = module.prod-to-landing-ew1-vpn.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
|
@ -38,11 +39,7 @@ module "landing-to-prod-ew1-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.64/27", 2)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 0
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
|
@ -53,25 +50,22 @@ module "landing-to-prod-ew1-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.64/27", 6)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 1
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "prod-to-landing-ew1-vpn" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.prod-spoke-project.project_id
|
||||
network = module.prod-spoke-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-landing-ew1"
|
||||
router_create = true
|
||||
router_name = "prod-spoke-vpn-ew1"
|
||||
router_asn = var.router_spoke_configs.spoke-prod-ew1.asn
|
||||
peer_gcp_gateway = module.landing-to-prod-ew1-vpn.self_link
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.prod-spoke-project.project_id
|
||||
network = module.prod-spoke-vpc.self_link
|
||||
region = "europe-west1"
|
||||
name = "vpn-to-landing-ew1"
|
||||
router_config = {
|
||||
name = "prod-spoke-vpn-ew1"
|
||||
asn = var.router_spoke_configs.spoke-prod-ew1.asn
|
||||
}
|
||||
peer_gateway = { gcp = module.landing-to-prod-ew1-vpn.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
|
@ -82,11 +76,8 @@ module "prod-to-landing-ew1-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.64/27", 1)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-prod-ew1-vpn.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
shared_secret = module.landing-to-prod-ew1-vpn.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
|
@ -97,11 +88,8 @@ module "prod-to-landing-ew1-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.64/27", 5)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-prod-ew1-vpn.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
shared_secret = module.landing-to-prod-ew1-vpn.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,15 +19,16 @@
|
|||
# local.vpn_spoke_bgp_peer_options is defined in the dev VPN file
|
||||
|
||||
module "landing-to-prod-ew4-vpn" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = "europe-west4"
|
||||
name = "vpn-to-prod-ew4"
|
||||
router_create = true
|
||||
router_name = "landing-vpn-ew4"
|
||||
router_asn = var.router_spoke_configs.landing-ew4.asn
|
||||
peer_gcp_gateway = module.prod-to-landing-ew4-vpn.self_link
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.landing-project.project_id
|
||||
network = module.landing-vpc.self_link
|
||||
region = "europe-west4"
|
||||
name = "vpn-to-prod-ew4"
|
||||
router_config = {
|
||||
name = "landing-vpn-ew4"
|
||||
asn = var.router_spoke_configs.landing-ew4.asn
|
||||
}
|
||||
peer_gateway = { gcp = module.prod-to-landing-ew4-vpn.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
|
@ -38,11 +39,7 @@ module "landing-to-prod-ew4-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.96/27", 2)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 0
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
|
@ -53,25 +50,22 @@ module "landing-to-prod-ew4-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.96/27", 6)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = null
|
||||
vpn_gateway_interface = 1
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "prod-to-landing-ew4-vpn" {
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.prod-spoke-project.project_id
|
||||
network = module.prod-spoke-vpc.self_link
|
||||
region = "europe-west4"
|
||||
name = "vpn-to-landing-ew4"
|
||||
router_create = true
|
||||
router_name = "prod-spoke-vpn-ew4"
|
||||
router_asn = var.router_spoke_configs.spoke-prod-ew4.asn
|
||||
peer_gcp_gateway = module.landing-to-prod-ew4-vpn.self_link
|
||||
source = "../../../modules/net-vpn-ha"
|
||||
project_id = module.prod-spoke-project.project_id
|
||||
network = module.prod-spoke-vpc.self_link
|
||||
region = "europe-west4"
|
||||
name = "vpn-to-landing-ew4"
|
||||
router_config = {
|
||||
name = "prod-spoke-vpn-ew4"
|
||||
asn = var.router_spoke_configs.spoke-prod-ew4.asn
|
||||
}
|
||||
peer_gateway = { gcp = module.landing-to-prod-ew4-vpn.self_link }
|
||||
tunnels = {
|
||||
0 = {
|
||||
bgp_peer = {
|
||||
|
@ -82,11 +76,8 @@ module "prod-to-landing-ew4-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.96/27", 1)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-prod-ew4-vpn.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
shared_secret = module.landing-to-prod-ew4-vpn.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
1 = {
|
||||
bgp_peer = {
|
||||
|
@ -97,11 +88,8 @@ module "prod-to-landing-ew4-vpn" {
|
|||
bgp_session_range = "${
|
||||
cidrhost("169.254.0.96/27", 5)
|
||||
}/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.landing-to-prod-ew4-vpn.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
shared_secret = module.landing-to-prod-ew4-vpn.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -224,17 +224,17 @@ vpc_sc_perimeters = {
|
|||
ingress_policies = ["iac"]
|
||||
resources = ["projects/1111111111"]
|
||||
}
|
||||
dev = {
|
||||
egress_policies = ["iac-gcs"]
|
||||
ingress_policies = ["iac"]
|
||||
resources = ["projects/0000000000"]
|
||||
}
|
||||
dev = {
|
||||
landing = {
|
||||
access_levels = ["onprem"]
|
||||
egress_policies = ["iac-gcs"]
|
||||
ingress_policies = ["iac"]
|
||||
resources = ["projects/2222222222"]
|
||||
}
|
||||
prod = {
|
||||
egress_policies = ["iac-gcs"]
|
||||
ingress_policies = ["iac"]
|
||||
resources = ["projects/0000000000"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -169,12 +169,12 @@ module "apigee" {
|
|||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [project_id](variables.tf#L76) | Project ID. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L75) | Project ID. | <code>string</code> | ✓ | |
|
||||
| [endpoint_attachments](variables.tf#L17) | Endpoint attachments. | <code title="map(object({ region = string service_attachment = string }))">map(object({…}))</code> | | <code>null</code> |
|
||||
| [envgroups](variables.tf#L26) | Environment groups (NAME => [HOSTNAMES]). | <code>map(list(string))</code> | | <code>null</code> |
|
||||
| [environments](variables.tf#L32) | Environments. | <code title="map(object({ display_name = optional(string) description = optional(string, "Terraform-managed") node_config = optional(object({ min_node_count = optional(number) max_node_count = optional(number) current_aggregate_node_count = number })) iam = optional(map(list(string))) envgroups = list(string) }))">map(object({…}))</code> | | <code>null</code> |
|
||||
| [instances](variables.tf#L48) | Instances. | <code title="map(object({ display_name = optional(string) description = optional(string, "Terraform-managed") region = string environments = list(string) psa_ip_cidr_range = string disk_encryption_key = optional(string) consumer_accept_list = optional(list(string)) }))">map(object({…}))</code> | | <code>null</code> |
|
||||
| [organization](variables.tf#L62) | Apigee organization. If set to null the organization must already exist. | <code title="object({ display_name = optional(string) description = optional(string, "Terraform-managed") authorized_network = optional(string) runtime_type = optional(string, "CLOUD") billing_type = optional(string) database_encryption_key = optional(string) analytics_region = optional(string, "europe-west1") })">object({…})</code> | | <code>null</code> |
|
||||
| [environments](variables.tf#L32) | Environments. | <code title="map(object({ display_name = optional(string) description = optional(string, "Terraform-managed") node_config = optional(object({ min_node_count = optional(number) max_node_count = optional(number) })) iam = optional(map(list(string))) envgroups = list(string) }))">map(object({…}))</code> | | <code>null</code> |
|
||||
| [instances](variables.tf#L47) | Instances. | <code title="map(object({ display_name = optional(string) description = optional(string, "Terraform-managed") region = string environments = list(string) psa_ip_cidr_range = string disk_encryption_key = optional(string) consumer_accept_list = optional(list(string)) }))">map(object({…}))</code> | | <code>null</code> |
|
||||
| [organization](variables.tf#L61) | Apigee organization. If set to null the organization must already exist. | <code title="object({ display_name = optional(string) description = optional(string, "Terraform-managed") authorized_network = optional(string) runtime_type = optional(string, "CLOUD") billing_type = optional(string) database_encryption_key = optional(string) analytics_region = optional(string, "europe-west1") })">object({…})</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -47,12 +47,16 @@ resource "google_apigee_environment" "environments" {
|
|||
dynamic "node_config" {
|
||||
for_each = try(each.value.node_config, null) != null ? [""] : []
|
||||
content {
|
||||
min_node_count = node_config.min_node_count
|
||||
max_node_count = node_config.max_node_count
|
||||
current_aggregate_node_count = node_config.current_aggregate_node_count
|
||||
min_node_count = each.value.node_config.min_node_count
|
||||
max_node_count = each.value.node_config.max_node_count
|
||||
}
|
||||
}
|
||||
org_id = local.org_id
|
||||
lifecycle {
|
||||
ignore_changes = [
|
||||
node_config["current_aggregate_node_count"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_apigee_envgroup_attachment" "envgroup_attachments" {
|
||||
|
|
|
@ -35,9 +35,8 @@ variable "environments" {
|
|||
display_name = optional(string)
|
||||
description = optional(string, "Terraform-managed")
|
||||
node_config = optional(object({
|
||||
min_node_count = optional(number)
|
||||
max_node_count = optional(number)
|
||||
current_aggregate_node_count = number
|
||||
min_node_count = optional(number)
|
||||
max_node_count = optional(number)
|
||||
}))
|
||||
iam = optional(map(list(string)))
|
||||
envgroups = list(string)
|
||||
|
@ -76,4 +75,4 @@ variable "organization" {
|
|||
variable "project_id" {
|
||||
description = "Project ID."
|
||||
type = string
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,17 +24,15 @@ The test instance is optional, as described above.
|
|||
|
||||
```hcl
|
||||
module "cloud-vpn" {
|
||||
source = "./fabric/modules/net-vpn-static"
|
||||
project_id = "my-project"
|
||||
region = "europe-west1"
|
||||
network = "my-vpc"
|
||||
name = "to-on-prem"
|
||||
source = "./fabric/modules/net-vpn-static"
|
||||
project_id = "my-project"
|
||||
region = "europe-west1"
|
||||
network = "my-vpc"
|
||||
name = "to-on-prem"
|
||||
remote_ranges = ["192.168.192.0/24"]
|
||||
tunnels = {
|
||||
remote-0 = {
|
||||
ike_version = 2
|
||||
peer_ip = module.on-prem.external_address
|
||||
shared_secret = ""
|
||||
traffic_selectors = { local = ["0.0.0.0/0"], remote = null }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,13 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM alpine:latest
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
RUN set -xe \
|
||||
&& apk add --no-cache strongswan bash sudo
|
||||
ENV STRONGSWAN_VERSION=5.9
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y sudo iptables procps strongswan=${STRONGSWAN_VERSION}* \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod 0755 /entrypoint.sh
|
||||
|
|
|
@ -22,7 +22,7 @@ _stop_ipsec() {
|
|||
echo "Shutting down strongSwan/ipsec..."
|
||||
ipsec stop
|
||||
}
|
||||
trap _stop_ipsec SIGTERM
|
||||
trap _stop_ipsec TERM
|
||||
|
||||
# Making the containter to work as a default gateway for LAN_NETWORKS
|
||||
iptables -t nat -A POSTROUTING -s ${LAN_NETWORKS} -o ${VPN_DEVICE} -m policy --dir out --pol ipsec -j ACCEPT
|
||||
|
|
|
@ -176,7 +176,7 @@ healthchecks:
|
|||
|---|---|:---:|:---:|:---:|
|
||||
| [network](variables.tf#L109) | Name of the network this set of firewall rules applies to. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L114) | Project id of the project that holds the network. | <code>string</code> | ✓ | |
|
||||
| [default_rules_config](variables.tf#L17) | Optionally created convenience rules. Set the variable or individual members to null to disable. | <code title="object({ admin_ranges = optional(list(string)) disabled = optional(bool, false) http_ranges = optional(list(string), [ "35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] ) http_tags = optional(list(string), ["http-server"]) https_ranges = optional(list(string), [ "35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] ) https_tags = optional(list(string), ["https-server"]) ssh_ranges = optional(list(string), ["35.235.240.0/20"]) ssh_tags = optional(list(string), ["ssh"]) })">object({…})</code> | | <code>{}</code> |
|
||||
| [default_rules_config](variables.tf#L17) | Optionally created convenience rules. Set the 'disabled' attribute to true, or individual rule attributes to empty lists to disable. | <code title="object({ admin_ranges = optional(list(string)) disabled = optional(bool, false) http_ranges = optional(list(string), [ "35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] ) http_tags = optional(list(string), ["http-server"]) https_ranges = optional(list(string), [ "35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] ) https_tags = optional(list(string), ["https-server"]) ssh_ranges = optional(list(string), ["35.235.240.0/20"]) ssh_tags = optional(list(string), ["ssh"]) })">object({…})</code> | | <code>{}</code> |
|
||||
| [egress_rules](variables.tf#L37) | List of egress rule definitions, default to deny action. | <code title="map(object({ deny = optional(bool, true) description = optional(string) destination_ranges = optional(list(string)) disabled = optional(bool, false) enable_logging = optional(object({ include_metadata = optional(bool) })) priority = optional(number, 1000) sources = optional(list(string)) targets = optional(list(string)) use_service_accounts = optional(bool, false) rules = optional(list(object({ protocol = string ports = optional(list(string)) })), [{ protocol = "all" }]) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [factories_config](variables.tf#L60) | Paths to data files and folders that enable factory functionality. | <code title="object({ cidr_tpl_file = optional(string) rules_folder = string })">object({…})</code> | | <code>null</code> |
|
||||
| [ingress_rules](variables.tf#L69) | List of ingress rule definitions, default to allow action. | <code title="map(object({ deny = optional(bool, false) description = optional(string) disabled = optional(bool, false) enable_logging = optional(object({ include_metadata = optional(bool) })) priority = optional(number, 1000) source_ranges = optional(list(string)) sources = optional(list(string)) targets = optional(list(string)) use_service_accounts = optional(bool, false) rules = optional(list(object({ protocol = string ports = optional(list(string)) })), [{ protocol = "all" }]) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
variable "default_rules_config" {
|
||||
description = "Optionally created convenience rules. Set the variable or individual members to null to disable."
|
||||
description = "Optionally created convenience rules. Set the 'disabled' attribute to true, or individual rule attributes to empty lists to disable."
|
||||
type = object({
|
||||
admin_ranges = optional(list(string))
|
||||
disabled = optional(bool, false)
|
||||
|
|
|
@ -8,35 +8,50 @@ This example shows how to configure a single VPN tunnel using a couple of extra
|
|||
- internally generated shared secret, which can be fetched from the module's `random_secret` output for reuse; a predefined secret can be used instead by assigning it to the `shared_secret` attribute
|
||||
|
||||
```hcl
|
||||
module "vm" {
|
||||
source = "./fabric/modules/compute-vm"
|
||||
project_id = "my-project"
|
||||
zone = "europe-west1-b"
|
||||
name = "my-vm"
|
||||
network_interfaces = [{
|
||||
nat = true
|
||||
network = var.vpc.self_link
|
||||
subnetwork = var.subnet.self_link
|
||||
}]
|
||||
service_account_create = true
|
||||
}
|
||||
|
||||
|
||||
module "vpn-dynamic" {
|
||||
source = "./fabric/modules/net-vpn-dynamic"
|
||||
project_id = "my-project"
|
||||
region = "europe-west1"
|
||||
network = "my-vpc"
|
||||
network = var.vpc.name
|
||||
name = "gateway-1"
|
||||
router_config = {
|
||||
asn = 64514
|
||||
}
|
||||
|
||||
tunnels = {
|
||||
remote-1 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.139.134"
|
||||
asn = 64513
|
||||
custom_advertise = {
|
||||
all_subnets = true
|
||||
all_vpc_subnets = false
|
||||
all_peer_vpc_subnets = false
|
||||
ip_ranges = {
|
||||
"192.168.0.0/24" = "Advertised range description"
|
||||
}
|
||||
}
|
||||
}
|
||||
bgp_session_range = "169.254.139.133/30"
|
||||
ike_version = 2
|
||||
peer_ip = "1.1.1.1"
|
||||
router = null
|
||||
shared_secret = null
|
||||
bgp_peer_options = {
|
||||
advertise_groups = ["ALL_SUBNETS"]
|
||||
advertise_ip_ranges = {
|
||||
"192.168.0.0/24" = "Advertised range description"
|
||||
}
|
||||
advertise_mode = "CUSTOM"
|
||||
route_priority = 1000
|
||||
}
|
||||
peer_ip = module.vm.external_ip
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=10
|
||||
# tftest modules=2 resources=12
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
|
@ -48,14 +63,10 @@ module "vpn-dynamic" {
|
|||
| [network](variables.tf#L34) | VPC used for the gateway and routes. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L39) | Project where resources will be created. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L44) | Region used for resources. | <code>string</code> | ✓ | |
|
||||
| [gateway_address](variables.tf#L17) | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | <code>string</code> | | <code>""</code> |
|
||||
| [router_config](variables.tf#L49) | Cloud Router configuration for the VPN. If you want to reuse an existing router, set create to false and use name to specify the desired router. | <code title="object({ create = optional(bool, true) asn = number name = optional(string) keepalive = optional(number) custom_advertise = optional(object({ all_subnets = bool ip_ranges = map(string) })) })">object({…})</code> | ✓ | |
|
||||
| [gateway_address](variables.tf#L17) | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | <code>string</code> | | <code>null</code> |
|
||||
| [gateway_address_create](variables.tf#L23) | Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable. | <code>bool</code> | | <code>true</code> |
|
||||
| [route_priority](variables.tf#L49) | Route priority, defaults to 1000. | <code>number</code> | | <code>1000</code> |
|
||||
| [router_advertise_config](variables.tf#L55) | Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions. | <code title="object({ groups = list(string) ip_ranges = map(string) mode = string })">object({…})</code> | | <code>null</code> |
|
||||
| [router_asn](variables.tf#L65) | Router ASN used for auto-created router. | <code>number</code> | | <code>64514</code> |
|
||||
| [router_create](variables.tf#L71) | Create router. | <code>bool</code> | | <code>true</code> |
|
||||
| [router_name](variables.tf#L77) | Router name used for auto created router, or to specify existing router to use. Leave blank to use VPN name for auto created router. | <code>string</code> | | <code>""</code> |
|
||||
| [tunnels](variables.tf#L83) | VPN tunnel configurations, bgp_peer_options is usually null. | <code title="map(object({ bgp_peer = object({ address = string asn = number }) bgp_peer_options = object({ advertise_groups = list(string) advertise_ip_ranges = map(string) advertise_mode = string route_priority = number }) bgp_session_range = string ike_version = number peer_ip = string router = string shared_secret = string }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [tunnels](variables.tf#L64) | VPN tunnel configurations. | <code title="map(object({ bgp_peer = object({ address = string asn = number route_priority = optional(number, 1000) custom_advertise = optional(object({ all_subnets = bool all_vpc_subnets = bool all_peer_vpc_subnets = bool ip_ranges = map(string) })) }) bgp_session_range = string ike_version = optional(number, 2) peer_ip = string router = optional(string) shared_secret = optional(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ locals {
|
|||
: var.gateway_address
|
||||
)
|
||||
router = (
|
||||
var.router_create
|
||||
? google_compute_router.router[0].name
|
||||
: var.router_name
|
||||
var.router_config.create
|
||||
? try(google_compute_router.router[0].name, null)
|
||||
: var.router_config.name
|
||||
)
|
||||
secret = random_id.secret.b64_url
|
||||
}
|
||||
|
@ -65,75 +65,56 @@ resource "google_compute_forwarding_rule" "udp-4500" {
|
|||
}
|
||||
|
||||
resource "google_compute_router" "router" {
|
||||
count = var.router_create ? 1 : 0
|
||||
name = var.router_name == "" ? "vpn-${var.name}" : var.router_name
|
||||
count = var.router_config.create ? 1 : 0
|
||||
name = coalesce(var.router_config.name, "vpn-${var.name}")
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
network = var.network
|
||||
bgp {
|
||||
advertise_mode = (
|
||||
var.router_advertise_config == null
|
||||
? null
|
||||
: var.router_advertise_config.mode
|
||||
var.router_config.custom_advertise != null
|
||||
? "CUSTOM"
|
||||
: "DEFAULT"
|
||||
)
|
||||
advertised_groups = (
|
||||
var.router_advertise_config == null ? null : (
|
||||
var.router_advertise_config.mode != "CUSTOM"
|
||||
? null
|
||||
: var.router_advertise_config.groups
|
||||
)
|
||||
try(var.router_config.custom_advertise.all_subnets, false)
|
||||
? ["ALL_SUBNETS"]
|
||||
: []
|
||||
)
|
||||
dynamic "advertised_ip_ranges" {
|
||||
for_each = (
|
||||
var.router_advertise_config == null ? {} : (
|
||||
var.router_advertise_config.mode != "CUSTOM"
|
||||
? null
|
||||
: var.router_advertise_config.ip_ranges
|
||||
)
|
||||
)
|
||||
for_each = try(var.router_config.custom_advertise.ip_ranges, {})
|
||||
iterator = range
|
||||
content {
|
||||
range = range.key
|
||||
description = range.value
|
||||
}
|
||||
}
|
||||
asn = var.router_asn
|
||||
keepalive_interval = try(var.router_config.keepalive, null)
|
||||
asn = var.router_config.asn
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_router_peer" "bgp_peer" {
|
||||
for_each = var.tunnels
|
||||
region = var.region
|
||||
project = var.project_id
|
||||
name = "${var.name}-${each.key}"
|
||||
router = each.value.router == null ? local.router : each.value.router
|
||||
peer_ip_address = each.value.bgp_peer.address
|
||||
peer_asn = each.value.bgp_peer.asn
|
||||
advertised_route_priority = (
|
||||
each.value.bgp_peer_options == null ? var.route_priority : (
|
||||
each.value.bgp_peer_options.route_priority == null
|
||||
? var.route_priority
|
||||
: each.value.bgp_peer_options.route_priority
|
||||
)
|
||||
)
|
||||
for_each = var.tunnels
|
||||
region = var.region
|
||||
project = var.project_id
|
||||
name = "${var.name}-${each.key}"
|
||||
router = coalesce(each.value.router, local.router)
|
||||
peer_ip_address = each.value.bgp_peer.address
|
||||
peer_asn = each.value.bgp_peer.asn
|
||||
advertised_route_priority = each.value.bgp_peer.route_priority
|
||||
advertise_mode = (
|
||||
each.value.bgp_peer_options == null ? null : each.value.bgp_peer_options.advertise_mode
|
||||
try(each.value.bgp_peer.custom_advertise, null) != null
|
||||
? "CUSTOM"
|
||||
: "DEFAULT"
|
||||
)
|
||||
advertised_groups = (
|
||||
each.value.bgp_peer_options == null ? null : (
|
||||
each.value.bgp_peer_options.advertise_mode != "CUSTOM"
|
||||
? null
|
||||
: each.value.bgp_peer_options.advertise_groups
|
||||
)
|
||||
advertised_groups = concat(
|
||||
try(each.value.bgp_peer.custom_advertise.all_subnets, false) ? ["ALL_SUBNETS"] : [],
|
||||
try(each.value.bgp_peer.custom_advertise.all_vpc_subnets, false) ? ["ALL_VPC_SUBNETS"] : [],
|
||||
try(each.value.bgp_peer.custom_advertise.all_peer_vpc_subnets, false) ? ["ALL_PEER_VPC_SUBNETS"] : []
|
||||
)
|
||||
dynamic "advertised_ip_ranges" {
|
||||
for_each = (
|
||||
each.value.bgp_peer_options == null ? {} : (
|
||||
each.value.bgp_peer_options.advertise_mode != "CUSTOM"
|
||||
? {}
|
||||
: each.value.bgp_peer_options.advertise_ip_ranges
|
||||
)
|
||||
)
|
||||
for_each = try(each.value.bgp_peer.custom_advertise.ip_ranges, {})
|
||||
iterator = range
|
||||
content {
|
||||
range = range.key
|
||||
|
@ -144,11 +125,12 @@ resource "google_compute_router_peer" "bgp_peer" {
|
|||
}
|
||||
|
||||
resource "google_compute_router_interface" "router_interface" {
|
||||
for_each = var.tunnels
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
name = "${var.name}-${each.key}"
|
||||
router = each.value.router == null ? local.router : each.value.router
|
||||
for_each = var.tunnels
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
name = "${var.name}-${each.key}"
|
||||
router = coalesce(each.value.router, local.router)
|
||||
# FIXME: can bgp_session_range be null?
|
||||
ip_range = each.value.bgp_session_range == "" ? null : each.value.bgp_session_range
|
||||
vpn_tunnel = google_compute_vpn_tunnel.tunnels[each.key].name
|
||||
}
|
||||
|
@ -161,18 +143,14 @@ resource "google_compute_vpn_gateway" "gateway" {
|
|||
}
|
||||
|
||||
resource "google_compute_vpn_tunnel" "tunnels" {
|
||||
for_each = var.tunnels
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
name = "${var.name}-${each.key}"
|
||||
router = each.value.router == null ? local.router : each.value.router
|
||||
peer_ip = each.value.peer_ip
|
||||
ike_version = each.value.ike_version
|
||||
shared_secret = (
|
||||
each.value.shared_secret == "" || each.value.shared_secret == null
|
||||
? local.secret
|
||||
: each.value.shared_secret
|
||||
)
|
||||
for_each = var.tunnels
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
name = "${var.name}-${each.key}"
|
||||
router = coalesce(each.value.router, local.router)
|
||||
peer_ip = each.value.peer_ip
|
||||
ike_version = each.value.ike_version
|
||||
shared_secret = coalesce(each.value.shared_secret, local.secret)
|
||||
target_vpn_gateway = google_compute_vpn_gateway.gateway.self_link
|
||||
depends_on = [google_compute_forwarding_rule.esp]
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ output "random_secret" {
|
|||
|
||||
output "router" {
|
||||
description = "Router resource (only if auto-created)."
|
||||
value = var.router_create ? google_compute_router.router[0] : null
|
||||
value = one(google_compute_router.router[*])
|
||||
}
|
||||
|
||||
output "router_name" {
|
||||
|
@ -54,7 +54,7 @@ output "tunnel_names" {
|
|||
description = "VPN tunnel names."
|
||||
value = {
|
||||
for name in keys(var.tunnels) :
|
||||
name => google_compute_vpn_tunnel.tunnels[name].name
|
||||
name => try(google_compute_vpn_tunnel.tunnels[name].name, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ output "tunnel_self_links" {
|
|||
description = "VPN tunnel self links."
|
||||
value = {
|
||||
for name in keys(var.tunnels) :
|
||||
name => google_compute_vpn_tunnel.tunnels[name].self_link
|
||||
name => try(google_compute_vpn_tunnel.tunnels[name].self_link, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,6 @@ output "tunnels" {
|
|||
description = "VPN tunnel resources."
|
||||
value = {
|
||||
for name in keys(var.tunnels) :
|
||||
name => google_compute_vpn_tunnel.tunnels[name]
|
||||
name => try(google_compute_vpn_tunnel.tunnels[name], null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
variable "gateway_address" {
|
||||
description = "Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false."
|
||||
type = string
|
||||
default = ""
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "gateway_address_create" {
|
||||
|
@ -46,60 +46,43 @@ variable "region" {
|
|||
type = string
|
||||
}
|
||||
|
||||
variable "route_priority" {
|
||||
description = "Route priority, defaults to 1000."
|
||||
type = number
|
||||
default = 1000
|
||||
}
|
||||
|
||||
variable "router_advertise_config" {
|
||||
description = "Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions."
|
||||
variable "router_config" {
|
||||
description = "Cloud Router configuration for the VPN. If you want to reuse an existing router, set create to false and use name to specify the desired router."
|
||||
type = object({
|
||||
groups = list(string)
|
||||
ip_ranges = map(string)
|
||||
mode = string
|
||||
create = optional(bool, true)
|
||||
asn = number
|
||||
name = optional(string)
|
||||
keepalive = optional(number)
|
||||
custom_advertise = optional(object({
|
||||
all_subnets = bool
|
||||
ip_ranges = map(string)
|
||||
}))
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "router_asn" {
|
||||
description = "Router ASN used for auto-created router."
|
||||
type = number
|
||||
default = 64514
|
||||
}
|
||||
|
||||
variable "router_create" {
|
||||
description = "Create router."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "router_name" {
|
||||
description = "Router name used for auto created router, or to specify existing router to use. Leave blank to use VPN name for auto created router."
|
||||
type = string
|
||||
default = ""
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "tunnels" {
|
||||
description = "VPN tunnel configurations, bgp_peer_options is usually null."
|
||||
description = "VPN tunnel configurations."
|
||||
type = map(object({
|
||||
bgp_peer = object({
|
||||
address = string
|
||||
asn = number
|
||||
})
|
||||
bgp_peer_options = object({
|
||||
advertise_groups = list(string)
|
||||
advertise_ip_ranges = map(string)
|
||||
advertise_mode = string
|
||||
route_priority = number
|
||||
address = string
|
||||
asn = number
|
||||
route_priority = optional(number, 1000)
|
||||
custom_advertise = optional(object({
|
||||
all_subnets = bool
|
||||
all_vpc_subnets = bool
|
||||
all_peer_vpc_subnets = bool
|
||||
ip_ranges = map(string)
|
||||
}))
|
||||
})
|
||||
# each BGP session on the same Cloud Router must use a unique /30 CIDR
|
||||
# from the 169.254.0.0/16 block.
|
||||
bgp_session_range = string
|
||||
ike_version = number
|
||||
ike_version = optional(number, 2)
|
||||
peer_ip = string
|
||||
router = string
|
||||
shared_secret = string
|
||||
router = optional(string)
|
||||
shared_secret = optional(string)
|
||||
}))
|
||||
default = {}
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
|
|
@ -5,20 +5,21 @@ This module makes it easy to deploy either GCP-to-GCP or GCP-to-On-prem [Cloud H
|
|||
|
||||
### GCP to GCP
|
||||
```hcl
|
||||
module "vpn_ha-1" {
|
||||
source = "./fabric/modules/net-vpn-ha"
|
||||
project_id = "<PROJECT_ID>"
|
||||
region = "europe-west4"
|
||||
network = "https://www.googleapis.com/compute/v1/projects/<PROJECT_ID>/global/networks/network-1"
|
||||
name = "net1-to-net-2"
|
||||
peer_gcp_gateway = module.vpn_ha-2.self_link
|
||||
router_asn = 64514
|
||||
router_advertise_config = {
|
||||
groups = ["ALL_SUBNETS"]
|
||||
ip_ranges = {
|
||||
"10.0.0.0/8" = "default"
|
||||
module "vpn-1" {
|
||||
source = "./fabric/modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
region = "europe-west4"
|
||||
network = var.vpc1.self_link
|
||||
name = "net1-to-net-2"
|
||||
peer_gateway = { gcp = module.vpn-2.self_link }
|
||||
router_config = {
|
||||
asn = 64514
|
||||
custom_advertise = {
|
||||
all_subnets = true
|
||||
ip_ranges = {
|
||||
"10.0.0.0/8" = "default"
|
||||
}
|
||||
}
|
||||
mode = "CUSTOM"
|
||||
}
|
||||
tunnels = {
|
||||
remote-0 = {
|
||||
|
@ -26,64 +27,46 @@ module "vpn_ha-1" {
|
|||
address = "169.254.1.1"
|
||||
asn = 64513
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.1.2/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = ""
|
||||
vpn_gateway_interface = 0
|
||||
bgp_session_range = "169.254.1.2/30"
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
remote-1 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.2.1"
|
||||
asn = 64513
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.2.2/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = ""
|
||||
vpn_gateway_interface = 1
|
||||
bgp_session_range = "169.254.2.2/30"
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "vpn_ha-2" {
|
||||
source = "./fabric/modules/net-vpn-ha"
|
||||
project_id = "<PROJECT_ID>"
|
||||
region = "europe-west4"
|
||||
network = "https://www.googleapis.com/compute/v1/projects/<PROJECT_ID>/global/networks/local-network"
|
||||
name = "net2-to-net1"
|
||||
router_asn = 64513
|
||||
peer_gcp_gateway = module.vpn_ha-1.self_link
|
||||
module "vpn-2" {
|
||||
source = "./fabric/modules/net-vpn-ha"
|
||||
project_id = var.project_id
|
||||
region = "europe-west4"
|
||||
network = var.vpc2.self_link
|
||||
name = "net2-to-net1"
|
||||
router_config = { asn = 64513 }
|
||||
peer_gateway = { gcp = module.vpn-1.self_link}
|
||||
tunnels = {
|
||||
remote-0 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.1.2"
|
||||
asn = 64514
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.1.1/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.vpn_ha-1.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
bgp_session_range = "169.254.1.1/30"
|
||||
shared_secret = module.vpn-1.random_secret
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
remote-1 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.2.2"
|
||||
asn = 64514
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.2.1/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = null
|
||||
router = null
|
||||
shared_secret = module.vpn_ha-1.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
bgp_session_range = "169.254.2.1/30"
|
||||
shared_secret = module.vpn-1.random_secret
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,25 +84,21 @@ module "vpn_ha" {
|
|||
region = var.region
|
||||
network = var.vpc.self_link
|
||||
name = "mynet-to-onprem"
|
||||
peer_external_gateway = {
|
||||
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
|
||||
interfaces = [{
|
||||
id = 0
|
||||
ip_address = "8.8.8.8" # on-prem router ip address
|
||||
}]
|
||||
peer_gateway = {
|
||||
external = {
|
||||
redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT"
|
||||
interfaces = ["8.8.8.8"] # on-prem router ip address
|
||||
}
|
||||
}
|
||||
router_asn = 64514
|
||||
router_config = { asn = 64514 }
|
||||
tunnels = {
|
||||
remote-0 = {
|
||||
bgp_peer = {
|
||||
address = "169.254.1.1"
|
||||
asn = 64513
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.1.2/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = 0
|
||||
router = null
|
||||
shared_secret = "mySecret"
|
||||
vpn_gateway_interface = 0
|
||||
}
|
||||
|
@ -128,11 +107,8 @@ module "vpn_ha" {
|
|||
address = "169.254.2.1"
|
||||
asn = 64513
|
||||
}
|
||||
bgp_peer_options = null
|
||||
bgp_session_range = "169.254.2.2/30"
|
||||
ike_version = 2
|
||||
peer_external_gateway_interface = 0
|
||||
router = null
|
||||
shared_secret = "mySecret"
|
||||
vpn_gateway_interface = 1
|
||||
}
|
||||
|
@ -148,18 +124,13 @@ module "vpn_ha" {
|
|||
|---|---|:---:|:---:|:---:|
|
||||
| [name](variables.tf#L17) | VPN Gateway name (if an existing VPN Gateway is not used), and prefix used for dependent resources. | <code>string</code> | ✓ | |
|
||||
| [network](variables.tf#L22) | VPC used for the gateway and routes. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L45) | Project where resources will be created. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L50) | Region used for resources. | <code>string</code> | ✓ | |
|
||||
| [peer_external_gateway](variables.tf#L27) | Configuration of an external VPN gateway to which this VPN is connected. | <code title="object({ redundancy_type = string interfaces = list(object({ id = number ip_address = string })) })">object({…})</code> | | <code>null</code> |
|
||||
| [peer_gcp_gateway](variables.tf#L39) | Self Link URL of the peer side HA GCP VPN gateway to which this VPN tunnel is connected. | <code>string</code> | | <code>null</code> |
|
||||
| [route_priority](variables.tf#L55) | Route priority, defaults to 1000. | <code>number</code> | | <code>1000</code> |
|
||||
| [router_advertise_config](variables.tf#L61) | Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions. | <code title="object({ groups = list(string) ip_ranges = map(string) mode = string })">object({…})</code> | | <code>null</code> |
|
||||
| [router_asn](variables.tf#L71) | Router ASN used for auto-created router. | <code>number</code> | | <code>64514</code> |
|
||||
| [router_create](variables.tf#L77) | Create router. | <code>bool</code> | | <code>true</code> |
|
||||
| [router_name](variables.tf#L83) | Router name used for auto created router, or to specify an existing router to use if `router_create` is set to `true`. Leave blank to use VPN name for auto created router. | <code>string</code> | | <code>""</code> |
|
||||
| [tunnels](variables.tf#L89) | VPN tunnel configurations, bgp_peer_options is usually null. | <code title="map(object({ bgp_peer = object({ address = string asn = number }) bgp_peer_options = object({ advertise_groups = list(string) advertise_ip_ranges = map(string) advertise_mode = string route_priority = number }) bgp_session_range = string ike_version = number peer_external_gateway_interface = number router = string shared_secret = string vpn_gateway_interface = number }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [vpn_gateway](variables.tf#L114) | HA VPN Gateway Self Link for using an existing HA VPN Gateway, leave empty if `vpn_gateway_create` is set to `true`. | <code>string</code> | | <code>null</code> |
|
||||
| [vpn_gateway_create](variables.tf#L120) | Create HA VPN Gateway. | <code>bool</code> | | <code>true</code> |
|
||||
| [peer_gateway](variables.tf#L27) | Configuration of the (external or GCP) peer gateway. | <code title="object({ external = optional(object({ redundancy_type = string interfaces = list(string) })) gcp = optional(string) })">object({…})</code> | ✓ | |
|
||||
| [project_id](variables.tf#L43) | Project where resources will be created. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L48) | Region used for resources. | <code>string</code> | ✓ | |
|
||||
| [router_config](variables.tf#L53) | Cloud Router configuration for the VPN. If you want to reuse an existing router, set create to false and use name to specify the desired router. | <code title="object({ create = optional(bool, true) asn = number name = optional(string) keepalive = optional(number) custom_advertise = optional(object({ all_subnets = bool ip_ranges = map(string) })) })">object({…})</code> | ✓ | |
|
||||
| [tunnels](variables.tf#L68) | VPN tunnel configurations. | <code title="map(object({ bgp_peer = object({ address = string asn = number route_priority = optional(number, 1000) custom_advertise = optional(object({ all_subnets = bool all_vpc_subnets = bool all_peer_vpc_subnets = bool ip_ranges = map(string) })) }) bgp_session_range = string ike_version = optional(number, 2) peer_external_gateway_interface = optional(number) router = optional(string) shared_secret = optional(string) vpn_gateway_interface = number }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [vpn_gateway](variables.tf#L95) | HA VPN Gateway Self Link for using an existing HA VPN Gateway. Ignored if `vpn_gateway_create` is set to `true`. | <code>string</code> | | <code>null</code> |
|
||||
| [vpn_gateway_create](variables.tf#L101) | Create HA VPN Gateway. | <code>bool</code> | | <code>true</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
@ -167,14 +138,14 @@ module "vpn_ha" {
|
|||
|---|---|:---:|
|
||||
| [bgp_peers](outputs.tf#L18) | BGP peer resources. | |
|
||||
| [external_gateway](outputs.tf#L25) | External VPN gateway resource. | |
|
||||
| [gateway](outputs.tf#L34) | VPN gateway resource (only if auto-created). | |
|
||||
| [name](outputs.tf#L43) | VPN gateway name (only if auto-created). . | |
|
||||
| [random_secret](outputs.tf#L52) | Generated secret. | |
|
||||
| [router](outputs.tf#L57) | Router resource (only if auto-created). | |
|
||||
| [router_name](outputs.tf#L66) | Router name. | |
|
||||
| [self_link](outputs.tf#L71) | HA VPN gateway self link. | |
|
||||
| [tunnel_names](outputs.tf#L76) | VPN tunnel names. | |
|
||||
| [tunnel_self_links](outputs.tf#L84) | VPN tunnel self links. | |
|
||||
| [tunnels](outputs.tf#L92) | VPN tunnel resources. | |
|
||||
| [gateway](outputs.tf#L30) | VPN gateway resource (only if auto-created). | |
|
||||
| [name](outputs.tf#L35) | VPN gateway name (only if auto-created). . | |
|
||||
| [random_secret](outputs.tf#L40) | Generated secret. | |
|
||||
| [router](outputs.tf#L45) | Router resource (only if auto-created). | |
|
||||
| [router_name](outputs.tf#L50) | Router name. | |
|
||||
| [self_link](outputs.tf#L55) | HA VPN gateway self link. | |
|
||||
| [tunnel_names](outputs.tf#L60) | VPN tunnel names. | |
|
||||
| [tunnel_self_links](outputs.tf#L68) | VPN tunnel self links. | |
|
||||
| [tunnels](outputs.tf#L76) | VPN tunnel resources. | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -16,16 +16,10 @@
|
|||
*/
|
||||
|
||||
locals {
|
||||
peer_external_gateway = (
|
||||
var.peer_external_gateway != null
|
||||
? google_compute_external_vpn_gateway.external_gateway[0].self_link
|
||||
: null
|
||||
|
||||
)
|
||||
router = (
|
||||
var.router_create
|
||||
var.router_config.create
|
||||
? try(google_compute_router.router[0].name, null)
|
||||
: var.router_name
|
||||
: var.router_config.name
|
||||
)
|
||||
vpn_gateway = (
|
||||
var.vpn_gateway_create
|
||||
|
@ -36,100 +30,79 @@ locals {
|
|||
}
|
||||
|
||||
resource "google_compute_ha_vpn_gateway" "ha_gateway" {
|
||||
provider = google-beta
|
||||
count = var.vpn_gateway_create ? 1 : 0
|
||||
name = var.name
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
network = var.network
|
||||
count = var.vpn_gateway_create ? 1 : 0
|
||||
name = var.name
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
network = var.network
|
||||
}
|
||||
|
||||
resource "google_compute_external_vpn_gateway" "external_gateway" {
|
||||
provider = google-beta
|
||||
count = var.peer_external_gateway != null ? 1 : 0
|
||||
count = var.peer_gateway.external != null ? 1 : 0
|
||||
name = "external-${var.name}"
|
||||
project = var.project_id
|
||||
redundancy_type = var.peer_external_gateway.redundancy_type
|
||||
redundancy_type = var.peer_gateway.external.redundancy_type
|
||||
description = "Terraform managed external VPN gateway"
|
||||
dynamic "interface" {
|
||||
for_each = var.peer_external_gateway.interfaces
|
||||
for_each = var.peer_gateway.external.interfaces
|
||||
content {
|
||||
id = interface.value.id
|
||||
ip_address = interface.value.ip_address
|
||||
id = interface.key
|
||||
ip_address = interface.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_router" "router" {
|
||||
count = var.router_create ? 1 : 0
|
||||
name = var.router_name == "" ? "vpn-${var.name}" : var.router_name
|
||||
count = var.router_config.create ? 1 : 0
|
||||
name = coalesce(var.router_config.name, "vpn-${var.name}")
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
network = var.network
|
||||
bgp {
|
||||
advertise_mode = (
|
||||
var.router_advertise_config == null
|
||||
? null
|
||||
: var.router_advertise_config.mode
|
||||
var.router_config.custom_advertise != null
|
||||
? "CUSTOM"
|
||||
: "DEFAULT"
|
||||
)
|
||||
advertised_groups = (
|
||||
var.router_advertise_config == null ? null : (
|
||||
var.router_advertise_config.mode != "CUSTOM"
|
||||
? null
|
||||
: var.router_advertise_config.groups
|
||||
)
|
||||
try(var.router_config.custom_advertise.all_subnets, false)
|
||||
? ["ALL_SUBNETS"]
|
||||
: []
|
||||
)
|
||||
dynamic "advertised_ip_ranges" {
|
||||
for_each = (
|
||||
var.router_advertise_config == null ? {} : (
|
||||
var.router_advertise_config.mode != "CUSTOM"
|
||||
? null
|
||||
: var.router_advertise_config.ip_ranges
|
||||
)
|
||||
)
|
||||
for_each = try(var.router_config.custom_advertise.ip_ranges, {})
|
||||
iterator = range
|
||||
content {
|
||||
range = range.key
|
||||
description = range.value
|
||||
}
|
||||
}
|
||||
asn = var.router_asn
|
||||
keepalive_interval = try(var.router_config.keepalive, null)
|
||||
asn = var.router_config.asn
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_router_peer" "bgp_peer" {
|
||||
for_each = var.tunnels
|
||||
region = var.region
|
||||
project = var.project_id
|
||||
name = "${var.name}-${each.key}"
|
||||
router = local.router
|
||||
peer_ip_address = each.value.bgp_peer.address
|
||||
peer_asn = each.value.bgp_peer.asn
|
||||
advertised_route_priority = (
|
||||
each.value.bgp_peer_options == null ? var.route_priority : (
|
||||
each.value.bgp_peer_options.route_priority == null
|
||||
? var.route_priority
|
||||
: each.value.bgp_peer_options.route_priority
|
||||
)
|
||||
)
|
||||
for_each = var.tunnels
|
||||
region = var.region
|
||||
project = var.project_id
|
||||
name = "${var.name}-${each.key}"
|
||||
router = coalesce(each.value.router, local.router)
|
||||
peer_ip_address = each.value.bgp_peer.address
|
||||
peer_asn = each.value.bgp_peer.asn
|
||||
advertised_route_priority = each.value.bgp_peer.route_priority
|
||||
advertise_mode = (
|
||||
each.value.bgp_peer_options == null ? null : each.value.bgp_peer_options.advertise_mode
|
||||
try(each.value.bgp_peer.custom_advertise, null) != null
|
||||
? "CUSTOM"
|
||||
: "DEFAULT"
|
||||
)
|
||||
advertised_groups = (
|
||||
each.value.bgp_peer_options == null ? null : (
|
||||
each.value.bgp_peer_options.advertise_mode != "CUSTOM"
|
||||
? null
|
||||
: each.value.bgp_peer_options.advertise_groups
|
||||
)
|
||||
advertised_groups = concat(
|
||||
try(each.value.bgp_peer.custom_advertise.all_subnets, false) ? ["ALL_SUBNETS"] : [],
|
||||
try(each.value.bgp_peer.custom_advertise.all_vpc_subnets, false) ? ["ALL_VPC_SUBNETS"] : [],
|
||||
try(each.value.bgp_peer.custom_advertise.all_peer_vpc_subnets, false) ? ["ALL_PEER_VPC_SUBNETS"] : []
|
||||
)
|
||||
dynamic "advertised_ip_ranges" {
|
||||
for_each = (
|
||||
each.value.bgp_peer_options == null ? {} : (
|
||||
each.value.bgp_peer_options.advertise_mode != "CUSTOM"
|
||||
? {}
|
||||
: each.value.bgp_peer_options.advertise_ip_ranges
|
||||
)
|
||||
)
|
||||
for_each = try(each.value.bgp_peer.custom_advertise.ip_ranges, {})
|
||||
iterator = range
|
||||
content {
|
||||
range = range.key
|
||||
|
@ -140,33 +113,29 @@ resource "google_compute_router_peer" "bgp_peer" {
|
|||
}
|
||||
|
||||
resource "google_compute_router_interface" "router_interface" {
|
||||
for_each = var.tunnels
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
name = "${var.name}-${each.key}"
|
||||
router = local.router
|
||||
for_each = var.tunnels
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
name = "${var.name}-${each.key}"
|
||||
router = local.router
|
||||
# FIXME: can bgp_session_range be null?
|
||||
ip_range = each.value.bgp_session_range == "" ? null : each.value.bgp_session_range
|
||||
vpn_tunnel = google_compute_vpn_tunnel.tunnels[each.key].name
|
||||
}
|
||||
|
||||
resource "google_compute_vpn_tunnel" "tunnels" {
|
||||
provider = google-beta
|
||||
for_each = var.tunnels
|
||||
project = var.project_id
|
||||
region = var.region
|
||||
name = "${var.name}-${each.key}"
|
||||
router = local.router
|
||||
peer_external_gateway = local.peer_external_gateway
|
||||
peer_external_gateway = one(google_compute_external_vpn_gateway.external_gateway[*].self_link)
|
||||
peer_external_gateway_interface = each.value.peer_external_gateway_interface
|
||||
peer_gcp_gateway = var.peer_gcp_gateway
|
||||
peer_gcp_gateway = var.peer_gateway.gcp
|
||||
vpn_gateway_interface = each.value.vpn_gateway_interface
|
||||
ike_version = each.value.ike_version
|
||||
shared_secret = (
|
||||
each.value.shared_secret == "" || each.value.shared_secret == null
|
||||
? local.secret
|
||||
: each.value.shared_secret
|
||||
)
|
||||
vpn_gateway = local.vpn_gateway
|
||||
shared_secret = coalesce(each.value.shared_secret, local.secret)
|
||||
vpn_gateway = local.vpn_gateway
|
||||
}
|
||||
|
||||
resource "random_id" "secret" {
|
||||
|
|
|
@ -24,29 +24,17 @@ output "bgp_peers" {
|
|||
|
||||
output "external_gateway" {
|
||||
description = "External VPN gateway resource."
|
||||
value = (
|
||||
var.peer_external_gateway != null
|
||||
? google_compute_external_vpn_gateway.external_gateway[0]
|
||||
: null
|
||||
)
|
||||
value = one(google_compute_external_vpn_gateway.external_gateway[*])
|
||||
}
|
||||
|
||||
output "gateway" {
|
||||
description = "VPN gateway resource (only if auto-created)."
|
||||
value = (
|
||||
var.vpn_gateway_create
|
||||
? google_compute_ha_vpn_gateway.ha_gateway[0]
|
||||
: null
|
||||
)
|
||||
value = one(google_compute_ha_vpn_gateway.ha_gateway[*])
|
||||
}
|
||||
|
||||
output "name" {
|
||||
description = "VPN gateway name (only if auto-created). ."
|
||||
value = (
|
||||
var.vpn_gateway_create
|
||||
? google_compute_ha_vpn_gateway.ha_gateway[0].name
|
||||
: null
|
||||
)
|
||||
value = one(google_compute_ha_vpn_gateway.ha_gateway[*].name)
|
||||
}
|
||||
|
||||
output "random_secret" {
|
||||
|
@ -56,11 +44,7 @@ output "random_secret" {
|
|||
|
||||
output "router" {
|
||||
description = "Router resource (only if auto-created)."
|
||||
value = (
|
||||
var.router_name == ""
|
||||
? google_compute_router.router[0]
|
||||
: null
|
||||
)
|
||||
value = one(google_compute_router.router[*])
|
||||
}
|
||||
|
||||
output "router_name" {
|
||||
|
|
|
@ -24,22 +24,20 @@ variable "network" {
|
|||
type = string
|
||||
}
|
||||
|
||||
variable "peer_external_gateway" {
|
||||
description = "Configuration of an external VPN gateway to which this VPN is connected."
|
||||
variable "peer_gateway" {
|
||||
description = "Configuration of the (external or GCP) peer gateway."
|
||||
type = object({
|
||||
redundancy_type = string
|
||||
interfaces = list(object({
|
||||
id = number
|
||||
ip_address = string
|
||||
external = optional(object({
|
||||
redundancy_type = string
|
||||
interfaces = list(string)
|
||||
}))
|
||||
gcp = optional(string)
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "peer_gcp_gateway" {
|
||||
description = "Self Link URL of the peer side HA GCP VPN gateway to which this VPN tunnel is connected."
|
||||
type = string
|
||||
default = null
|
||||
nullable = false
|
||||
validation {
|
||||
condition = (var.peer_gateway.external != null) != (var.peer_gateway.gcp != null)
|
||||
error_message = "Peer gateway configuration must define exactly one between `external` and `gcp`."
|
||||
}
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
|
@ -52,67 +50,50 @@ variable "region" {
|
|||
type = string
|
||||
}
|
||||
|
||||
variable "route_priority" {
|
||||
description = "Route priority, defaults to 1000."
|
||||
type = number
|
||||
default = 1000
|
||||
}
|
||||
|
||||
variable "router_advertise_config" {
|
||||
description = "Router custom advertisement configuration, ip_ranges is a map of address ranges and descriptions."
|
||||
variable "router_config" {
|
||||
description = "Cloud Router configuration for the VPN. If you want to reuse an existing router, set create to false and use name to specify the desired router."
|
||||
type = object({
|
||||
groups = list(string)
|
||||
ip_ranges = map(string)
|
||||
mode = string
|
||||
create = optional(bool, true)
|
||||
asn = number
|
||||
name = optional(string)
|
||||
keepalive = optional(number)
|
||||
custom_advertise = optional(object({
|
||||
all_subnets = bool
|
||||
ip_ranges = map(string)
|
||||
}))
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "router_asn" {
|
||||
description = "Router ASN used for auto-created router."
|
||||
type = number
|
||||
default = 64514
|
||||
}
|
||||
|
||||
variable "router_create" {
|
||||
description = "Create router."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "router_name" {
|
||||
description = "Router name used for auto created router, or to specify an existing router to use if `router_create` is set to `true`. Leave blank to use VPN name for auto created router."
|
||||
type = string
|
||||
default = ""
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "tunnels" {
|
||||
description = "VPN tunnel configurations, bgp_peer_options is usually null."
|
||||
description = "VPN tunnel configurations."
|
||||
type = map(object({
|
||||
bgp_peer = object({
|
||||
address = string
|
||||
asn = number
|
||||
})
|
||||
bgp_peer_options = object({
|
||||
advertise_groups = list(string)
|
||||
advertise_ip_ranges = map(string)
|
||||
advertise_mode = string
|
||||
route_priority = number
|
||||
address = string
|
||||
asn = number
|
||||
route_priority = optional(number, 1000)
|
||||
custom_advertise = optional(object({
|
||||
all_subnets = bool
|
||||
all_vpc_subnets = bool
|
||||
all_peer_vpc_subnets = bool
|
||||
ip_ranges = map(string)
|
||||
}))
|
||||
})
|
||||
# each BGP session on the same Cloud Router must use a unique /30 CIDR
|
||||
# from the 169.254.0.0/16 block.
|
||||
bgp_session_range = string
|
||||
ike_version = number
|
||||
peer_external_gateway_interface = number
|
||||
router = string
|
||||
shared_secret = string
|
||||
ike_version = optional(number, 2)
|
||||
peer_external_gateway_interface = optional(number)
|
||||
router = optional(string)
|
||||
shared_secret = optional(string)
|
||||
vpn_gateway_interface = number
|
||||
}))
|
||||
default = {}
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "vpn_gateway" {
|
||||
description = "HA VPN Gateway Self Link for using an existing HA VPN Gateway, leave empty if `vpn_gateway_create` is set to `true`."
|
||||
description = "HA VPN Gateway Self Link for using an existing HA VPN Gateway. Ignored if `vpn_gateway_create` is set to `true`."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
|
|
@ -12,17 +12,16 @@ module "addresses" {
|
|||
}
|
||||
|
||||
module "vpn" {
|
||||
source = "./fabric/modules/net-vpn-static"
|
||||
project_id = var.project_id
|
||||
region = var.region
|
||||
network = var.vpc.self_link
|
||||
name = "remote"
|
||||
source = "./fabric/modules/net-vpn-static"
|
||||
project_id = var.project_id
|
||||
region = var.region
|
||||
network = var.vpc.self_link
|
||||
name = "remote"
|
||||
gateway_address_create = false
|
||||
gateway_address = module.addresses.external_addresses["vpn"].address
|
||||
remote_ranges = ["10.10.0.0/24"]
|
||||
remote_ranges = ["10.10.0.0/24"]
|
||||
tunnels = {
|
||||
remote-0 = {
|
||||
ike_version = 2
|
||||
peer_ip = "1.1.1.1"
|
||||
shared_secret = "mysecret"
|
||||
traffic_selectors = { local = ["0.0.0.0/0"], remote = ["0.0.0.0/0"] }
|
||||
|
@ -41,11 +40,11 @@ module "vpn" {
|
|||
| [network](variables.tf#L34) | VPC used for the gateway and routes. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L39) | Project where resources will be created. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L44) | Region used for resources. | <code>string</code> | ✓ | |
|
||||
| [gateway_address](variables.tf#L17) | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | <code>string</code> | | <code>""</code> |
|
||||
| [gateway_address](variables.tf#L17) | Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false. | <code>string</code> | | <code>null</code> |
|
||||
| [gateway_address_create](variables.tf#L23) | Create external address assigned to the VPN gateway. Needs to be explicitly set to false to use address in gateway_address variable. | <code>bool</code> | | <code>true</code> |
|
||||
| [remote_ranges](variables.tf#L49) | Remote IP CIDR ranges. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [route_priority](variables.tf#L55) | Route priority, defaults to 1000. | <code>number</code> | | <code>1000</code> |
|
||||
| [tunnels](variables.tf#L61) | VPN tunnel configurations. | <code title="map(object({ ike_version = number peer_ip = string shared_secret = string traffic_selectors = object({ local = list(string) remote = list(string) }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [route_priority](variables.tf#L56) | Route priority, defaults to 1000. | <code>number</code> | | <code>1000</code> |
|
||||
| [tunnels](variables.tf#L62) | VPN tunnel configurations. | <code title="map(object({ ike_version = optional(number, 2) peer_ip = string shared_secret = optional(string) traffic_selectors = object({ local = list(string) remote = list(string) }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ resource "google_compute_vpn_tunnel" "tunnels" {
|
|||
local_traffic_selector = each.value.traffic_selectors.local
|
||||
remote_traffic_selector = each.value.traffic_selectors.remote
|
||||
ike_version = each.value.ike_version
|
||||
shared_secret = each.value.shared_secret == "" ? local.secret : each.value.shared_secret
|
||||
shared_secret = coalesce(each.value.shared_secret, local.secret)
|
||||
target_vpn_gateway = google_compute_vpn_gateway.gateway.self_link
|
||||
depends_on = [google_compute_forwarding_rule.esp]
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
variable "gateway_address" {
|
||||
description = "Optional address assigned to the VPN gateway. Ignored unless gateway_address_create is set to false."
|
||||
type = string
|
||||
default = ""
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "gateway_address_create" {
|
||||
|
@ -50,6 +50,7 @@ variable "remote_ranges" {
|
|||
description = "Remote IP CIDR ranges."
|
||||
type = list(string)
|
||||
default = []
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "route_priority" {
|
||||
|
@ -61,13 +62,14 @@ variable "route_priority" {
|
|||
variable "tunnels" {
|
||||
description = "VPN tunnel configurations."
|
||||
type = map(object({
|
||||
ike_version = number
|
||||
ike_version = optional(number, 2)
|
||||
peer_ip = string
|
||||
shared_secret = string
|
||||
shared_secret = optional(string)
|
||||
traffic_selectors = object({
|
||||
local = list(string)
|
||||
remote = list(string)
|
||||
})
|
||||
}))
|
||||
default = {}
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ module "test" {
|
|||
| [egress_policies](variables.tf#L70) | Egress policy definitions that can be referenced in perimeters. | <code title="map(object({ from = object({ identity_type = optional(string, "ANY_IDENTITY") identities = optional(list(string)) }) to = object({ operations = optional(list(object({ method_selectors = optional(list(string)) service_name = string })), []) resources = optional(list(string)) resource_type_external = optional(bool, false) }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [ingress_policies](variables.tf#L99) | Ingress policy definitions that can be referenced in perimeters. | <code title="map(object({ from = object({ access_levels = optional(list(string), []) identity_type = optional(string) identities = optional(list(string)) resources = optional(list(string), []) }) to = object({ operations = optional(list(object({ method_selectors = optional(list(string)) service_name = string })), []) resources = optional(list(string)) }) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [service_perimeters_bridge](variables.tf#L130) | Bridge service perimeters. | <code title="map(object({ spec_resources = optional(list(string)) status_resources = optional(list(string)) use_explicit_dry_run_spec = optional(bool, false) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [service_perimeters_regular](variables.tf#L140) | Regular service perimeters. | <code title="map(object({ spec = optional(object({ access_levels = optional(list(string)) resources = optional(list(string)) restricted_services = optional(list(string)) egress_policies = optional(list(string)) ingress_policies = optional(list(string)) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = bool })) }), {}) status = optional(object({ access_levels = optional(list(string)) resources = optional(list(string)) restricted_services = optional(list(string)) egress_policies = optional(list(string)) ingress_policies = optional(list(string)) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = bool })) }), {}) use_explicit_dry_run_spec = optional(bool, false) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [service_perimeters_regular](variables.tf#L140) | Regular service perimeters. | <code title="map(object({ spec = optional(object({ access_levels = optional(list(string)) resources = optional(list(string)) restricted_services = optional(list(string)) egress_policies = optional(list(string)) ingress_policies = optional(list(string)) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = bool })) })) status = optional(object({ access_levels = optional(list(string)) resources = optional(list(string)) restricted_services = optional(list(string)) egress_policies = optional(list(string)) ingress_policies = optional(list(string)) vpc_accessible_services = optional(object({ allowed_services = list(string) enable_restriction = bool })) })) use_explicit_dry_run_spec = optional(bool, false) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -28,20 +28,21 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
|||
perimeter_type = "PERIMETER_TYPE_REGULAR"
|
||||
use_explicit_dry_run_spec = each.value.use_explicit_dry_run_spec
|
||||
dynamic "spec" {
|
||||
for_each = each.value.spec == null ? [] : [""]
|
||||
for_each = each.value.spec == null ? [] : [each.value.spec]
|
||||
iterator = spec
|
||||
content {
|
||||
access_levels = (
|
||||
each.value.spec.access_levels == null ? null : [
|
||||
for k in each.value.spec.access_levels :
|
||||
spec.value.access_levels == null ? null : [
|
||||
for k in spec.value.access_levels :
|
||||
try(google_access_context_manager_access_level.basic[k].id, k)
|
||||
]
|
||||
)
|
||||
resources = each.value.spec.resources
|
||||
restricted_services = each.value.spec.restricted_services
|
||||
resources = spec.value.resources
|
||||
restricted_services = spec.value.restricted_services
|
||||
|
||||
dynamic "egress_policies" {
|
||||
for_each = each.value.spec.egress_policies == null ? {} : {
|
||||
for k in each.value.spec.egress_policies :
|
||||
for_each = spec.value.egress_policies == null ? {} : {
|
||||
for k in spec.value.egress_policies :
|
||||
k => lookup(var.egress_policies, k, null)
|
||||
if contains(keys(var.egress_policies), k)
|
||||
}
|
||||
|
@ -77,8 +78,8 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
|||
}
|
||||
|
||||
dynamic "ingress_policies" {
|
||||
for_each = each.value.spec.ingress_policies == null ? {} : {
|
||||
for k in each.value.spec.ingress_policies :
|
||||
for_each = spec.value.ingress_policies == null ? {} : {
|
||||
for k in spec.value.ingress_policies :
|
||||
k => lookup(var.ingress_policies, k, null)
|
||||
if contains(keys(var.ingress_policies), k)
|
||||
}
|
||||
|
@ -129,30 +130,31 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
|||
}
|
||||
|
||||
dynamic "vpc_accessible_services" {
|
||||
for_each = each.value.spec.vpc_accessible_services == null ? {} : { 1 = 1 }
|
||||
for_each = spec.value.vpc_accessible_services == null ? {} : { 1 = 1 }
|
||||
content {
|
||||
allowed_services = each.value.spec.vpc_accessible_services.allowed_services
|
||||
enable_restriction = each.value.spec.vpc_accessible_services.enable_restriction
|
||||
allowed_services = spec.value.vpc_accessible_services.allowed_services
|
||||
enable_restriction = spec.value.vpc_accessible_services.enable_restriction
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
dynamic "status" {
|
||||
for_each = each.value.status == null ? {} : { 1 = 1 }
|
||||
for_each = each.value.status == null ? [] : [each.value.status]
|
||||
iterator = status
|
||||
content {
|
||||
access_levels = (
|
||||
each.value.status.access_levels == null ? null : [
|
||||
for k in each.value.status.access_levels :
|
||||
status.value.access_levels == null ? null : [
|
||||
for k in status.value.access_levels :
|
||||
try(google_access_context_manager_access_level.basic[k].id, k)
|
||||
]
|
||||
)
|
||||
resources = each.value.status.resources
|
||||
restricted_services = each.value.status.restricted_services
|
||||
resources = status.value.resources
|
||||
restricted_services = status.value.restricted_services
|
||||
|
||||
dynamic "egress_policies" {
|
||||
for_each = each.value.spec.egress_policies == null ? {} : {
|
||||
for k in each.value.spec.egress_policies :
|
||||
for_each = status.value.egress_policies == null ? {} : {
|
||||
for k in status.value.egress_policies :
|
||||
k => lookup(var.egress_policies, k, null)
|
||||
if contains(keys(var.egress_policies), k)
|
||||
}
|
||||
|
@ -188,8 +190,8 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
|||
}
|
||||
|
||||
dynamic "ingress_policies" {
|
||||
for_each = each.value.spec.ingress_policies == null ? {} : {
|
||||
for k in each.value.spec.ingress_policies :
|
||||
for_each = status.value.ingress_policies == null ? {} : {
|
||||
for k in status.value.ingress_policies :
|
||||
k => lookup(var.ingress_policies, k, null)
|
||||
if contains(keys(var.ingress_policies), k)
|
||||
}
|
||||
|
@ -205,7 +207,8 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
|||
iterator = s
|
||||
content {
|
||||
access_level = try(
|
||||
google_access_context_manager_access_level.basic[s.value].id, s.value
|
||||
google_access_context_manager_access_level.basic[s.value].id,
|
||||
s.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -240,10 +243,10 @@ resource "google_access_context_manager_service_perimeter" "regular" {
|
|||
}
|
||||
|
||||
dynamic "vpc_accessible_services" {
|
||||
for_each = each.value.status.vpc_accessible_services == null ? {} : { 1 = 1 }
|
||||
for_each = status.value.vpc_accessible_services == null ? {} : { 1 = 1 }
|
||||
content {
|
||||
allowed_services = each.value.status.vpc_accessible_services.allowed_services
|
||||
enable_restriction = each.value.status.vpc_accessible_services.enable_restriction
|
||||
allowed_services = status.value.vpc_accessible_services.allowed_services
|
||||
enable_restriction = status.value.vpc_accessible_services.enable_restriction
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ variable "egress_policies" {
|
|||
"ANY_USER", "ANY_SERVICE_ACCOUNT"
|
||||
], v.from.identity_type)
|
||||
])
|
||||
error_message = "Invalid `from.identity_type` value in eress policy."
|
||||
error_message = "Invalid `from.identity_type` value in egress policy."
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ variable "service_perimeters_regular" {
|
|||
allowed_services = list(string)
|
||||
enable_restriction = bool
|
||||
}))
|
||||
}), {})
|
||||
}))
|
||||
status = optional(object({
|
||||
access_levels = optional(list(string))
|
||||
resources = optional(list(string))
|
||||
|
@ -161,7 +161,7 @@ variable "service_perimeters_regular" {
|
|||
allowed_services = list(string)
|
||||
enable_restriction = bool
|
||||
}))
|
||||
}), {})
|
||||
}))
|
||||
use_explicit_dry_run_spec = optional(bool, false)
|
||||
}))
|
||||
default = {}
|
||||
|
|
|
@ -79,9 +79,8 @@ variable "environments" {
|
|||
display_name = optional(string)
|
||||
description = optional(string)
|
||||
node_config = optional(object({
|
||||
min_node_count = optional(number)
|
||||
max_node_count = optional(number)
|
||||
current_aggregate_node_count = number
|
||||
min_node_count = optional(number)
|
||||
max_node_count = optional(number)
|
||||
}))
|
||||
iam = optional(map(list(string)))
|
||||
envgroups = list(string)
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
# 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.
|
||||
"""Pytest plugin to discover tests specified in YAML files.
|
||||
|
||||
This plugin uses the pytest_collect_file hook to collect all files
|
||||
matching tftest*.yaml and runs plan_validate for each test found.
|
||||
See FabricTestFile for details on the file structure.
|
||||
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from .fixtures import plan_summary, plan_validator
|
||||
|
||||
|
||||
class FabricTestFile(pytest.File):
|
||||
|
||||
def collect(self):
|
||||
"""Read yaml test spec and yield test items for each test definition.
|
||||
|
||||
The test spec should contain a `module` key with the path of the
|
||||
terraform module to test, relative to the root of the repository
|
||||
|
||||
Tests are defined within the top-level `tests` key, and should
|
||||
have the following structure:
|
||||
|
||||
test-name:
|
||||
tfvars:
|
||||
- tfvars1.tfvars
|
||||
- tfvars2.tfvars
|
||||
inventory:
|
||||
- inventory1.yaml
|
||||
- inventory2.yaml
|
||||
|
||||
All paths specifications are relative to the location of the test
|
||||
spec. The inventory key is optional, if omitted, the inventory
|
||||
will be taken from the file test-name.yaml
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
raw = yaml.safe_load(self.path.open())
|
||||
module = raw.pop('module')
|
||||
except (IOError, OSError, yaml.YAMLError) as e:
|
||||
raise Exception(f'cannot read test spec {self.path}: {e}')
|
||||
except KeyError as e:
|
||||
raise Exception(f'`module` key not found in {self.path}: {e}')
|
||||
common = raw.pop('common_tfvars', [])
|
||||
for test_name, spec in raw.get('tests', {}).items():
|
||||
spec = {} if spec is None else spec
|
||||
inventories = spec.get('inventory', [f'{test_name}.yaml'])
|
||||
tfvars = common + [f'{test_name}.tfvars'] + spec.get('tfvars', [])
|
||||
for i in inventories:
|
||||
name = test_name
|
||||
if isinstance(inventories, list) and len(inventories) > 1:
|
||||
name = f'{test_name}[{i}]'
|
||||
yield FabricTestItem.from_parent(self, name=name, module=module,
|
||||
inventory=[i], tfvars=tfvars)
|
||||
|
||||
|
||||
class FabricTestItem(pytest.Item):
|
||||
|
||||
def __init__(self, name, parent, module, inventory, tfvars):
|
||||
super().__init__(name, parent)
|
||||
self.module = module
|
||||
self.inventory = inventory
|
||||
self.tfvars = tfvars
|
||||
|
||||
def runtest(self):
|
||||
s = plan_validator(self.module, self.inventory, self.parent.path.parent,
|
||||
self.tfvars)
|
||||
|
||||
def reportinfo(self):
|
||||
return self.path, None, self.name
|
||||
|
||||
|
||||
def pytest_collect_file(parent, file_path):
|
||||
'Collect tftest*.yaml files and run plan_validator from them.'
|
||||
if file_path.suffix == '.yaml' and file_path.name.startswith('tftest'):
|
||||
return FabricTestFile.from_parent(parent, path=file_path)
|
|
@ -11,144 +11,12 @@
|
|||
# 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.
|
||||
"Shared fixtures"
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
'Pytest configuration.'
|
||||
|
||||
import pytest
|
||||
import tftest
|
||||
|
||||
BASEDIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def _plan_runner():
|
||||
"Returns a function to run Terraform plan on a fixture."
|
||||
|
||||
def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
|
||||
targets=None, refresh=True, tmpdir=True, **tf_vars):
|
||||
"Runs Terraform plan and returns parsed output."
|
||||
if fixture_path is None:
|
||||
# find out the fixture directory from the caller's directory
|
||||
caller = inspect.stack()[2]
|
||||
fixture_path = os.path.join(os.path.dirname(caller.filename), "fixture")
|
||||
|
||||
fixture_parent = os.path.dirname(fixture_path)
|
||||
fixture_prefix = os.path.basename(fixture_path) + "_"
|
||||
with tempfile.TemporaryDirectory(prefix=fixture_prefix,
|
||||
dir=fixture_parent) as tmp_path:
|
||||
# copy fixture to a temporary directory so we can execute
|
||||
# multiple tests in parallel
|
||||
if tmpdir:
|
||||
shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True)
|
||||
tf = tftest.TerraformTest(tmp_path if tmpdir else fixture_path, BASEDIR,
|
||||
os.environ.get('TERRAFORM', 'terraform'))
|
||||
tf.setup(extra_files=extra_files, upgrade=True)
|
||||
plan = tf.plan(output=True, refresh=refresh, tf_var_file=tf_var_file,
|
||||
tf_vars=tf_vars, targets=targets)
|
||||
return plan
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def plan_runner(_plan_runner):
|
||||
"Returns a function to run Terraform plan on a module fixture."
|
||||
|
||||
def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
|
||||
targets=None, **tf_vars):
|
||||
"Runs Terraform plan and returns plan and module resources."
|
||||
plan = _plan_runner(fixture_path, extra_files=extra_files,
|
||||
tf_var_file=tf_var_file, targets=targets, **tf_vars)
|
||||
# skip the fixture
|
||||
root_module = plan.root_module['child_modules'][0]
|
||||
return plan, root_module['resources']
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def e2e_plan_runner(_plan_runner):
|
||||
"Returns a function to run Terraform plan on an end-to-end fixture."
|
||||
|
||||
def run_plan(fixture_path=None, tf_var_file=None, targets=None,
|
||||
refresh=True, include_bare_resources=False, **tf_vars):
|
||||
"Runs Terraform plan on an end-to-end module using defaults, returns data."
|
||||
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
|
||||
refresh=refresh, **tf_vars)
|
||||
# skip the fixture
|
||||
root_module = plan.root_module['child_modules'][0]
|
||||
modules = dict((mod['address'], mod['resources'])
|
||||
for mod in root_module['child_modules'])
|
||||
resources = [r for m in modules.values() for r in m]
|
||||
if include_bare_resources:
|
||||
bare_resources = root_module['resources']
|
||||
resources.extend(bare_resources)
|
||||
return modules, resources
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def recursive_e2e_plan_runner(_plan_runner):
|
||||
"""Plan runner for end-to-end root module, returns total number of
|
||||
(nested) modules and resources"""
|
||||
|
||||
def walk_plan(node, modules, resources):
|
||||
# TODO(jccb): this would be better with node.get() but
|
||||
# TerraformPlanOutput objects don't have it
|
||||
new_modules = node.get('child_modules', [])
|
||||
resources += node.get('resources', [])
|
||||
modules += new_modules
|
||||
for module in new_modules:
|
||||
walk_plan(module, modules, resources)
|
||||
|
||||
def run_plan(fixture_path=None, tf_var_file=None, targets=None, refresh=True,
|
||||
include_bare_resources=False, compute_sums=True, tmpdir=True,
|
||||
**tf_vars):
|
||||
"Runs Terraform plan on a root module using defaults, returns data."
|
||||
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
|
||||
refresh=refresh, tmpdir=tmpdir, **tf_vars)
|
||||
modules = []
|
||||
resources = []
|
||||
walk_plan(plan.root_module, modules, resources)
|
||||
return len(modules), len(resources)
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def apply_runner():
|
||||
"Returns a function to run Terraform apply on a fixture."
|
||||
|
||||
def run_apply(fixture_path=None, **tf_vars):
|
||||
"Runs Terraform plan and returns parsed output."
|
||||
if fixture_path is None:
|
||||
# find out the fixture directory from the caller's directory
|
||||
caller = inspect.stack()[1]
|
||||
fixture_path = os.path.join(os.path.dirname(caller.filename), "fixture")
|
||||
|
||||
fixture_parent = os.path.dirname(fixture_path)
|
||||
fixture_prefix = os.path.basename(fixture_path) + "_"
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix=fixture_prefix,
|
||||
dir=fixture_parent) as tmp_path:
|
||||
# copy fixture to a temporary directory so we can execute
|
||||
# multiple tests in parallel
|
||||
shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True)
|
||||
tf = tftest.TerraformTest(tmp_path, BASEDIR,
|
||||
os.environ.get('TERRAFORM', 'terraform'))
|
||||
tf.setup(upgrade=True)
|
||||
apply = tf.apply(tf_vars=tf_vars)
|
||||
output = tf.output(json_format=True)
|
||||
return apply, output
|
||||
|
||||
return run_apply
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def basedir():
|
||||
return BASEDIR
|
||||
pytest_plugins = (
|
||||
'tests.fixtures',
|
||||
'tests.legacy_fixtures',
|
||||
'tests.collectors',
|
||||
)
|
||||
|
|
|
@ -68,14 +68,21 @@ variable "subnet" {
|
|||
variable "vpc" {
|
||||
default = {
|
||||
name = "vpc_name"
|
||||
self_link = "projects/xxx/global/networks/yyy"
|
||||
self_link = "projects/xxx/global/networks/aaa"
|
||||
}
|
||||
}
|
||||
|
||||
variable "vpc1" {
|
||||
default = {
|
||||
name = "vpc_name"
|
||||
self_link = "projects/xxx/global/networks/bbb"
|
||||
}
|
||||
}
|
||||
|
||||
variable "vpc2" {
|
||||
default = {
|
||||
name = "vpc2_name"
|
||||
self_link = "vpc2_self_link"
|
||||
self_link = "projects/xxx/global/networks/ccc"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,29 +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 "stage" {
|
||||
source = "../../../../../fast/stages/00-bootstrap"
|
||||
prefix = "fast"
|
||||
organization = {
|
||||
domain = "fast.example.com"
|
||||
id = 123456789012
|
||||
customer_id = "C00000000"
|
||||
}
|
||||
billing_account = {
|
||||
id = "000000-111111-222222"
|
||||
organization_id = 123456789012
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
organization = {
|
||||
domain = "fast.example.com"
|
||||
id = 123456789012
|
||||
customer_id = "C00000000"
|
||||
}
|
||||
billing_account = {
|
||||
id = "000000-111111-222222"
|
||||
organization_id = 123456789012
|
||||
}
|
||||
prefix = "fast"
|
||||
outputs_location = "/fast-config"
|
|
@ -0,0 +1,49 @@
|
|||
# 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_bigquery_dataset: 2
|
||||
google_bigquery_dataset_iam_member: 2
|
||||
google_bigquery_default_service_account: 3
|
||||
google_logging_organization_sink: 2
|
||||
google_organization_iam_binding: 19
|
||||
google_organization_iam_custom_role: 2
|
||||
google_organization_iam_member: 16
|
||||
google_project: 3
|
||||
google_project_iam_binding: 9
|
||||
google_project_iam_member: 1
|
||||
google_project_service: 29
|
||||
google_project_service_identity: 2
|
||||
google_service_account: 3
|
||||
google_service_account_iam_binding: 3
|
||||
google_storage_bucket: 4
|
||||
google_storage_bucket_iam_binding: 2
|
||||
google_storage_bucket_iam_member: 3
|
||||
google_storage_bucket_object: 5
|
||||
google_storage_project_service_account: 3
|
||||
local_file: 5
|
||||
|
||||
outputs:
|
||||
custom_roles:
|
||||
organization_iam_admin: organizations/123456789012/roles/organizationIamAdmin
|
||||
service_project_network_admin: organizations/123456789012/roles/serviceProjectNetworkAdmin
|
||||
outputs_bucket: fast-prod-iac-core-outputs-0
|
||||
project_ids:
|
||||
automation: fast-prod-iac-core-0
|
||||
billing-export: fast-prod-billing-exp-0
|
||||
log-export: fast-prod-audit-logs-0
|
||||
service_accounts:
|
||||
bootstrap: fast-prod-bootstrap-0@fast-prod-iac-core-0.iam.gserviceaccount.com
|
||||
cicd: fast-prod-cicd-0@fast-prod-iac-core-0.iam.gserviceaccount.com
|
||||
resman: fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com
|
|
@ -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.
|
||||
|
||||
values:
|
||||
module.automation-project.google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: 000000-111111-222222
|
||||
name: fast-prod-iac-core-0
|
||||
org_id: '123456789012'
|
||||
project_id: fast-prod-iac-core-0
|
||||
module.billing-export-project[0].google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: 000000-111111-222222
|
||||
name: fast-prod-billing-exp-0
|
||||
org_id: '123456789012'
|
||||
project_id: fast-prod-billing-exp-0
|
||||
module.log-export-project.google_project.project[0]:
|
||||
auto_create_network: false
|
||||
billing_account: 000000-111111-222222
|
||||
name: fast-prod-audit-logs-0
|
||||
org_id: '123456789012'
|
||||
project_id: fast-prod-audit-logs-0
|
|
@ -0,0 +1,27 @@
|
|||
# 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.
|
||||
|
||||
values:
|
||||
module.automation-tf-bootstrap-sa.google_service_account.service_account[0]:
|
||||
account_id: fast-prod-bootstrap-0
|
||||
display_name: Terraform organization bootstrap service account.
|
||||
project: fast-prod-iac-core-0
|
||||
module.automation-tf-cicd-provisioning-sa.google_service_account.service_account[0]:
|
||||
account_id: fast-prod-cicd-0
|
||||
display_name: Terraform stage 1 CICD service account.
|
||||
project: fast-prod-iac-core-0
|
||||
module.automation-tf-resman-sa.google_service_account.service_account[0]:
|
||||
account_id: fast-prod-resman-0
|
||||
display_name: Terraform stage 1 resman service account.
|
||||
project: fast-prod-iac-core-0
|
|
@ -1,33 +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.
|
||||
|
||||
# _RESOURCE_COUNT = {
|
||||
# 'module.organization': 28,
|
||||
# 'module.automation-project': 23,
|
||||
# 'module.automation-tf-bootstrap-gcs': 1,
|
||||
# 'module.automation-tf-bootstrap-sa': 1,
|
||||
# 'module.automation-tf-resman-gcs': 2,
|
||||
# 'module.automation-tf-resman-sa': 1,
|
||||
# 'module.billing-export-dataset': 1,
|
||||
# 'module.billing-export-project': 7,
|
||||
# 'module.log-export-dataset': 1,
|
||||
# 'module.log-export-project': 7,
|
||||
# }
|
||||
|
||||
|
||||
def test_counts(recursive_e2e_plan_runner):
|
||||
"Test stage."
|
||||
# TODO: to re-enable per-module resource count check print _, then test
|
||||
num_modules, num_resources = recursive_e2e_plan_runner()
|
||||
assert num_modules > 0 and num_resources > 0
|
|
@ -0,0 +1,12 @@
|
|||
# skip boilerplate check
|
||||
|
||||
module: fast/stages/00-bootstrap
|
||||
|
||||
tests:
|
||||
simple:
|
||||
tfvars:
|
||||
- simple.tfvars
|
||||
inventory:
|
||||
- simple.yaml
|
||||
- simple_projects.yaml
|
||||
- simple_sas.yaml
|
|
@ -0,0 +1,235 @@
|
|||
# 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.
|
||||
"""Common fixtures."""
|
||||
|
||||
import collections
|
||||
import contextlib
|
||||
import itertools
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import tftest
|
||||
import yaml
|
||||
|
||||
PlanSummary = collections.namedtuple('PlanSummary', 'values counts outputs')
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _prepare_root_module(path):
|
||||
"""Context manager to prepare a terraform module to be tested.
|
||||
|
||||
If the TFTEST_COPY environment variable is set, `path` is copied to
|
||||
a temporary directory and a few terraform files (e.g.
|
||||
terraform.tfvars) are delete to ensure a clean test environment.
|
||||
Otherwise, `path` is simply returned untouched.
|
||||
"""
|
||||
if os.environ.get('TFTEST_COPY'):
|
||||
# if the TFTEST_COPY is set, create temp dir and copy the root
|
||||
# module there
|
||||
with tempfile.TemporaryDirectory(dir=path.parent) as tmp_path:
|
||||
tmp_path = Path(tmp_path)
|
||||
|
||||
# if we're copying the module, we might as well ignore files and
|
||||
# directories that are automatically read by terraform. Useful
|
||||
# to avoid surprises if, for example, you have an active fast
|
||||
# deployment with links to configs)
|
||||
ignore_patterns = shutil.ignore_patterns('*.auto.tfvars',
|
||||
'*.auto.tfvars.json',
|
||||
'terraform.tfstate*',
|
||||
'terraform.tfvars', '.terraform')
|
||||
|
||||
shutil.copytree(path, tmp_path, dirs_exist_ok=True,
|
||||
ignore=ignore_patterns)
|
||||
|
||||
yield tmp_path
|
||||
else:
|
||||
# if TFTEST_COPY is not set, just return the same path
|
||||
yield path
|
||||
|
||||
|
||||
def plan_summary(module_path, basedir, tf_var_files=None, **tf_vars):
|
||||
"""
|
||||
Run a Terraform plan on the module located at `module_path`.
|
||||
|
||||
- module_path: terraform root module to run. Can be an absolute
|
||||
path or relative to the root of the repository
|
||||
|
||||
- basedir: directory root to use for relative paths in
|
||||
tf_var_files.
|
||||
|
||||
- tf_var_files: set of terraform variable files (tfvars) to pass
|
||||
in to terraform
|
||||
|
||||
Returns a PlanSummary object containing 3 attributes:
|
||||
- values: dictionary where the keys are terraform plan addresses
|
||||
and values are the JSON representation (converted to python
|
||||
types) of the attribute values of the resource.
|
||||
|
||||
- counts: dictionary where the keys are the terraform resource
|
||||
types and the values are the number of times that type appears
|
||||
in the plan
|
||||
|
||||
- outputs: dictionary of the modules outputs that can be
|
||||
determined at plan type.
|
||||
|
||||
Consult [1] for mode details on the structure of values and outputs
|
||||
|
||||
[1] https://developer.hashicorp.com/terraform/internals/json-format
|
||||
"""
|
||||
# make the module_path relative to the root of the repo while still
|
||||
# supporting absolute paths
|
||||
module_path = Path(__file__).parents[1] / module_path
|
||||
with _prepare_root_module(module_path) as test_path:
|
||||
binary = os.environ.get('TERRAFORM', 'terraform')
|
||||
tf = tftest.TerraformTest(test_path, binary=binary)
|
||||
tf.setup(upgrade=True)
|
||||
tf_var_files = [(basedir / x).resolve() for x in tf_var_files or []]
|
||||
plan = tf.plan(output=True, tf_var_file=tf_var_files, tf_vars=tf_vars)
|
||||
|
||||
# compute resource type counts and address->values map
|
||||
values = {}
|
||||
counts = collections.defaultdict(int)
|
||||
q = collections.deque([plan.root_module])
|
||||
while q:
|
||||
e = q.popleft()
|
||||
|
||||
if 'type' in e:
|
||||
counts[e['type']] += 1
|
||||
if 'values' in e:
|
||||
values[e['address']] = e['values']
|
||||
|
||||
for x in e.get('resources', []):
|
||||
q.append(x)
|
||||
for x in e.get('child_modules', []):
|
||||
q.append(x)
|
||||
|
||||
# extract planned outputs
|
||||
outputs = plan.get('planned_values', {}).get('outputs', {})
|
||||
|
||||
return PlanSummary(values, dict(counts), outputs)
|
||||
|
||||
|
||||
@pytest.fixture(name='plan_summary')
|
||||
def plan_summary_fixture(request):
|
||||
"""Return a function to generate a PlanSummary.
|
||||
|
||||
In the returned function `basedir` becomes optional and it defaults
|
||||
to the directory of the calling test
|
||||
"""
|
||||
|
||||
def inner(module_path, basedir=None, tf_var_files=None, **tf_vars):
|
||||
if basedir is None:
|
||||
basedir = Path(request.fspath).parent
|
||||
return plan_summary(module_path=module_path, basedir=basedir,
|
||||
tf_var_files=tf_var_files, **tf_vars)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def plan_validator(module_path, inventory_paths, basedir, tf_var_files=None,
|
||||
**tf_vars):
|
||||
summary = plan_summary(module_path=module_path, tf_var_files=tf_var_files,
|
||||
basedir=basedir, **tf_vars)
|
||||
|
||||
# allow single single string for inventory_paths
|
||||
if not isinstance(inventory_paths, list):
|
||||
inventory_paths = [inventory_paths]
|
||||
|
||||
for path in inventory_paths:
|
||||
# allow tfvars and inventory to be relative to the caller
|
||||
path = basedir / path
|
||||
try:
|
||||
inventory = yaml.safe_load(path.read_text())
|
||||
except (IOError, OSError, yaml.YAMLError) as e:
|
||||
raise Exception(f'cannot read test inventory {path}: {e}')
|
||||
|
||||
# don't fail if the inventory is empty
|
||||
inventory = inventory or {}
|
||||
|
||||
# If you add additional asserts to this function:
|
||||
# - put the values coming from the plan on the left side of
|
||||
# any comparison operators
|
||||
# - put the values coming from user's inventory the right
|
||||
# side of any comparison operators.
|
||||
# - include a descriptive error message to the assert
|
||||
|
||||
# for values:
|
||||
# - verify each address in the user's inventory exists in the plan
|
||||
# - for those address that exist on both the user's inventory and
|
||||
# the plan output, ensure the set of keys on the inventory are a
|
||||
# subset of the keys in the plan, and compare their values by
|
||||
# equality
|
||||
if 'values' in inventory:
|
||||
expected_values = inventory['values']
|
||||
for address, expected_value in expected_values.items():
|
||||
assert address in summary.values, \
|
||||
f'{address} is not a valid address in the plan'
|
||||
for k, v in expected_value.items():
|
||||
assert k in summary.values[address], \
|
||||
f'{k} not found at {address}'
|
||||
plan_value = summary.values[address][k]
|
||||
assert plan_value == v, \
|
||||
f'{k} at {address} failed. Got `{plan_value}`, expected `{v}`'
|
||||
|
||||
if 'counts' in inventory:
|
||||
expected_counts = inventory['counts']
|
||||
for type_, expected_count in expected_counts.items():
|
||||
assert type_ in summary.counts, \
|
||||
f'module does not create any resources of type `{type_}`'
|
||||
plan_count = summary.counts[type_]
|
||||
assert plan_count == expected_count, \
|
||||
f'count of {type_} resources failed. Got {plan_count}, expected {expected_count}'
|
||||
|
||||
if 'outputs' in inventory:
|
||||
expected_outputs = inventory['outputs']
|
||||
for output_name, expected_output in expected_outputs.items():
|
||||
assert output_name in summary.outputs, \
|
||||
f'module does not output `{output_name}`'
|
||||
output = summary.outputs[output_name]
|
||||
# assert 'value' in output, \
|
||||
# f'output `{output_name}` does not have a value (is it sensitive or dynamic?)'
|
||||
plan_output = output.get('value', '__missing__')
|
||||
assert plan_output == expected_output, \
|
||||
f'output {output_name} failed. Got `{plan_output}`, expected `{expected_output}`'
|
||||
|
||||
return summary
|
||||
|
||||
|
||||
@pytest.fixture(name='plan_validator')
|
||||
def plan_validator_fixture(request):
|
||||
"""Return a function to build a PlanSummary and compare it to a YAML inventory.
|
||||
|
||||
In the returned function `basedir` becomes optional and it defaults
|
||||
to the directory of the calling test'
|
||||
|
||||
"""
|
||||
|
||||
def inner(module_path, inventory_paths, basedir=None, tf_var_files=None,
|
||||
**tf_vars):
|
||||
if basedir is None:
|
||||
basedir = Path(request.fspath).parent
|
||||
return plan_validator(module_path=module_path,
|
||||
inventory_paths=inventory_paths, basedir=basedir,
|
||||
tf_var_files=tf_var_paths, **tf_vars)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
# @pytest.fixture
|
||||
# def repo_root():
|
||||
# 'Return a pathlib.Path to the root of the repository'
|
||||
# return Path(__file__).parents[1]
|
|
@ -0,0 +1,153 @@
|
|||
# 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.
|
||||
"""Legacy pytest fixtures.
|
||||
|
||||
The fixtures contained in this file will eventually go away. Consider
|
||||
using one of the fixtures in fixtures.py
|
||||
"""
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
import tftest
|
||||
|
||||
BASEDIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def _plan_runner():
|
||||
'Return a function to run Terraform plan on a fixture.'
|
||||
|
||||
def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
|
||||
targets=None, refresh=True, tmpdir=True, **tf_vars):
|
||||
'Run Terraform plan and returns parsed output.'
|
||||
if fixture_path is None:
|
||||
# find out the fixture directory from the caller's directory
|
||||
caller = inspect.stack()[2]
|
||||
fixture_path = os.path.join(os.path.dirname(caller.filename), 'fixture')
|
||||
|
||||
fixture_parent = os.path.dirname(fixture_path)
|
||||
fixture_prefix = os.path.basename(fixture_path) + '_'
|
||||
with tempfile.TemporaryDirectory(prefix=fixture_prefix,
|
||||
dir=fixture_parent) as tmp_path:
|
||||
# copy fixture to a temporary directory so we can execute
|
||||
# multiple tests in parallel
|
||||
if tmpdir:
|
||||
shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True)
|
||||
tf = tftest.TerraformTest(tmp_path if tmpdir else fixture_path, BASEDIR,
|
||||
os.environ.get('TERRAFORM', 'terraform'))
|
||||
tf.setup(extra_files=extra_files, upgrade=True)
|
||||
plan = tf.plan(output=True, refresh=refresh, tf_var_file=tf_var_file,
|
||||
tf_vars=tf_vars, targets=targets)
|
||||
return plan
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def plan_runner(_plan_runner):
|
||||
'Return a function to run Terraform plan on a module fixture.'
|
||||
|
||||
def run_plan(fixture_path=None, extra_files=None, tf_var_file=None,
|
||||
targets=None, **tf_vars):
|
||||
'Run Terraform plan and returns plan and module resources.'
|
||||
plan = _plan_runner(fixture_path, extra_files=extra_files,
|
||||
tf_var_file=tf_var_file, targets=targets, **tf_vars)
|
||||
# skip the fixture
|
||||
root_module = plan.root_module['child_modules'][0]
|
||||
return plan, root_module['resources']
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def e2e_plan_runner(_plan_runner):
|
||||
'Return a function to run Terraform plan on an end-to-end fixture.'
|
||||
|
||||
def run_plan(fixture_path=None, tf_var_file=None, targets=None, refresh=True,
|
||||
include_bare_resources=False, **tf_vars):
|
||||
'Run Terraform plan on an end-to-end module using defaults, returns data.'
|
||||
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
|
||||
refresh=refresh, **tf_vars)
|
||||
# skip the fixture
|
||||
root_module = plan.root_module['child_modules'][0]
|
||||
modules = dict((mod['address'], mod['resources'])
|
||||
for mod in root_module['child_modules'])
|
||||
resources = [r for m in modules.values() for r in m]
|
||||
if include_bare_resources:
|
||||
bare_resources = root_module['resources']
|
||||
resources.extend(bare_resources)
|
||||
return modules, resources
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def recursive_e2e_plan_runner(_plan_runner):
|
||||
"""
|
||||
Plan runner for end-to-end root module, returns total number of
|
||||
(nested) modules and resources
|
||||
"""
|
||||
|
||||
def walk_plan(node, modules, resources):
|
||||
new_modules = node.get('child_modules', [])
|
||||
resources += node.get('resources', [])
|
||||
modules += new_modules
|
||||
for module in new_modules:
|
||||
walk_plan(module, modules, resources)
|
||||
|
||||
def run_plan(fixture_path=None, tf_var_file=None, targets=None, refresh=True,
|
||||
include_bare_resources=False, compute_sums=True, tmpdir=True,
|
||||
**tf_vars):
|
||||
'Run Terraform plan on a root module using defaults, returns data.'
|
||||
plan = _plan_runner(fixture_path, tf_var_file=tf_var_file, targets=targets,
|
||||
refresh=refresh, tmpdir=tmpdir, **tf_vars)
|
||||
modules = []
|
||||
resources = []
|
||||
walk_plan(plan.root_module, modules, resources)
|
||||
return len(modules), len(resources)
|
||||
|
||||
return run_plan
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def apply_runner():
|
||||
'Return a function to run Terraform apply on a fixture.'
|
||||
|
||||
def run_apply(fixture_path=None, **tf_vars):
|
||||
'Run Terraform plan and returns parsed output.'
|
||||
if fixture_path is None:
|
||||
# find out the fixture directory from the caller's directory
|
||||
caller = inspect.stack()[1]
|
||||
fixture_path = os.path.join(os.path.dirname(caller.filename), 'fixture')
|
||||
|
||||
fixture_parent = os.path.dirname(fixture_path)
|
||||
fixture_prefix = os.path.basename(fixture_path) + '_'
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix=fixture_prefix,
|
||||
dir=fixture_parent) as tmp_path:
|
||||
# copy fixture to a temporary directory so we can execute
|
||||
# multiple tests in parallel
|
||||
shutil.copytree(fixture_path, tmp_path, dirs_exist_ok=True)
|
||||
tf = tftest.TerraformTest(tmp_path, BASEDIR,
|
||||
os.environ.get('TERRAFORM', 'terraform'))
|
||||
tf.setup(upgrade=True)
|
||||
apply = tf.apply(tf_vars=tf_vars)
|
||||
output = tf.output(json_format=True)
|
||||
return apply, output
|
||||
|
||||
return run_apply
|
|
@ -4,5 +4,9 @@ environments = {
|
|||
display_name = "APIs test"
|
||||
description = "APIs Test"
|
||||
envgroups = ["test"]
|
||||
node_config = {
|
||||
min_node_count = 2
|
||||
max_node_count = 5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,8 @@ variable "environments" {
|
|||
display_name = optional(string)
|
||||
description = optional(string, "Terraform-managed")
|
||||
node_config = optional(object({
|
||||
min_node_count = optional(number)
|
||||
max_node_count = optional(number)
|
||||
current_aggregate_node_count = number
|
||||
min_node_count = optional(number)
|
||||
max_node_count = optional(number)
|
||||
}))
|
||||
iam = optional(map(list(string)))
|
||||
envgroups = list(string)
|
||||
|
@ -76,4 +75,4 @@ variable "organization" {
|
|||
variable "project_id" {
|
||||
description = "Project ID."
|
||||
type = string
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,16 +20,11 @@ import pytest
|
|||
import yaml
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
for item in items:
|
||||
item.add_marker(pytest.mark.xdist_group(name=item.path.parent.name))
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def tfvars_to_yaml():
|
||||
@pytest.fixture()
|
||||
def tfvars_to_yaml(request):
|
||||
|
||||
def converter(source, dest, from_var, to_var=None):
|
||||
p_fixture = pathlib.Path(inspect.stack()[1].filename).parent / 'fixture'
|
||||
p_fixture = pathlib.Path(request.path).parent
|
||||
p_source = p_fixture / source
|
||||
if not p_source.exists():
|
||||
raise ValueError(f"tfvars '{source}' not found")
|
||||
|
|
|
@ -31,13 +31,14 @@ def test_policy_list(plan_runner):
|
|||
|
||||
def test_factory_policy_boolean(plan_runner, tfvars_to_yaml, tmp_path):
|
||||
dest = tmp_path / 'policies.yaml'
|
||||
tfvars_to_yaml('test.orgpolicies-boolean.tfvars', dest, 'org_policies')
|
||||
tfvars_to_yaml('fixture/test.orgpolicies-boolean.tfvars', dest,
|
||||
'org_policies')
|
||||
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"')
|
||||
validate_policy_boolean(resources)
|
||||
|
||||
|
||||
def test_factory_policy_list(plan_runner, tfvars_to_yaml, tmp_path):
|
||||
dest = tmp_path / 'policies.yaml'
|
||||
tfvars_to_yaml('test.orgpolicies-list.tfvars', dest, 'org_policies')
|
||||
tfvars_to_yaml('fixture/test.orgpolicies-list.tfvars', dest, 'org_policies')
|
||||
_, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"')
|
||||
validate_policy_list(resources)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
project_id = "test-project"
|
||||
name = "test"
|
|
@ -0,0 +1 @@
|
|||
data_folder = "../../tests/modules/net_vpc/data"
|
|
@ -0,0 +1,44 @@
|
|||
# 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.
|
||||
|
||||
values:
|
||||
google_compute_subnetwork.subnetwork["europe-west1/factory-subnet"]:
|
||||
description: 'Sample description'
|
||||
ip_cidr_range: '10.128.0.0/24'
|
||||
ipv6_access_type: null
|
||||
log_config: []
|
||||
name: 'factory-subnet'
|
||||
private_ip_google_access: false
|
||||
project: 'test-project'
|
||||
region: 'europe-west1'
|
||||
role: null
|
||||
secondary_ip_range:
|
||||
- ip_cidr_range: '192.168.128.0/24'
|
||||
range_name: 'secondary-range-a'
|
||||
google_compute_subnetwork.subnetwork["europe-west4/factory-subnet2"]:
|
||||
description: 'Sample description'
|
||||
ip_cidr_range: '10.129.0.0/24'
|
||||
log_config: []
|
||||
name: 'factory-subnet2'
|
||||
private_ip_google_access: true
|
||||
project: 'test-project'
|
||||
region: 'europe-west4'
|
||||
role: null
|
||||
secondary_ip_range: []
|
||||
|
||||
# FIXME: should we have some bindings here?
|
||||
|
||||
counts:
|
||||
google_compute_network: 1
|
||||
google_compute_subnetwork: 2
|
|
@ -0,0 +1,5 @@
|
|||
peering_config = {
|
||||
peer_vpc_self_link = "projects/my-project/global/networks/peer"
|
||||
export_routes = true
|
||||
import_routes = null
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
# 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.
|
||||
|
||||
values:
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering.local[0]:
|
||||
export_custom_routes: true
|
||||
import_custom_routes: false
|
||||
name: test-peer
|
||||
peer_network: projects/my-project/global/networks/peer
|
||||
google_compute_network_peering.remote[0]:
|
||||
export_custom_routes: false
|
||||
import_custom_routes: true
|
||||
name: peer-test
|
||||
network: projects/my-project/global/networks/peer
|
||||
|
||||
counts:
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering: 2
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
project_id: test-project
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
|
@ -0,0 +1,7 @@
|
|||
psa_config = {
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
}
|
||||
export_routes = true
|
||||
import_routes = false
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
# 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.
|
||||
|
||||
values:
|
||||
google_compute_global_address.psa_ranges["bar"]:
|
||||
address: 172.16.100.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: bar
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
enable_ula_internal_ipv6: null
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering_routes_config.psa_routes["1"]:
|
||||
export_custom_routes: true
|
||||
import_custom_routes: false
|
||||
project: test-project
|
||||
google_service_networking_connection.psa_connection["1"]:
|
||||
reserved_peering_ranges:
|
||||
- bar
|
||||
service: servicenetworking.googleapis.com
|
||||
|
||||
counts:
|
||||
google_compute_global_address: 1
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering_routes_config: 1
|
||||
google_service_networking_connection: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
name: __missing__
|
||||
network: __missing__
|
||||
project_id: test-project
|
||||
self_link: __missing__
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
|
@ -0,0 +1,7 @@
|
|||
psa_config = {
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
}
|
||||
export_routes = false
|
||||
import_routes = true
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
# 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.
|
||||
|
||||
values:
|
||||
google_compute_global_address.psa_ranges["bar"]:
|
||||
address: 172.16.100.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: bar
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
enable_ula_internal_ipv6: null
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering_routes_config.psa_routes["1"]:
|
||||
export_custom_routes: false
|
||||
import_custom_routes: true
|
||||
project: test-project
|
||||
google_service_networking_connection.psa_connection["1"]:
|
||||
reserved_peering_ranges:
|
||||
- bar
|
||||
service: servicenetworking.googleapis.com
|
||||
|
||||
counts:
|
||||
google_compute_global_address: 1
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering_routes_config: 1
|
||||
google_service_networking_connection: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
name: __missing__
|
||||
network: __missing__
|
||||
project_id: test-project
|
||||
self_link: __missing__
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
|
@ -0,0 +1,7 @@
|
|||
psa_config = {
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
}
|
||||
export_routes = true
|
||||
import_routes = true
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
# 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.
|
||||
|
||||
values:
|
||||
google_compute_global_address.psa_ranges["bar"]:
|
||||
address: 172.16.100.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: bar
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
enable_ula_internal_ipv6: null
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering_routes_config.psa_routes["1"]:
|
||||
export_custom_routes: true
|
||||
import_custom_routes: true
|
||||
project: test-project
|
||||
google_service_networking_connection.psa_connection["1"]:
|
||||
reserved_peering_ranges:
|
||||
- bar
|
||||
service: servicenetworking.googleapis.com
|
||||
|
||||
counts:
|
||||
google_compute_global_address: 1
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering_routes_config: 1
|
||||
google_service_networking_connection: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
name: __missing__
|
||||
network: __missing__
|
||||
project_id: test-project
|
||||
self_link: __missing__
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
|
@ -0,0 +1,7 @@
|
|||
psa_config = {
|
||||
ranges = {
|
||||
bar = "172.16.100.0/24"
|
||||
foo = "172.16.101.0/24"
|
||||
}
|
||||
routes = null
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
# 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.
|
||||
|
||||
values:
|
||||
google_compute_global_address.psa_ranges["bar"]:
|
||||
address: 172.16.100.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: bar
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_global_address.psa_ranges["foo"]:
|
||||
address: 172.16.101.0
|
||||
address_type: INTERNAL
|
||||
description: null
|
||||
ip_version: null
|
||||
name: foo
|
||||
prefix_length: 24
|
||||
project: test-project
|
||||
purpose: VPC_PEERING
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
enable_ula_internal_ipv6: null
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_network_peering_routes_config.psa_routes["1"]:
|
||||
export_custom_routes: false
|
||||
import_custom_routes: false
|
||||
project: test-project
|
||||
google_service_networking_connection.psa_connection["1"]:
|
||||
reserved_peering_ranges:
|
||||
- bar
|
||||
- foo
|
||||
service: servicenetworking.googleapis.com
|
||||
|
||||
counts:
|
||||
google_compute_global_address: 2
|
||||
google_compute_network: 1
|
||||
google_compute_network_peering_routes_config: 1
|
||||
google_service_networking_connection: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
name: __missing__
|
||||
network: __missing__
|
||||
project_id: test-project
|
||||
self_link: __missing__
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
|
@ -0,0 +1,2 @@
|
|||
shared_vpc_host = true
|
||||
shared_vpc_service_projects = ["tf-a", "tf-b"]
|
|
@ -0,0 +1,46 @@
|
|||
# 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.
|
||||
|
||||
values:
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
google_compute_shared_vpc_host_project.shared_vpc_host[0]:
|
||||
project: test-project
|
||||
google_compute_shared_vpc_service_project.service_projects["tf-a"]:
|
||||
host_project: test-project
|
||||
service_project: tf-a
|
||||
google_compute_shared_vpc_service_project.service_projects["tf-b"]:
|
||||
host_project: test-project
|
||||
service_project: tf-b
|
||||
|
||||
counts:
|
||||
google_compute_network: 1
|
||||
google_compute_shared_vpc_host_project: 1
|
||||
google_compute_shared_vpc_service_project: 2
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
project_id: test-project
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
|
@ -0,0 +1 @@
|
|||
# skip boilerplate check
|
|
@ -0,0 +1,36 @@
|
|||
# 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.
|
||||
|
||||
values:
|
||||
google_compute_network.network[0]:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_routes_on_create: false
|
||||
description: Terraform-managed.
|
||||
name: test
|
||||
project: test-project
|
||||
routing_mode: GLOBAL
|
||||
|
||||
counts:
|
||||
google_compute_network: 1
|
||||
|
||||
outputs:
|
||||
bindings: {}
|
||||
project_id: test-project
|
||||
subnet_ips: {}
|
||||
subnet_regions: {}
|
||||
subnet_secondary_ranges: {}
|
||||
subnet_self_links: {}
|
||||
subnets: {}
|
||||
subnets_proxy_only: {}
|
||||
subnets_psc: {}
|
|
@ -0,0 +1,44 @@
|
|||
subnet_iam = {
|
||||
"europe-west1/a" = {
|
||||
"roles/compute.networkUser" = [
|
||||
"user:a@example.com", "group:g-a@example.com"
|
||||
]
|
||||
}
|
||||
"europe-west1/c" = {
|
||||
"roles/compute.networkUser" = [
|
||||
"user:c@example.com", "group:g-c@example.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
subnets = [
|
||||
{
|
||||
name = "a"
|
||||
region = "europe-west1"
|
||||
ip_cidr_range = "10.0.0.0/24"
|
||||
},
|
||||
{
|
||||
name = "b"
|
||||
region = "europe-west1"
|
||||
ip_cidr_range = "10.0.1.0/24",
|
||||
description = "Subnet b"
|
||||
enable_private_access = false
|
||||
},
|
||||
{
|
||||
name = "c"
|
||||
region = "europe-west1"
|
||||
ip_cidr_range = "10.0.2.0/24"
|
||||
secondary_ip_ranges = {
|
||||
a = "192.168.0.0/24"
|
||||
b = "192.168.1.0/24"
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "d"
|
||||
region = "europe-west1"
|
||||
ip_cidr_range = "10.0.3.0/24"
|
||||
flow_logs_config = {
|
||||
flow_sampling = 0.5
|
||||
aggregation_interval = "INTERVAL_10_MIN"
|
||||
}
|
||||
}
|
||||
]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue