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