Merge branch 'master' into nstrelkova-fast-fix-ew3

This commit is contained in:
Natalia Strelkova 2022-12-07 12:45:07 +01:00 committed by GitHub
commit 77736c13e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
143 changed files with 3192 additions and 1828 deletions

View File

@ -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 -->

View File

@ -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&#40;list&#40;string&#41;&#41;</code> | ✓ | |
| [environments](variables.tf#L30) | Environments. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; node_config &#61; optional&#40;object&#40;&#123;&#10; min_node_count &#61; optional&#40;number&#41;&#10; max_node_count &#61; optional&#40;number&#41;&#10; current_aggregate_node_count &#61; number&#10; &#125;&#41;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; envgroups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [instances](variables.tf#L46) | Instance. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; region &#61; string&#10; environments &#61; list&#40;string&#41;&#10; psa_ip_cidr_range &#61; string&#10; disk_encryption_key &#61; optional&#40;string&#41;&#10; consumer_accept_list &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [project_id](variables.tf#L92) | Project ID. | <code>string</code> | ✓ | |
| [psc_config](variables.tf#L98) | PSC configuration. | <code>map&#40;string&#41;</code> | ✓ | |
| [environments](variables.tf#L30) | Environments. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; node_config &#61; optional&#40;object&#40;&#123;&#10; min_node_count &#61; optional&#40;number&#41;&#10; max_node_count &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; envgroups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [instances](variables.tf#L45) | Instance. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; region &#61; string&#10; environments &#61; list&#40;string&#41;&#10; psa_ip_cidr_range &#61; string&#10; disk_encryption_key &#61; optional&#40;string&#41;&#10; consumer_accept_list &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [project_id](variables.tf#L91) | Project ID. | <code>string</code> | ✓ | |
| [psc_config](variables.tf#L97) | PSC configuration. | <code>map&#40;string&#41;</code> | ✓ | |
| [datastore_name](variables.tf#L17) | Datastore. | <code>string</code> | | <code>&#34;gcs&#34;</code> |
| [organization](variables.tf#L60) | Apigee organization. | <code title="object&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Apigee organization created by tf module&#34;&#41;&#10; description &#61; optional&#40;string, &#34;Apigee organization created by tf module&#34;&#41;&#10; authorized_network &#61; optional&#40;string, &#34;vpc&#34;&#41;&#10; runtime_type &#61; optional&#40;string, &#34;CLOUD&#34;&#41;&#10; billing_type &#61; optional&#40;string&#41;&#10; database_encryption_key &#61; optional&#40;string&#41;&#10; analytics_region &#61; optional&#40;string, &#34;europe-west1&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [path](variables.tf#L76) | Bucket path. | <code>string</code> | | <code>&#34;&#47;analytics&#34;</code> |
| [project_create](variables.tf#L83) | Parameters for the creation of the new project. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</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&#40;&#123;&#10; display_name &#61; optional&#40;string, &#34;Apigee organization created by tf module&#34;&#41;&#10; description &#61; optional&#40;string, &#34;Apigee organization created by tf module&#34;&#41;&#10; authorized_network &#61; optional&#40;string, &#34;vpc&#34;&#41;&#10; runtime_type &#61; optional&#40;string, &#34;CLOUD&#34;&#41;&#10; billing_type &#61; optional&#40;string&#41;&#10; database_encryption_key &#61; optional&#40;string&#41;&#10; analytics_region &#61; optional&#40;string, &#34;europe-west1&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [path](variables.tf#L75) | Bucket path. | <code>string</code> | | <code>&#34;&#47;analytics&#34;</code> |
| [project_create](variables.tf#L82) | Parameters for the creation of the new project. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</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

View File

@ -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": {

View File

@ -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)

View File

@ -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")

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
}
]
}

View File

@ -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 -->

View File

@ -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" {

View File

@ -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"

View File

@ -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>&#34;.oidc_token&#34;</code> |
## Outputs

View File

@ -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

View File

@ -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"]
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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 }

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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 = ""
}
}
}

View File

@ -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
}
}
}

View File

@ -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&#40;string&#41;</code> | | <code title="&#123;&#10; europe-west1 &#61; &#34;ew1&#34;&#10; europe-west4 &#61; &#34;ew4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_configs](variables.tf#L192) | Configurations for CRs and onprem routers. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-ew1 &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10; landing-trusted-ew4 &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L215) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>01-resman</code> |
| [vpn_onprem_configs](variables.tf#L229) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;object&#40;&#123;&#10; id &#61; number&#10; ip_address &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; landing-trusted-ew4 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [vpn_onprem_configs](variables.tf#L229) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; landing-trusted-ew4 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs

View File

@ -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 = [
{

View File

@ -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
}

View File

@ -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&#40;string&#41;</code> | | <code title="&#123;&#10; europe-west1 &#61; &#34;ew1&#34;&#10; europe-west3 &#61; &#34;ew3&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_onprem_configs](variables.tf#L175) | Configurations for routers used for onprem connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; asn &#61; &#34;65533&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L193) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>01-resman</code> |
| [vpn_onprem_configs](variables.tf#L207) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;object&#40;&#123;&#10; id &#61; number&#10; ip_address &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [vpn_onprem_configs](variables.tf#L207) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs

View File

@ -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 = [
{

View File

@ -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
}

View File

@ -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&#40;&#123;&#10; dev &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; prod &#61; object&#40;&#123;&#10; ranges &#61; map&#40;string&#41;&#10; routes &#61; object&#40;&#123;&#10; export &#61; bool&#10; import &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [router_onprem_configs](variables.tf#L166) | Configurations for routers used for onprem connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; prod-ew1 &#61; &#123;&#10; asn &#61; &#34;65533&#34;&#10; adv &#61; null&#10; &#125;&#10; dev-ew1 &#61; &#123;&#10; asn &#61; &#34;65534&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L189) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>01-resman</code> |
| [vpn_onprem_configs](variables.tf#L201) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;object&#40;&#123;&#10; id &#61; number&#10; ip_address &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; dev-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_dev&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65544&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65544&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; prod-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_prod&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65543&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65543&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [vpn_onprem_configs](variables.tf#L201) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; dev-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_dev&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10;&#10;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65544&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65544&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; prod-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_prod&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65543&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65543&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs

View File

@ -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 = [
{

View File

@ -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
}

View File

@ -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
}

View File

@ -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&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; asn &#61; &#34;65533&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_spoke_configs](variables-vpn.tf#L18) | Configurations for routers used for internal connectivity. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123; asn &#61; &#34;64512&#34;, adv &#61; null &#125;&#10; landing-ew4 &#61; &#123; asn &#61; &#34;64512&#34;, adv &#61; null &#125;&#10; spoke-dev-ew1 &#61; &#123; asn &#61; &#34;64513&#34;, adv &#61; null &#125;&#10; spoke-dev-ew4 &#61; &#123; asn &#61; &#34;64513&#34;, adv &#61; null &#125;&#10; spoke-prod-ew1 &#61; &#123; asn &#61; &#34;64514&#34;, adv &#61; null &#125;&#10; spoke-prod-ew4 &#61; &#123; asn &#61; &#34;64514&#34;, adv &#61; null &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [service_accounts](variables.tf#L193) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; data-platform-dev &#61; string&#10; data-platform-prod &#61; string&#10; gke-dev &#61; string&#10; gke-prod &#61; string&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>01-resman</code> |
| [vpn_onprem_configs](variables.tf#L207) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;object&#40;&#123;&#10; id &#61; number&#10; ip_address &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [vpn_onprem_configs](variables.tf#L207) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#34;8.8.8.8&#34;&#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [vpn_spoke_configs](variables-vpn.tf#L37) | VPN gateway configuration for spokes. | <code title="map&#40;object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;rfc_1918_10&#34;, &#34;rfc_1918_172&#34;, &#34;rfc_1918_192&#34;&#93;&#10; &#125;&#10; landing-ew4 &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;rfc_1918_10&#34;, &#34;rfc_1918_172&#34;, &#34;rfc_1918_192&#34;&#93;&#10; &#125;&#10; dev-ew1 &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_dev&#34;&#93;&#10; &#125;&#10; prod-ew1 &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_prod&#34;&#93;&#10; &#125;&#10; prod-ew4 &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_prod&#34;&#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs

View File

@ -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 = [
{

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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"]
}
}
```

View File

@ -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&#40;object&#40;&#123;&#10; region &#61; string&#10; service_attachment &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> |
| [envgroups](variables.tf#L26) | Environment groups (NAME => [HOSTNAMES]). | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>null</code> |
| [environments](variables.tf#L32) | Environments. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed&#34;&#41;&#10; node_config &#61; optional&#40;object&#40;&#123;&#10; min_node_count &#61; optional&#40;number&#41;&#10; max_node_count &#61; optional&#40;number&#41;&#10; current_aggregate_node_count &#61; number&#10; &#125;&#41;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; envgroups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> |
| [instances](variables.tf#L48) | Instances. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed&#34;&#41;&#10; region &#61; string&#10; environments &#61; list&#40;string&#41;&#10; psa_ip_cidr_range &#61; string&#10; disk_encryption_key &#61; optional&#40;string&#41;&#10; consumer_accept_list &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> |
| [organization](variables.tf#L62) | Apigee organization. If set to null the organization must already exist. | <code title="object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed&#34;&#41;&#10; authorized_network &#61; optional&#40;string&#41;&#10; runtime_type &#61; optional&#40;string, &#34;CLOUD&#34;&#41;&#10; billing_type &#61; optional&#40;string&#41;&#10; database_encryption_key &#61; optional&#40;string&#41;&#10; analytics_region &#61; optional&#40;string, &#34;europe-west1&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [environments](variables.tf#L32) | Environments. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed&#34;&#41;&#10; node_config &#61; optional&#40;object&#40;&#123;&#10; min_node_count &#61; optional&#40;number&#41;&#10; max_node_count &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;&#41;&#10; envgroups &#61; list&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> |
| [instances](variables.tf#L47) | Instances. | <code title="map&#40;object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed&#34;&#41;&#10; region &#61; string&#10; environments &#61; list&#40;string&#41;&#10; psa_ip_cidr_range &#61; string&#10; disk_encryption_key &#61; optional&#40;string&#41;&#10; consumer_accept_list &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> |
| [organization](variables.tf#L61) | Apigee organization. If set to null the organization must already exist. | <code title="object&#40;&#123;&#10; display_name &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed&#34;&#41;&#10; authorized_network &#61; optional&#40;string&#41;&#10; runtime_type &#61; optional&#40;string, &#34;CLOUD&#34;&#41;&#10; billing_type &#61; optional&#40;string&#41;&#10; database_encryption_key &#61; optional&#40;string&#41;&#10; analytics_region &#61; optional&#40;string, &#34;europe-west1&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
## Outputs

View File

@ -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" {

View File

@ -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
}
}

View File

@ -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 }
}
}

View File

@ -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

View File

@ -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

View File

@ -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&#40;&#123;&#10; admin_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; http_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;&#10; &#41;&#10; http_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;http-server&#34;&#93;&#41;&#10; https_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;&#10; &#41;&#10; https_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;https-server&#34;&#93;&#41;&#10; ssh_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#34;35.235.240.0&#47;20&#34;&#93;&#41;&#10; ssh_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;ssh&#34;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</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&#40;&#123;&#10; admin_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; http_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;&#10; &#41;&#10; http_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;http-server&#34;&#93;&#41;&#10; https_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;&#10; &#41;&#10; https_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;https-server&#34;&#93;&#41;&#10; ssh_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#34;35.235.240.0&#47;20&#34;&#93;&#41;&#10; ssh_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;ssh&#34;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [egress_rules](variables.tf#L37) | List of egress rule definitions, default to deny action. | <code title="map&#40;object&#40;&#123;&#10; deny &#61; optional&#40;bool, true&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;object&#40;&#123;&#10; include_metadata &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; priority &#61; optional&#40;number, 1000&#41;&#10; sources &#61; optional&#40;list&#40;string&#41;&#41;&#10; targets &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_service_accounts &#61; optional&#40;bool, false&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; string&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123; protocol &#61; &#34;all&#34; &#125;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [factories_config](variables.tf#L60) | Paths to data files and folders that enable factory functionality. | <code title="object&#40;&#123;&#10; cidr_tpl_file &#61; optional&#40;string&#41;&#10; rules_folder &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [ingress_rules](variables.tf#L69) | List of ingress rule definitions, default to allow action. | <code title="map&#40;object&#40;&#123;&#10; deny &#61; optional&#40;bool, false&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;object&#40;&#123;&#10; include_metadata &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; priority &#61; optional&#40;number, 1000&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; sources &#61; optional&#40;list&#40;string&#41;&#41;&#10; targets &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_service_accounts &#61; optional&#40;bool, false&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; string&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123; protocol &#61; &#34;all&#34; &#125;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |

View File

@ -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)

View File

@ -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>&#34;&#34;</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&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</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&#40;&#123;&#10; groups &#61; list&#40;string&#41;&#10; ip_ranges &#61; map&#40;string&#41;&#10; mode &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</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>&#34;&#34;</code> |
| [tunnels](variables.tf#L83) | VPN tunnel configurations, bgp_peer_options is usually null. | <code title="map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; &#125;&#41;&#10; bgp_peer_options &#61; object&#40;&#123;&#10; advertise_groups &#61; list&#40;string&#41;&#10; advertise_ip_ranges &#61; map&#40;string&#41;&#10; advertise_mode &#61; string&#10; route_priority &#61; number&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; number&#10; peer_ip &#61; string&#10; router &#61; string&#10; shared_secret &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [tunnels](variables.tf#L64) | VPN tunnel configurations. | <code title="map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_ip &#61; string&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs

View File

@ -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]
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;object&#40;&#123;&#10; id &#61; number&#10; ip_address &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</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&#40;&#123;&#10; groups &#61; list&#40;string&#41;&#10; ip_ranges &#61; map&#40;string&#41;&#10; mode &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</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>&#34;&#34;</code> |
| [tunnels](variables.tf#L89) | VPN tunnel configurations, bgp_peer_options is usually null. | <code title="map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; &#125;&#41;&#10; bgp_peer_options &#61; object&#40;&#123;&#10; advertise_groups &#61; list&#40;string&#41;&#10; advertise_ip_ranges &#61; map&#40;string&#41;&#10; advertise_mode &#61; string&#10; route_priority &#61; number&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; number&#10; peer_external_gateway_interface &#61; number&#10; router &#61; string&#10; shared_secret &#61; string&#10; vpn_gateway_interface &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</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&#40;&#123;&#10; external &#61; optional&#40;object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10; gcp &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</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&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; number&#10; name &#61; optional&#40;string&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [tunnels](variables.tf#L68) | VPN tunnel configurations. | <code title="map&#40;object&#40;&#123;&#10; bgp_peer &#61; object&#40;&#123;&#10; address &#61; string&#10; asn &#61; number&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; all_vpc_subnets &#61; bool&#10; all_peer_vpc_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; bgp_session_range &#61; string&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_external_gateway_interface &#61; optional&#40;number&#41;&#10; router &#61; optional&#40;string&#41;&#10; shared_secret &#61; optional&#40;string&#41;&#10; vpn_gateway_interface &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</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 -->

View File

@ -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" {

View File

@ -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" {

View File

@ -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
}

View File

@ -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>&#34;&#34;</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&#40;string&#41;</code> | | <code>&#91;&#93;</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&#40;object&#40;&#123;&#10; ike_version &#61; number&#10; peer_ip &#61; string&#10; shared_secret &#61; string&#10; traffic_selectors &#61; object&#40;&#123;&#10; local &#61; list&#40;string&#41;&#10; remote &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</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&#40;object&#40;&#123;&#10; ike_version &#61; optional&#40;number, 2&#41;&#10; peer_ip &#61; string&#10; shared_secret &#61; optional&#40;string&#41;&#10; traffic_selectors &#61; object&#40;&#123;&#10; local &#61; list&#40;string&#41;&#10; remote &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs

View File

@ -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]
}

View File

@ -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
}

View File

@ -193,7 +193,7 @@ module "test" {
| [egress_policies](variables.tf#L70) | Egress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#10; from &#61; object&#40;&#123;&#10; identity_type &#61; optional&#40;string, &#34;ANY_IDENTITY&#34;&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; resource_type_external &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [ingress_policies](variables.tf#L99) | Ingress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#10; from &#61; object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; identity_type &#61; optional&#40;string&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_bridge](variables.tf#L130) | Bridge service perimeters. | <code title="map&#40;object&#40;&#123;&#10; spec_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; status_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_explicit_dry_run_spec &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_regular](variables.tf#L140) | Regular service perimeters. | <code title="map&#40;object&#40;&#123;&#10; spec &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; bool&#10; &#125;&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; status &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; bool&#10; &#125;&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; use_explicit_dry_run_spec &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_regular](variables.tf#L140) | Regular service perimeters. | <code title="map&#40;object&#40;&#123;&#10; spec &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; bool&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; status &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; bool&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; use_explicit_dry_run_spec &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs

View File

@ -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
}
}

View File

@ -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 = {}

View File

@ -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)

92
tests/collectors.py Normal file
View File

@ -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)

View File

@ -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',
)

View File

@ -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"
}
}

View File

@ -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
}
}

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

235
tests/fixtures.py Normal file
View File

@ -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]

153
tests/legacy_fixtures.py Normal file
View File

@ -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

View File

@ -4,5 +4,9 @@ environments = {
display_name = "APIs test"
description = "APIs Test"
envgroups = ["test"]
node_config = {
min_node_count = 2
max_node_count = 5
}
}
}
}

View File

@ -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
}
}

View File

@ -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")

View File

@ -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)

View File

@ -0,0 +1,2 @@
project_id = "test-project"
name = "test"

View File

@ -0,0 +1 @@
data_folder = "../../tests/modules/net_vpc/data"

View File

@ -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

View File

@ -0,0 +1,5 @@
peering_config = {
peer_vpc_self_link = "projects/my-project/global/networks/peer"
export_routes = true
import_routes = null
}

View File

@ -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: {}

View File

@ -0,0 +1,7 @@
psa_config = {
ranges = {
bar = "172.16.100.0/24"
}
export_routes = true
import_routes = false
}

View File

@ -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: {}

View File

@ -0,0 +1,7 @@
psa_config = {
ranges = {
bar = "172.16.100.0/24"
}
export_routes = false
import_routes = true
}

View File

@ -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: {}

View File

@ -0,0 +1,7 @@
psa_config = {
ranges = {
bar = "172.16.100.0/24"
}
export_routes = true
import_routes = true
}

View File

@ -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: {}

View File

@ -0,0 +1,7 @@
psa_config = {
ranges = {
bar = "172.16.100.0/24"
foo = "172.16.101.0/24"
}
routes = null
}

View File

@ -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: {}

View File

@ -0,0 +1,2 @@
shared_vpc_host = true
shared_vpc_service_projects = ["tf-a", "tf-b"]

View File

@ -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: {}

View File

@ -0,0 +1 @@
# skip boilerplate check

View File

@ -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: {}

View File

@ -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