From 0fe3f165edcf77f7cfc5dcb7649360c21cdb0785 Mon Sep 17 00:00:00 2001 From: Ana Fernandez del Alamo <20087057+afda16@users.noreply.github.com> Date: Fri, 2 Jun 2023 15:55:35 +0100 Subject: [PATCH] Add VPN monitoring alerts to 2-networking and VPN usage chart The Fast stage 2-networking-* currently adds a monitoring dashboard for VPN metrics. This change adds an additional chart to monitor the usage of the VPN bandwidth. This change also adds the following monitoring alerts: * VPN tunnel established * [VPN bandwidth](https://cloud.google.com/network-connectivity/docs/vpn/how-to/viewing-logs-metrics#define-bandwidth-alerts) To configure the alerts, there is a new `alert_config` variable with defined default values. The alerts are created in the stage `b` by default. In the stages a, c, d, and e, the alerts are created if the user creates the On-prem VPN. To disable the creation of alerts, add the following to `terraform.tfvars`: ``` alert_config = { vpn_tunnel_established = null vpn_tunnel_bandwidth = null } ``` --- fast/stages/2-networking-a-peering/README.md | 28 +- .../data/dashboards/vpn.json | 468 ++++++++++-------- .../monitoring-vpn-onprem.tf | 99 ++++ .../2-networking-a-peering/variables.tf | 27 +- fast/stages/2-networking-b-vpn/README.md | 28 +- .../data/dashboards/vpn.json | 468 ++++++++++-------- .../2-networking-b-vpn/monitoring-vpn.tf | 99 ++++ fast/stages/2-networking-b-vpn/variables.tf | 27 +- fast/stages/2-networking-c-nva/README.md | 34 +- .../data/dashboards/vpn.json | 466 +++++++++-------- .../monitoring-vpn-onprem.tf | 99 ++++ fast/stages/2-networking-c-nva/variables.tf | 27 +- .../2-networking-d-separate-envs/README.md | 30 +- .../data/dashboards/vpn.json | 468 ++++++++++-------- .../monitoring-vpn-onprem.tf | 108 ++++ .../2-networking-d-separate-envs/variables.tf | 27 +- fast/stages/2-networking-e-nva-bgp/README.md | 38 +- .../data/dashboards/vpn.json | 466 +++++++++-------- .../monitoring-vpn-onprem.tf | 99 ++++ .../2-networking-e-nva-bgp/variables.tf | 27 +- .../stages/s2_networking_a_peering/stage.yaml | 2 +- .../stages/s2_networking_b_vpn/stage.yaml | 2 +- .../stages/s2_networking_c_nva/stage.yaml | 2 +- .../s2_networking_d_separate_envs/stage.yaml | 2 +- .../stages/s2_networking_e_nva_bgp/stage.yaml | 2 +- 25 files changed, 2076 insertions(+), 1067 deletions(-) create mode 100644 fast/stages/2-networking-a-peering/monitoring-vpn-onprem.tf create mode 100644 fast/stages/2-networking-b-vpn/monitoring-vpn.tf create mode 100644 fast/stages/2-networking-c-nva/monitoring-vpn-onprem.tf create mode 100644 fast/stages/2-networking-d-separate-envs/monitoring-vpn-onprem.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/monitoring-vpn-onprem.tf diff --git a/fast/stages/2-networking-a-peering/README.md b/fast/stages/2-networking-a-peering/README.md index 861549d5..0edfe06f 100644 --- a/fast/stages/2-networking-a-peering/README.md +++ b/fast/stages/2-networking-a-peering/README.md @@ -380,6 +380,7 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | | [landing.tf](./landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | | [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring-vpn-onprem.tf](./monitoring-vpn-onprem.tf) | VPN monitoring alerts. | | google_monitoring_alert_policy | | [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | | [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | | [peerings.tf](./peerings.tf) | None | net-vpc-peering | | @@ -395,20 +396,21 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L76) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L86) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L102) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | -| [custom_roles](variables.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [dns](variables.tf#L47) | Onprem DNS resolvers. | map(list(string)) | | {…} | | -| [factories_config](variables.tf#L55) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L96) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L101) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L111) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L127) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | +| [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [dns](variables.tf#L72) | Onprem DNS resolvers. | map(list(string)) | | {…} | | +| [factories_config](variables.tf#L80) | Configuration for network resource factories. | object({…}) | | {…} | | +| [outputs_location](variables.tf#L121) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | | [peering_configs](variables-peerings.tf#L19) | Peering configurations. | map(object({…})) | | {…} | | -| [psa_ranges](variables.tf#L113) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | null | | -| [regions](variables.tf#L134) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L146) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L160) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [psa_ranges](variables.tf#L138) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | null | | +| [regions](variables.tf#L159) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L171) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L185) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-a-peering/data/dashboards/vpn.json b/fast/stages/2-networking-a-peering/data/dashboards/vpn.json index 4396cc00..4c86a307 100644 --- a/fast/stages/2-networking-a-peering/data/dashboards/vpn.json +++ b/fast/stages/2-networking-a-peering/data/dashboards/vpn.json @@ -1,248 +1,322 @@ { "displayName": "VPN Monitoring", - "gridLayout": { - "columns": "2", - "widgets": [ + "mosaicLayout": { + "columns": 12, + "tiles": [ { - "title": "Number of connections", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Number of connections", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4 }, { - "title": "Tunnel established", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Tunnel established", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 4 }, { - "title": "Cloud VPN Gateway - Received bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "height": 4, + "widget": { + "title": "VPN Tunnel Bandwidth usage", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesQueryLanguage": "fetch vpn_gateway| { metric vpn.googleapis.com/network/sent_bytes_count; metric vpn.googleapis.com/network/received_bytes_count }| align rate (1m)| group_by [metric.tunnel_name]| outer_join 0,0| value val(0) + val(1)| condition val() > 187.5 \"MBy/s\"" + } } + ], + "thresholds": [ + { + "targetAxis": "Y1", + "value": 187500000 + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 8 }, { - "title": "Cloud VPN Gateway - Sent bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Received packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Sent packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 8 }, { - "title": "Incoming packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 8 }, { - "title": "Outgoing packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Incoming packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 12 + }, + { + "height": 4, + "widget": { + "title": "Outgoing packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + "width": 6, + "yPos": 12 } ] } -} \ No newline at end of file +} diff --git a/fast/stages/2-networking-a-peering/monitoring-vpn-onprem.tf b/fast/stages/2-networking-a-peering/monitoring-vpn-onprem.tf new file mode 100644 index 00000000..d63c14d8 --- /dev/null +++ b/fast/stages/2-networking-a-peering/monitoring-vpn-onprem.tf @@ -0,0 +1,99 @@ +/** + * 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. + */ + +# tfdoc:file:description VPN monitoring alerts. + +resource "google_monitoring_alert_policy" "vpn_tunnel_established" { + count = (var.vpn_onprem_primary_config != null && var.alert_config.vpn_tunnel_established != null) ? 1 : 0 + + project = module.landing-project.project_id + display_name = "VPN Tunnel Established" + enabled = var.alert_config.vpn_tunnel_established.enabled + notification_channels = var.alert_config.vpn_tunnel_established.notification_channels + user_labels = var.alert_config.vpn_tunnel_established.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Established" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| metric vpn.googleapis.com/tunnel_established", + "| group_by 5m, [value_tunnel_established_max: max(value.tunnel_established)]", + "| every 5m", + "| condition val() < 1 '1'", + ]) + + duration = var.alert_config.vpn_tunnel_established.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_established.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_established.auto_close + } + } +} + +# https://cloud.google.com/network-connectivity/docs/vpn/how-to/viewing-logs-metrics#define-bandwidth-alerts +resource "google_monitoring_alert_policy" "vpn_tunnel_bandwidth" { + count = (var.vpn_onprem_primary_config != null && var.alert_config.vpn_tunnel_bandwidth != null) ? 1 : 0 + + project = module.landing-project.project_id + display_name = "VPN Tunnel Bandwidth usage" + enabled = var.alert_config.vpn_tunnel_bandwidth.enabled + notification_channels = var.alert_config.vpn_tunnel_bandwidth.notification_channels + user_labels = var.alert_config.vpn_tunnel_bandwidth.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Bandwidth usage" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| { metric vpn.googleapis.com/network/sent_bytes_count", + "; metric vpn.googleapis.com/network/received_bytes_count }", + "| align rate (1m)", + "| group_by [metric.tunnel_name]", + "| outer_join 0,0", + "| value val(0) + val(1)", + "| condition val() > ${var.alert_config.vpn_tunnel_bandwidth.threshold_mbys} \"MBy/s\"", + ]) + + duration = var.alert_config.vpn_tunnel_bandwidth.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_bandwidth.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_bandwidth.auto_close + } + } +} diff --git a/fast/stages/2-networking-a-peering/variables.tf b/fast/stages/2-networking-a-peering/variables.tf index c5f58cd4..89024258 100644 --- a/fast/stages/2-networking-a-peering/variables.tf +++ b/fast/stages/2-networking-a-peering/variables.tf @@ -14,6 +14,31 @@ * limitations under the License. */ +variable "alert_config" { + description = "Configuration for monitoring alerts." + type = object({ + vpn_tunnel_established = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + user_labels = optional(map(string), {}) + })) + vpn_tunnel_bandwidth = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + threshold_mbys = optional(string, "187.5") + user_labels = optional(map(string), {}) + })) + }) + default = { + vpn_tunnel_established = {} + vpn_tunnel_bandwidth = {} + } +} + variable "automation" { # tfdoc:variable:source 0-bootstrap description = "Automation resources created by the bootstrap stage." @@ -198,4 +223,4 @@ variable "vpn_onprem_primary_config" { })) }) default = null -} +} \ No newline at end of file diff --git a/fast/stages/2-networking-b-vpn/README.md b/fast/stages/2-networking-b-vpn/README.md index 0d0b02ff..c74cfb79 100644 --- a/fast/stages/2-networking-b-vpn/README.md +++ b/fast/stages/2-networking-b-vpn/README.md @@ -403,6 +403,7 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | | [landing.tf](./landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | | [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring-vpn.tf](./monitoring-vpn.tf) | VPN monitoring alerts. | | google_monitoring_alert_policy | | [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | | [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | | [regions.tf](./regions.tf) | Compute short names for regions. | | | @@ -420,20 +421,21 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L76) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L86) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L102) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | -| [custom_roles](variables.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [dns](variables.tf#L47) | Onprem DNS resolvers. | map(list(string)) | | {…} | | -| [factories_config](variables.tf#L55) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L96) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L113) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | null | | -| [regions](variables.tf#L134) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L146) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L101) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L111) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L127) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | +| [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [dns](variables.tf#L72) | Onprem DNS resolvers. | map(list(string)) | | {…} | | +| [factories_config](variables.tf#L80) | Configuration for network resource factories. | object({…}) | | {…} | | +| [outputs_location](variables.tf#L121) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L138) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | null | | +| [regions](variables.tf#L159) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L171) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | | [vpn_configs](variables-vpn.tf#L17) | Hub to spokes VPN configurations. | object({…}) | | {…} | | -| [vpn_onprem_primary_config](variables.tf#L160) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_primary_config](variables.tf#L185) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-b-vpn/data/dashboards/vpn.json b/fast/stages/2-networking-b-vpn/data/dashboards/vpn.json index 4396cc00..4c86a307 100644 --- a/fast/stages/2-networking-b-vpn/data/dashboards/vpn.json +++ b/fast/stages/2-networking-b-vpn/data/dashboards/vpn.json @@ -1,248 +1,322 @@ { "displayName": "VPN Monitoring", - "gridLayout": { - "columns": "2", - "widgets": [ + "mosaicLayout": { + "columns": 12, + "tiles": [ { - "title": "Number of connections", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Number of connections", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4 }, { - "title": "Tunnel established", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Tunnel established", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 4 }, { - "title": "Cloud VPN Gateway - Received bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "height": 4, + "widget": { + "title": "VPN Tunnel Bandwidth usage", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesQueryLanguage": "fetch vpn_gateway| { metric vpn.googleapis.com/network/sent_bytes_count; metric vpn.googleapis.com/network/received_bytes_count }| align rate (1m)| group_by [metric.tunnel_name]| outer_join 0,0| value val(0) + val(1)| condition val() > 187.5 \"MBy/s\"" + } } + ], + "thresholds": [ + { + "targetAxis": "Y1", + "value": 187500000 + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 8 }, { - "title": "Cloud VPN Gateway - Sent bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Received packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Sent packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 8 }, { - "title": "Incoming packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 8 }, { - "title": "Outgoing packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Incoming packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 12 + }, + { + "height": 4, + "widget": { + "title": "Outgoing packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + "width": 6, + "yPos": 12 } ] } -} \ No newline at end of file +} diff --git a/fast/stages/2-networking-b-vpn/monitoring-vpn.tf b/fast/stages/2-networking-b-vpn/monitoring-vpn.tf new file mode 100644 index 00000000..eae1641e --- /dev/null +++ b/fast/stages/2-networking-b-vpn/monitoring-vpn.tf @@ -0,0 +1,99 @@ +/** + * 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. + */ + +# tfdoc:file:description VPN monitoring alerts. + +resource "google_monitoring_alert_policy" "vpn_tunnel_established" { + count = var.alert_config.vpn_tunnel_established != null ? 1 : 0 + + project = module.landing-project.project_id + display_name = "VPN Tunnel Established" + enabled = var.alert_config.vpn_tunnel_established.enabled + notification_channels = var.alert_config.vpn_tunnel_established.notification_channels + user_labels = var.alert_config.vpn_tunnel_established.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Established" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| metric vpn.googleapis.com/tunnel_established", + "| group_by 5m, [value_tunnel_established_max: max(value.tunnel_established)]", + "| every 5m", + "| condition val() < 1 '1'", + ]) + + duration = var.alert_config.vpn_tunnel_established.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_established.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_established.auto_close + } + } +} + +# https://cloud.google.com/network-connectivity/docs/vpn/how-to/viewing-logs-metrics#define-bandwidth-alerts +resource "google_monitoring_alert_policy" "vpn_tunnel_bandwidth" { + count = var.alert_config.vpn_tunnel_bandwidth != null ? 1 : 0 + + project = module.landing-project.project_id + display_name = "VPN Tunnel Bandwidth usage" + enabled = var.alert_config.vpn_tunnel_bandwidth.enabled + notification_channels = var.alert_config.vpn_tunnel_bandwidth.notification_channels + user_labels = var.alert_config.vpn_tunnel_bandwidth.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Bandwidth usage" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| { metric vpn.googleapis.com/network/sent_bytes_count", + "; metric vpn.googleapis.com/network/received_bytes_count }", + "| align rate (1m)", + "| group_by [metric.tunnel_name]", + "| outer_join 0,0", + "| value val(0) + val(1)", + "| condition val() > ${var.alert_config.vpn_tunnel_bandwidth.threshold_mbys} \"MBy/s\"", + ]) + + duration = var.alert_config.vpn_tunnel_bandwidth.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_bandwidth.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_bandwidth.auto_close + } + } +} diff --git a/fast/stages/2-networking-b-vpn/variables.tf b/fast/stages/2-networking-b-vpn/variables.tf index c5f58cd4..89024258 100644 --- a/fast/stages/2-networking-b-vpn/variables.tf +++ b/fast/stages/2-networking-b-vpn/variables.tf @@ -14,6 +14,31 @@ * limitations under the License. */ +variable "alert_config" { + description = "Configuration for monitoring alerts." + type = object({ + vpn_tunnel_established = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + user_labels = optional(map(string), {}) + })) + vpn_tunnel_bandwidth = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + threshold_mbys = optional(string, "187.5") + user_labels = optional(map(string), {}) + })) + }) + default = { + vpn_tunnel_established = {} + vpn_tunnel_bandwidth = {} + } +} + variable "automation" { # tfdoc:variable:source 0-bootstrap description = "Automation resources created by the bootstrap stage." @@ -198,4 +223,4 @@ variable "vpn_onprem_primary_config" { })) }) default = null -} +} \ No newline at end of file diff --git a/fast/stages/2-networking-c-nva/README.md b/fast/stages/2-networking-c-nva/README.md index 32471fd1..34443a18 100644 --- a/fast/stages/2-networking-c-nva/README.md +++ b/fast/stages/2-networking-c-nva/README.md @@ -462,6 +462,7 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | | [landing.tf](./landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | | [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring-vpn-onprem.tf](./monitoring-vpn-onprem.tf) | VPN monitoring alerts. | | google_monitoring_alert_policy | | [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | | [nva.tf](./nva.tf) | None | compute-mig · compute-vm · simple-nva | | | [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | @@ -476,22 +477,23 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L76) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L109) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L125) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | -| [custom_roles](variables.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [dns](variables.tf#L47) | Onprem DNS resolvers. | map(list(string)) | | {…} | | -| [factories_config](variables.tf#L55) | Configuration for network resource factories. | object({…}) | | {…} | | -| [gcp_ranges](variables.tf#L86) | GCP address ranges in name => range format. | map(string) | | {…} | | -| [onprem_cidr](variables.tf#L101) | Onprem addresses in name => range format. | map(string) | | {…} | | -| [outputs_location](variables.tf#L119) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L136) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | -| [regions](variables.tf#L157) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L169) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L183) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | -| [vpn_onprem_secondary_config](variables.tf#L226) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | +| [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L101) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L134) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L150) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | +| [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [dns](variables.tf#L72) | Onprem DNS resolvers. | map(list(string)) | | {…} | | +| [factories_config](variables.tf#L80) | Configuration for network resource factories. | object({…}) | | {…} | | +| [gcp_ranges](variables.tf#L111) | GCP address ranges in name => range format. | map(string) | | {…} | | +| [onprem_cidr](variables.tf#L126) | Onprem addresses in name => range format. | map(string) | | {…} | | +| [outputs_location](variables.tf#L144) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L161) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | +| [regions](variables.tf#L182) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L194) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L208) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_secondary_config](variables.tf#L251) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-c-nva/data/dashboards/vpn.json b/fast/stages/2-networking-c-nva/data/dashboards/vpn.json index 1aec3e45..4c86a307 100644 --- a/fast/stages/2-networking-c-nva/data/dashboards/vpn.json +++ b/fast/stages/2-networking-c-nva/data/dashboards/vpn.json @@ -1,247 +1,321 @@ { "displayName": "VPN Monitoring", - "gridLayout": { - "columns": "2", - "widgets": [ + "mosaicLayout": { + "columns": 12, + "tiles": [ { - "title": "Number of connections", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Number of connections", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4 }, { - "title": "Tunnel established", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Tunnel established", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 4 }, { - "title": "Cloud VPN Gateway - Received bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "height": 4, + "widget": { + "title": "VPN Tunnel Bandwidth usage", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesQueryLanguage": "fetch vpn_gateway| { metric vpn.googleapis.com/network/sent_bytes_count; metric vpn.googleapis.com/network/received_bytes_count }| align rate (1m)| group_by [metric.tunnel_name]| outer_join 0,0| value val(0) + val(1)| condition val() > 187.5 \"MBy/s\"" + } } + ], + "thresholds": [ + { + "targetAxis": "Y1", + "value": 187500000 + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 8 }, { - "title": "Cloud VPN Gateway - Sent bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Received packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Sent packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 8 }, { - "title": "Incoming packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 8 }, { - "title": "Outgoing packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Incoming packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 12 + }, + { + "height": 4, + "widget": { + "title": "Outgoing packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + "width": 6, + "yPos": 12 } ] } diff --git a/fast/stages/2-networking-c-nva/monitoring-vpn-onprem.tf b/fast/stages/2-networking-c-nva/monitoring-vpn-onprem.tf new file mode 100644 index 00000000..d63c14d8 --- /dev/null +++ b/fast/stages/2-networking-c-nva/monitoring-vpn-onprem.tf @@ -0,0 +1,99 @@ +/** + * 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. + */ + +# tfdoc:file:description VPN monitoring alerts. + +resource "google_monitoring_alert_policy" "vpn_tunnel_established" { + count = (var.vpn_onprem_primary_config != null && var.alert_config.vpn_tunnel_established != null) ? 1 : 0 + + project = module.landing-project.project_id + display_name = "VPN Tunnel Established" + enabled = var.alert_config.vpn_tunnel_established.enabled + notification_channels = var.alert_config.vpn_tunnel_established.notification_channels + user_labels = var.alert_config.vpn_tunnel_established.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Established" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| metric vpn.googleapis.com/tunnel_established", + "| group_by 5m, [value_tunnel_established_max: max(value.tunnel_established)]", + "| every 5m", + "| condition val() < 1 '1'", + ]) + + duration = var.alert_config.vpn_tunnel_established.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_established.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_established.auto_close + } + } +} + +# https://cloud.google.com/network-connectivity/docs/vpn/how-to/viewing-logs-metrics#define-bandwidth-alerts +resource "google_monitoring_alert_policy" "vpn_tunnel_bandwidth" { + count = (var.vpn_onprem_primary_config != null && var.alert_config.vpn_tunnel_bandwidth != null) ? 1 : 0 + + project = module.landing-project.project_id + display_name = "VPN Tunnel Bandwidth usage" + enabled = var.alert_config.vpn_tunnel_bandwidth.enabled + notification_channels = var.alert_config.vpn_tunnel_bandwidth.notification_channels + user_labels = var.alert_config.vpn_tunnel_bandwidth.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Bandwidth usage" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| { metric vpn.googleapis.com/network/sent_bytes_count", + "; metric vpn.googleapis.com/network/received_bytes_count }", + "| align rate (1m)", + "| group_by [metric.tunnel_name]", + "| outer_join 0,0", + "| value val(0) + val(1)", + "| condition val() > ${var.alert_config.vpn_tunnel_bandwidth.threshold_mbys} \"MBy/s\"", + ]) + + duration = var.alert_config.vpn_tunnel_bandwidth.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_bandwidth.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_bandwidth.auto_close + } + } +} diff --git a/fast/stages/2-networking-c-nva/variables.tf b/fast/stages/2-networking-c-nva/variables.tf index b56c72d8..c5becd21 100644 --- a/fast/stages/2-networking-c-nva/variables.tf +++ b/fast/stages/2-networking-c-nva/variables.tf @@ -14,6 +14,31 @@ * limitations under the License. */ +variable "alert_config" { + description = "Configuration for monitoring alerts." + type = object({ + vpn_tunnel_established = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + user_labels = optional(map(string), {}) + })) + vpn_tunnel_bandwidth = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + threshold_mbys = optional(string, "187.5") + user_labels = optional(map(string), {}) + })) + }) + default = { + vpn_tunnel_established = {} + vpn_tunnel_bandwidth = {} + } +} + variable "automation" { # tfdoc:variable:source 0-bootstrap description = "Automation resources created by the bootstrap stage." @@ -264,4 +289,4 @@ variable "vpn_onprem_secondary_config" { })) }) default = null -} +} \ No newline at end of file diff --git a/fast/stages/2-networking-d-separate-envs/README.md b/fast/stages/2-networking-d-separate-envs/README.md index 7158a964..4b122ee2 100644 --- a/fast/stages/2-networking-d-separate-envs/README.md +++ b/fast/stages/2-networking-d-separate-envs/README.md @@ -325,6 +325,7 @@ Regions are defined via the `regions` variable which sets up a mapping between t | [dns-dev.tf](./dns-dev.tf) | Development spoke DNS zones and peerings setup. | dns · dns-response-policy | | | [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns · dns-response-policy | | | [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring-vpn-onprem.tf](./monitoring-vpn-onprem.tf) | VPN monitoring alerts. | | google_monitoring_alert_policy | | [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | | [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | | [regions.tf](./regions.tf) | Compute short names for regions. | | | @@ -338,20 +339,21 @@ Regions are defined via the `regions` variable which sets up a mapping between t | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L77) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L87) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L103) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | -| [custom_roles](variables.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [dns](variables.tf#L47) | Onprem DNS resolvers. | map(list(string)) | | {…} | | -| [factories_config](variables.tf#L56) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L97) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L114) | IP ranges used for Private Service Access (e.g. CloudSQL). | object({…}) | | null | | -| [regions](variables.tf#L135) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L145) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_dev_primary_config](variables.tf#L159) | VPN gateway configuration for onprem interconnection from dev in the primary region. | object({…}) | | null | | -| [vpn_onprem_prod_primary_config](variables.tf#L202) | VPN gateway configuration for onprem interconnection from prod in the primary region. | object({…}) | | null | | +| [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L102) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L112) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L128) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | +| [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [dns](variables.tf#L72) | Onprem DNS resolvers. | map(list(string)) | | {…} | | +| [factories_config](variables.tf#L81) | Configuration for network resource factories. | object({…}) | | {…} | | +| [outputs_location](variables.tf#L122) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L139) | IP ranges used for Private Service Access (e.g. CloudSQL). | object({…}) | | null | | +| [regions](variables.tf#L160) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L170) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_dev_primary_config](variables.tf#L184) | VPN gateway configuration for onprem interconnection from dev in the primary region. | object({…}) | | null | | +| [vpn_onprem_prod_primary_config](variables.tf#L227) | VPN gateway configuration for onprem interconnection from prod in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-d-separate-envs/data/dashboards/vpn.json b/fast/stages/2-networking-d-separate-envs/data/dashboards/vpn.json index 4396cc00..4c86a307 100644 --- a/fast/stages/2-networking-d-separate-envs/data/dashboards/vpn.json +++ b/fast/stages/2-networking-d-separate-envs/data/dashboards/vpn.json @@ -1,248 +1,322 @@ { "displayName": "VPN Monitoring", - "gridLayout": { - "columns": "2", - "widgets": [ + "mosaicLayout": { + "columns": 12, + "tiles": [ { - "title": "Number of connections", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Number of connections", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4 }, { - "title": "Tunnel established", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Tunnel established", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 4 }, { - "title": "Cloud VPN Gateway - Received bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "height": 4, + "widget": { + "title": "VPN Tunnel Bandwidth usage", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesQueryLanguage": "fetch vpn_gateway| { metric vpn.googleapis.com/network/sent_bytes_count; metric vpn.googleapis.com/network/received_bytes_count }| align rate (1m)| group_by [metric.tunnel_name]| outer_join 0,0| value val(0) + val(1)| condition val() > 187.5 \"MBy/s\"" + } } + ], + "thresholds": [ + { + "targetAxis": "Y1", + "value": 187500000 + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 8 }, { - "title": "Cloud VPN Gateway - Sent bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Received packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Sent packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 8 }, { - "title": "Incoming packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 8 }, { - "title": "Outgoing packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Incoming packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 12 + }, + { + "height": 4, + "widget": { + "title": "Outgoing packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + "width": 6, + "yPos": 12 } ] } -} \ No newline at end of file +} diff --git a/fast/stages/2-networking-d-separate-envs/monitoring-vpn-onprem.tf b/fast/stages/2-networking-d-separate-envs/monitoring-vpn-onprem.tf new file mode 100644 index 00000000..d8d8ed27 --- /dev/null +++ b/fast/stages/2-networking-d-separate-envs/monitoring-vpn-onprem.tf @@ -0,0 +1,108 @@ +/** + * 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. + */ + +# tfdoc:file:description VPN monitoring alerts. + +# VPN alerts + +locals { + alert_project_id = flatten([ + var.vpn_onprem_dev_primary_config == null ? [] : [module.dev-spoke-project.project_id], + var.vpn_onprem_prod_primary_config == null ? [] : [module.prod-spoke-project.project_id] + ]) +} + +resource "google_monitoring_alert_policy" "vpn_tunnel_established" { + for_each = var.alert_config.vpn_tunnel_established != null ? toset(local.alert_project_id) : [] + + project = each.key + display_name = "VPN Tunnel Established" + enabled = var.alert_config.vpn_tunnel_established.enabled + notification_channels = var.alert_config.vpn_tunnel_established.notification_channels + user_labels = var.alert_config.vpn_tunnel_established.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Established" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| metric vpn.googleapis.com/tunnel_established", + "| group_by 5m, [value_tunnel_established_max: max(value.tunnel_established)]", + "| every 5m", + "| condition val() < 1 '1'", + ]) + + duration = var.alert_config.vpn_tunnel_established.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_established.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_established.auto_close + } + } +} + +# https://cloud.google.com/network-connectivity/docs/vpn/how-to/viewing-logs-metrics#define-bandwidth-alerts +resource "google_monitoring_alert_policy" "vpn_tunnel_bandwidth" { + for_each = var.alert_config.vpn_tunnel_bandwidth != null ? toset(local.alert_project_id) : [] + + project = each.key + display_name = "VPN Tunnel Bandwidth usage" + enabled = var.alert_config.vpn_tunnel_bandwidth.enabled + notification_channels = var.alert_config.vpn_tunnel_bandwidth.notification_channels + user_labels = var.alert_config.vpn_tunnel_bandwidth.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Bandwidth usage" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| { metric vpn.googleapis.com/network/sent_bytes_count", + "; metric vpn.googleapis.com/network/received_bytes_count }", + "| align rate (1m)", + "| group_by [metric.tunnel_name]", + "| outer_join 0,0", + "| value val(0) + val(1)", + "| condition val() > ${var.alert_config.vpn_tunnel_bandwidth.threshold_mbys} \"MBy/s\"", + ]) + + duration = var.alert_config.vpn_tunnel_bandwidth.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_bandwidth.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_bandwidth.auto_close + } + } +} \ No newline at end of file diff --git a/fast/stages/2-networking-d-separate-envs/variables.tf b/fast/stages/2-networking-d-separate-envs/variables.tf index 5ea75c52..52112396 100644 --- a/fast/stages/2-networking-d-separate-envs/variables.tf +++ b/fast/stages/2-networking-d-separate-envs/variables.tf @@ -14,6 +14,31 @@ * limitations under the License. */ +variable "alert_config" { + description = "Configuration for monitoring alerts." + type = object({ + vpn_tunnel_established = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + user_labels = optional(map(string), {}) + })) + vpn_tunnel_bandwidth = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + threshold_mbys = optional(string, "187.5") + user_labels = optional(map(string), {}) + })) + }) + default = { + vpn_tunnel_established = {} + vpn_tunnel_bandwidth = {} + } +} + variable "automation" { # tfdoc:variable:source 0-bootstrap description = "Automation resources created by the bootstrap stage." @@ -240,4 +265,4 @@ variable "vpn_onprem_prod_primary_config" { })) }) default = null -} +} \ No newline at end of file diff --git a/fast/stages/2-networking-e-nva-bgp/README.md b/fast/stages/2-networking-e-nva-bgp/README.md index f13f703d..474644a5 100644 --- a/fast/stages/2-networking-e-nva-bgp/README.md +++ b/fast/stages/2-networking-e-nva-bgp/README.md @@ -486,6 +486,7 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | | [landing.tf](./landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | | [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring-vpn-onprem.tf](./monitoring-vpn-onprem.tf) | VPN monitoring alerts. | | google_monitoring_alert_policy | | [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | | [ncc.tf](./ncc.tf) | None | ncc-spoke-ra | | | [nva.tf](./nva.tf) | None | compute-vm · simple-nva | google_compute_address | @@ -501,24 +502,25 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | -| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L76) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L120) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L136) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | -| [custom_roles](variables.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [dns](variables.tf#L47) | Onprem DNS resolvers. | map(list(string)) | | {…} | | -| [factories_config](variables.tf#L55) | Configuration for network resource factories. | object({…}) | | {…} | | -| [gcp_ranges](variables.tf#L86) | GCP address ranges in name => range format. | map(string) | | {…} | | -| [ncc_asn](variables.tf#L101) | The NCC Cloud Routers ASN configuration. | map(number) | | {…} | | -| [onprem_cidr](variables.tf#L112) | Onprem addresses in name => range format. | map(string) | | {…} | | -| [outputs_location](variables.tf#L130) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L147) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | -| [regions](variables.tf#L168) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L180) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L194) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | -| [vpn_onprem_secondary_config](variables.tf#L237) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | -| [zones](variables.tf#L280) | Zones in which NVAs are deployed. | list(string) | | ["b", "c"] | | +| [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L101) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L145) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L161) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | +| [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [dns](variables.tf#L72) | Onprem DNS resolvers. | map(list(string)) | | {…} | | +| [factories_config](variables.tf#L80) | Configuration for network resource factories. | object({…}) | | {…} | | +| [gcp_ranges](variables.tf#L111) | GCP address ranges in name => range format. | map(string) | | {…} | | +| [ncc_asn](variables.tf#L126) | The NCC Cloud Routers ASN configuration. | map(number) | | {…} | | +| [onprem_cidr](variables.tf#L137) | Onprem addresses in name => range format. | map(string) | | {…} | | +| [outputs_location](variables.tf#L155) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L172) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | +| [regions](variables.tf#L193) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L205) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L219) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_secondary_config](variables.tf#L262) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | +| [zones](variables.tf#L305) | Zones in which NVAs are deployed. | list(string) | | ["b", "c"] | | ## Outputs diff --git a/fast/stages/2-networking-e-nva-bgp/data/dashboards/vpn.json b/fast/stages/2-networking-e-nva-bgp/data/dashboards/vpn.json index 1aec3e45..4c86a307 100644 --- a/fast/stages/2-networking-e-nva-bgp/data/dashboards/vpn.json +++ b/fast/stages/2-networking-e-nva-bgp/data/dashboards/vpn.json @@ -1,247 +1,321 @@ { "displayName": "VPN Monitoring", - "gridLayout": { - "columns": "2", - "widgets": [ + "mosaicLayout": { + "columns": 12, + "tiles": [ { - "title": "Number of connections", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Number of connections", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4 }, { - "title": "Tunnel established", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_MEAN" + "height": 4, + "widget": { + "title": "Tunnel established", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 4 }, { - "title": "Cloud VPN Gateway - Received bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" - }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "height": 4, + "widget": { + "title": "VPN Tunnel Bandwidth usage", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesQueryLanguage": "fetch vpn_gateway| { metric vpn.googleapis.com/network/sent_bytes_count; metric vpn.googleapis.com/network/received_bytes_count }| align rate (1m)| group_by [metric.tunnel_name]| outer_join 0,0| value val(0) + val(1)| condition val() > 187.5 \"MBy/s\"" + } } + ], + "thresholds": [ + { + "targetAxis": "Y1", + "value": 187500000 + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 4, + "xPos": 8 }, { - "title": "Cloud VPN Gateway - Sent bytes", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "By" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Received packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "By" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 4 }, { - "title": "Cloud VPN Gateway - Sent packets", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Received packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "{packets}" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "yPos": 8 }, { - "title": "Incoming packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Cloud VPN Gateway - Sent packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "{packets}" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 8 }, { - "title": "Outgoing packets dropped", - "xyChart": { - "chartOptions": { - "mode": "COLOR" - }, - "dataSets": [ - { - "minAlignmentPeriod": "60s", - "plotType": "LINE", - "targetAxis": "Y1", - "timeSeriesQuery": { - "timeSeriesFilter": { - "aggregation": { - "perSeriesAligner": "ALIGN_RATE" + "height": 4, + "widget": { + "title": "Incoming packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} }, - "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", - "secondaryAggregation": {} - }, - "unitOverride": "1" + "unitOverride": "1" + } } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" } - ], - "timeshiftDuration": "0s", - "yAxis": { - "label": "y1Axis", - "scale": "LINEAR" } - } + }, + "width": 6, + "xPos": 6, + "yPos": 12 + }, + { + "height": 4, + "widget": { + "title": "Outgoing packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + "width": 6, + "yPos": 12 } ] } diff --git a/fast/stages/2-networking-e-nva-bgp/monitoring-vpn-onprem.tf b/fast/stages/2-networking-e-nva-bgp/monitoring-vpn-onprem.tf new file mode 100644 index 00000000..d63c14d8 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/monitoring-vpn-onprem.tf @@ -0,0 +1,99 @@ +/** + * 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. + */ + +# tfdoc:file:description VPN monitoring alerts. + +resource "google_monitoring_alert_policy" "vpn_tunnel_established" { + count = (var.vpn_onprem_primary_config != null && var.alert_config.vpn_tunnel_established != null) ? 1 : 0 + + project = module.landing-project.project_id + display_name = "VPN Tunnel Established" + enabled = var.alert_config.vpn_tunnel_established.enabled + notification_channels = var.alert_config.vpn_tunnel_established.notification_channels + user_labels = var.alert_config.vpn_tunnel_established.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Established" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| metric vpn.googleapis.com/tunnel_established", + "| group_by 5m, [value_tunnel_established_max: max(value.tunnel_established)]", + "| every 5m", + "| condition val() < 1 '1'", + ]) + + duration = var.alert_config.vpn_tunnel_established.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_established.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_established.auto_close + } + } +} + +# https://cloud.google.com/network-connectivity/docs/vpn/how-to/viewing-logs-metrics#define-bandwidth-alerts +resource "google_monitoring_alert_policy" "vpn_tunnel_bandwidth" { + count = (var.vpn_onprem_primary_config != null && var.alert_config.vpn_tunnel_bandwidth != null) ? 1 : 0 + + project = module.landing-project.project_id + display_name = "VPN Tunnel Bandwidth usage" + enabled = var.alert_config.vpn_tunnel_bandwidth.enabled + notification_channels = var.alert_config.vpn_tunnel_bandwidth.notification_channels + user_labels = var.alert_config.vpn_tunnel_bandwidth.user_labels + combiner = "OR" + + conditions { + display_name = "VPN Tunnel Bandwidth usage" + + condition_monitoring_query_language { + query = join("", [ + "fetch vpn_gateway", + "| { metric vpn.googleapis.com/network/sent_bytes_count", + "; metric vpn.googleapis.com/network/received_bytes_count }", + "| align rate (1m)", + "| group_by [metric.tunnel_name]", + "| outer_join 0,0", + "| value val(0) + val(1)", + "| condition val() > ${var.alert_config.vpn_tunnel_bandwidth.threshold_mbys} \"MBy/s\"", + ]) + + duration = var.alert_config.vpn_tunnel_bandwidth.duration + + trigger { + count = "1" + } + } + } + + dynamic "alert_strategy" { + for_each = var.alert_config.vpn_tunnel_bandwidth.auto_close != null ? [1] : [] + + content { + auto_close = var.alert_config.vpn_tunnel_bandwidth.auto_close + } + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/variables.tf b/fast/stages/2-networking-e-nva-bgp/variables.tf index 155a715c..4d4a451c 100644 --- a/fast/stages/2-networking-e-nva-bgp/variables.tf +++ b/fast/stages/2-networking-e-nva-bgp/variables.tf @@ -14,6 +14,31 @@ * limitations under the License. */ +variable "alert_config" { + description = "Configuration for monitoring alerts." + type = object({ + vpn_tunnel_established = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + user_labels = optional(map(string), {}) + })) + vpn_tunnel_bandwidth = optional(object({ + auto_close = optional(string, null) + duration = optional(string, "120s") + enabled = optional(bool, true) + notification_channels = optional(list(string), []) + threshold_mbys = optional(string, "187.5") + user_labels = optional(map(string), {}) + })) + }) + default = { + vpn_tunnel_established = {} + vpn_tunnel_bandwidth = {} + } +} + variable "automation" { # tfdoc:variable:source 0-bootstrap description = "Automation resources created by the bootstrap stage." @@ -281,4 +306,4 @@ variable "zones" { description = "Zones in which NVAs are deployed." type = list(string) default = ["b", "c"] -} +} \ No newline at end of file diff --git a/tests/fast/stages/s2_networking_a_peering/stage.yaml b/tests/fast/stages/s2_networking_a_peering/stage.yaml index e307c614..8858c11b 100644 --- a/tests/fast/stages/s2_networking_a_peering/stage.yaml +++ b/tests/fast/stages/s2_networking_a_peering/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 27 - resources: 139 + resources: 141 diff --git a/tests/fast/stages/s2_networking_b_vpn/stage.yaml b/tests/fast/stages/s2_networking_b_vpn/stage.yaml index ebe3fb48..711c85c7 100644 --- a/tests/fast/stages/s2_networking_b_vpn/stage.yaml +++ b/tests/fast/stages/s2_networking_b_vpn/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 29 - resources: 176 + resources: 178 diff --git a/tests/fast/stages/s2_networking_c_nva/stage.yaml b/tests/fast/stages/s2_networking_c_nva/stage.yaml index bbd6d6ba..c31bb661 100644 --- a/tests/fast/stages/s2_networking_c_nva/stage.yaml +++ b/tests/fast/stages/s2_networking_c_nva/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 41 - resources: 185 + resources: 187 diff --git a/tests/fast/stages/s2_networking_d_separate_envs/stage.yaml b/tests/fast/stages/s2_networking_d_separate_envs/stage.yaml index 0313e359..7df8df8a 100644 --- a/tests/fast/stages/s2_networking_d_separate_envs/stage.yaml +++ b/tests/fast/stages/s2_networking_d_separate_envs/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 20 - resources: 156 + resources: 160 diff --git a/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml b/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml index 10abee57..5af436d8 100644 --- a/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml +++ b/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml @@ -14,4 +14,4 @@ counts: modules: 35 - resources: 198 + resources: 200