From 6a8ae06e74c1997e1d6979d611c2e41658cad050 Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Fri, 6 Dec 2024 16:56:49 +0000 Subject: [PATCH 01/33] WIP: Logging Alerts Factory --- fast/stages/0-bootstrap/automation.tf | 2 + fast/stages/0-bootstrap/billing.tf | 6 +- .../logging-alerts/compliance-alerts.yaml | 136 ++++++++++++++++++ fast/stages/0-bootstrap/log-export.tf | 8 +- .../0-bootstrap/terraform.tfvars.sample | 2 + fast/stages/0-bootstrap/variables.tf | 13 +- modules/project/alert-metrics.tf | 87 +++++++++++ modules/project/variables-metrics-alerts.tf | 17 +++ modules/project/variables.tf | 13 +- 9 files changed, 275 insertions(+), 9 deletions(-) create mode 100644 fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml create mode 100644 modules/project/alert-metrics.tf create mode 100644 modules/project/variables-metrics-alerts.tf diff --git a/fast/stages/0-bootstrap/automation.tf b/fast/stages/0-bootstrap/automation.tf index 0b369aacac..ed9d88a2d8 100644 --- a/fast/stages/0-bootstrap/automation.tf +++ b/fast/stages/0-bootstrap/automation.tf @@ -42,7 +42,9 @@ module "automation-project" { org_policies = ( var.bootstrap_user != null ? null : var.factories_config.org_policies_iac ) + logging_metrics_alerts = var.factories_config.logging_metrics_alerts } + default_alerts_email = var.default_alerts_email # human (groups) IAM bindings iam_by_principals = { (local.principals.gcp-devops) = [ diff --git a/fast/stages/0-bootstrap/billing.tf b/fast/stages/0-bootstrap/billing.tf index 77b3050011..f2d869536c 100644 --- a/fast/stages/0-bootstrap/billing.tf +++ b/fast/stages/0-bootstrap/billing.tf @@ -41,7 +41,11 @@ module "billing-export-project" { source = "../../../modules/project" count = local.billing_mode == "org" ? 1 : 0 billing_account = var.billing_account.id - name = "billing-exp-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email + name = "billing-exp-0" parent = coalesce( var.project_parent_ids.billing, "organizations/${var.organization.id}" ) diff --git a/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml b/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml new file mode 100644 index 0000000000..aafd306475 --- /dev/null +++ b/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml @@ -0,0 +1,136 @@ +route-changes: + description: "Monitor VPC network route configuration changes inside GCP projects" + filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + display_name: "Network Route Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +network-firewall-config-changes: + description: "Monitor VPC network firewall configuration changes inside GCP projects" + filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" + display_name: "VPC Network Firewall Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +vpc-network-config-changes: + description: "Monitor VPC network configuration changes inside GCP projects" + filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + display_name: "VPC Network Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudsql-changes: + description: "Monitor Cloud SQL configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + display_name: "CloudSQL Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudstorage-changes: + description: "Monitor Cloud Storage configuration changes inside GCP projects" + filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + display_name: "CloudStorage IAM Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: "gcs_bucket" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +customrole-changes: + description: "Monitor IAM Custom Role configuration changes inside GCP projects" + filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + display_name: "IAM Custom Role Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +audit-changes: + description: "Monitor Audit configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + display_name: "Audit Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +iam-owner-changes: + description: "Monitor IAM Owner configuration changes inside GCP projects" + filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + display_name: "Owner IAM Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_DELTA + cross_series_reducer: REDUCE_SUM + alignment_period: "600s" diff --git a/fast/stages/0-bootstrap/log-export.tf b/fast/stages/0-bootstrap/log-export.tf index ccecd693e9..9a6f83467e 100644 --- a/fast/stages/0-bootstrap/log-export.tf +++ b/fast/stages/0-bootstrap/log-export.tf @@ -42,8 +42,12 @@ module "log-export-project" { parent = coalesce( var.project_parent_ids.logging, "organizations/${var.organization.id}" ) - prefix = local.prefix - billing_account = var.billing_account.id + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email + prefix = local.prefix + billing_account = var.billing_account.id contacts = ( var.bootstrap_user != null || var.essential_contacts == null ? {} diff --git a/fast/stages/0-bootstrap/terraform.tfvars.sample b/fast/stages/0-bootstrap/terraform.tfvars.sample index 564e8113fb..a396c1a0d3 100644 --- a/fast/stages/0-bootstrap/terraform.tfvars.sample +++ b/fast/stages/0-bootstrap/terraform.tfvars.sample @@ -23,3 +23,5 @@ outputs_location = "~/fast-config" # use something unique and no longer than 9 characters prefix = "abcd" + +default_alerts_email = "support@company.com" diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index b9b30587d3..5b49a99172 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -133,9 +133,10 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for the resource factories or external data." type = object({ - custom_roles = optional(string, "data/custom-roles") - org_policies = optional(string, "data/org-policies") - org_policies_iac = optional(string, "data/org-policies-iac") + custom_roles = optional(string, "data/custom-roles") + org_policies = optional(string, "data/org-policies") + org_policies_iac = optional(string, "data/org-policies-iac") + logging_metrics_alerts = optional(string, "data/logging-alerts") }) nullable = false default = {} @@ -342,3 +343,9 @@ variable "workload_identity_providers" { # error_message = "Custom settings cannot be null." # } } + +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + nullable = false +} diff --git a/modules/project/alert-metrics.tf b/modules/project/alert-metrics.tf new file mode 100644 index 0000000000..b69a4eef72 --- /dev/null +++ b/modules/project/alert-metrics.tf @@ -0,0 +1,87 @@ +/** + * Copyright 2024 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. + */ + +locals { + _logging_metrics_alerts_factory_data_raw = merge([ + for f in try(fileset(var.factories_config.logging_metrics_alerts, "*.yaml"), []) : + yamldecode(file("${var.factories_config.logging_metrics_alerts}/${f}")) + ]...) + _logging_metrics_alerts_factory_data = { + for k, v in local._logging_metrics_alerts_factory_data_raw : + k => merge({ + name = k + description = null + filter = null + display_name = null + metric_descriptor = {} + condition_threshold = {} + trigger_count = null + aggregations = {} + combiner = null + notification_channels = [] + }, v) + } + logging_metrics_alerts = merge(local._logging_metrics_alerts_factory_data, var.logging_metrics_alerts) +} + +resource "google_monitoring_notification_channel" "default" { + project = local.project.project_id + display_name = "Default Email Notification" + type = "email" + labels = { + email_address = try(var.default_alerts_email) + } +} + +resource "google_logging_metric" "default" { + for_each = local.logging_metrics_alerts + project = local.project.project_id + filter = each.value.filter + name = each.value.name + description = each.value.description + metric_descriptor { + metric_kind = each.value.metric_descriptor.metric_kind + value_type = each.value.metric_descriptor.value_type + } +} + +resource "google_monitoring_alert_policy" "default" { + for_each = local.logging_metrics_alerts + project = local.project.project_id + combiner = each.value.combiner + display_name = each.value.display_name + conditions { + display_name = each.value.display_name + condition_threshold { + comparison = each.value.condition_threshold.comparison + duration = each.value.condition_threshold.duration + filter = try("resource.type = \"${each.value.condition_threshold.resource_type}\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.default[each.value.name].name}\"", "resource.type = \"global\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.default[each.value.name].name}\"") + trigger { + count = each.value.trigger_count + } + aggregations { + per_series_aligner = each.value.aggregations.per_series_aligner + cross_series_reducer = each.value.aggregations.cross_series_reducer + alignment_period = each.value.aggregations.alignment_period + } + } + } + notification_channels = try(split(",", google_monitoring_notification_channel.default.id), + each.value.notification_channels) + alert_strategy { + auto_close = "604800s" + } +} diff --git a/modules/project/variables-metrics-alerts.tf b/modules/project/variables-metrics-alerts.tf new file mode 100644 index 0000000000..0da9c1b849 --- /dev/null +++ b/modules/project/variables-metrics-alerts.tf @@ -0,0 +1,17 @@ +variable "logging_metrics_alerts" { + description = "Logging metrics alerts configuration." + type = map(object({ + name = optional(string) + description = optional(string) + filter = optional(string) + display_name = optional(string) + metric_descriptor = optional(map(string)) + condition_threshold = optional(map(string)) + trigger_count = optional(number) + aggregations = optional(map(string)) + combiner = optional(string) + notification_channels = optional(list(string)) + })) + nullable = false + default = {} +} diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 88f59763a0..a8cefa9365 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -81,9 +81,10 @@ variable "descriptive_name" { variable "factories_config" { description = "Paths to data files and folders that enable factory functionality." type = object({ - custom_roles = optional(string) - org_policies = optional(string) - quotas = optional(string) + custom_roles = optional(string) + org_policies = optional(string) + quotas = optional(string) + logging_metrics_alerts = optional(string) }) nullable = false default = {} @@ -314,3 +315,9 @@ variable "vpc_sc" { }) default = null } + +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + default = null +} From 2586c541153f7e61a8882f2b2d25aca2f441bd5a Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Mon, 9 Dec 2024 11:11:36 +0000 Subject: [PATCH 02/33] Implement Logging Alerts on Remaining Modules --- fast/stages/0-bootstrap/outputs.tf | 1 + fast/stages/0-bootstrap/variables.tf | 12 +- .../logging-alerts/compliance-alerts.yaml | 136 ++++++++++++++++++ fast/stages/1-resman/tenant-root.tf | 4 + fast/stages/1-resman/variables-fast.tf | 6 + fast/stages/1-resman/variables.tf | 7 +- .../logging-alerts/compliance-alerts.yaml | 136 ++++++++++++++++++ fast/stages/2-network-security/main.tf | 4 + .../2-network-security/variables-fast.tf | 6 + fast/stages/2-network-security/variables.tf | 1 + .../logging-alerts/compliance-alerts.yaml | 136 ++++++++++++++++++ fast/stages/2-networking-a-simple/net-dev.tf | 4 + .../2-networking-a-simple/net-landing.tf | 4 + fast/stages/2-networking-a-simple/net-prod.tf | 4 + .../2-networking-a-simple/variables-fast.tf | 6 + .../stages/2-networking-a-simple/variables.tf | 7 +- .../logging-alerts/compliance-alerts.yaml | 136 ++++++++++++++++++ fast/stages/2-networking-b-nva/net-dev.tf | 4 + fast/stages/2-networking-b-nva/net-landing.tf | 4 + fast/stages/2-networking-b-nva/net-prod.tf | 4 + .../2-networking-b-nva/variables-fast.tf | 6 + fast/stages/2-networking-b-nva/variables.tf | 7 +- .../logging-alerts/compliance-alerts.yaml | 136 ++++++++++++++++++ .../2-networking-c-separate-envs/net-dev.tf | 4 + .../2-networking-c-separate-envs/net-prod.tf | 4 + .../variables-fast.tf | 6 + .../2-networking-c-separate-envs/variables.tf | 7 +- .../logging-alerts/compliance-alerts.yaml | 136 ++++++++++++++++++ fast/stages/2-project-factory/main.tf | 4 +- .../2-project-factory/variables-fast.tf | 6 + fast/stages/2-project-factory/variables.tf | 5 +- fast/stages/2-security/core-dev.tf | 4 + fast/stages/2-security/core-prod.tf | 4 + .../logging-alerts/compliance-alerts.yaml | 136 ++++++++++++++++++ fast/stages/2-security/variables-fast.tf | 6 + fast/stages/2-security/variables.tf | 8 ++ modules/project-factory/main.tf | 10 +- modules/project-factory/variables.tf | 11 +- modules/project/variables.tf | 12 +- 39 files changed, 1102 insertions(+), 32 deletions(-) create mode 100644 fast/stages/1-resman/data/logging-alerts/compliance-alerts.yaml create mode 100644 fast/stages/2-network-security/data/logging-alerts/compliance-alerts.yaml create mode 100644 fast/stages/2-networking-a-simple/data/logging-alerts/compliance-alerts.yaml create mode 100644 fast/stages/2-networking-b-nva/data/logging-alerts/compliance-alerts.yaml create mode 100644 fast/stages/2-networking-c-separate-envs/data/logging-alerts/compliance-alerts.yaml create mode 100644 fast/stages/2-project-factory/data/logging-alerts/compliance-alerts.yaml create mode 100644 fast/stages/2-security/data/logging-alerts/compliance-alerts.yaml diff --git a/fast/stages/0-bootstrap/outputs.tf b/fast/stages/0-bootstrap/outputs.tf index 29bf4a89f5..262131f1ea 100644 --- a/fast/stages/0-bootstrap/outputs.tf +++ b/fast/stages/0-bootstrap/outputs.tf @@ -109,6 +109,7 @@ locals { vpcsc = module.automation-tf-vpcsc-sa.email vpcsc-r = module.automation-tf-vpcsc-r-sa.email } + default_alerts_email = var.default_alerts_email } custom_roles = module.organization.custom_role_id logging = { diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index 5b49a99172..6471a30bf1 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -91,6 +91,12 @@ variable "custom_roles" { default = {} } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + nullable = false +} + variable "environments" { description = "Environment names." type = map(object({ @@ -343,9 +349,3 @@ variable "workload_identity_providers" { # error_message = "Custom settings cannot be null." # } } - -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - nullable = false -} diff --git a/fast/stages/1-resman/data/logging-alerts/compliance-alerts.yaml b/fast/stages/1-resman/data/logging-alerts/compliance-alerts.yaml new file mode 100644 index 0000000000..aafd306475 --- /dev/null +++ b/fast/stages/1-resman/data/logging-alerts/compliance-alerts.yaml @@ -0,0 +1,136 @@ +route-changes: + description: "Monitor VPC network route configuration changes inside GCP projects" + filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + display_name: "Network Route Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +network-firewall-config-changes: + description: "Monitor VPC network firewall configuration changes inside GCP projects" + filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" + display_name: "VPC Network Firewall Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +vpc-network-config-changes: + description: "Monitor VPC network configuration changes inside GCP projects" + filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + display_name: "VPC Network Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudsql-changes: + description: "Monitor Cloud SQL configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + display_name: "CloudSQL Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudstorage-changes: + description: "Monitor Cloud Storage configuration changes inside GCP projects" + filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + display_name: "CloudStorage IAM Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: "gcs_bucket" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +customrole-changes: + description: "Monitor IAM Custom Role configuration changes inside GCP projects" + filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + display_name: "IAM Custom Role Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +audit-changes: + description: "Monitor Audit configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + display_name: "Audit Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +iam-owner-changes: + description: "Monitor IAM Owner configuration changes inside GCP projects" + filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + display_name: "Owner IAM Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_DELTA + cross_series_reducer: REDUCE_SUM + alignment_period: "600s" diff --git a/fast/stages/1-resman/tenant-root.tf b/fast/stages/1-resman/tenant-root.tf index e5d79c717c..771907d164 100644 --- a/fast/stages/1-resman/tenant-root.tf +++ b/fast/stages/1-resman/tenant-root.tf @@ -38,6 +38,10 @@ module "automation-project" { project_create = false # do not assign tagViewer or tagUser roles here on tag keys and values as # they are managed authoritatively and will break multitenant stages + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email tags = merge(local.tags, { (var.tag_names.context) = { description = "Resource management context." diff --git a/fast/stages/1-resman/variables-fast.tf b/fast/stages/1-resman/variables-fast.tf index a35f5eca40..2501ce33dd 100644 --- a/fast/stages/1-resman/variables-fast.tf +++ b/fast/stages/1-resman/variables-fast.tf @@ -68,6 +68,12 @@ variable "custom_roles" { default = null } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + nullable = false +} + variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/1-resman/variables.tf b/fast/stages/1-resman/variables.tf index 6443f02dc8..faa8a8f3ae 100644 --- a/fast/stages/1-resman/variables.tf +++ b/fast/stages/1-resman/variables.tf @@ -20,9 +20,10 @@ variable "factories_config" { description = "Configuration for the resource factories or external data." type = object({ - org_policies = optional(string, "data/org-policies") - stage_3 = optional(string, "data/stage-3") - top_level_folders = optional(string, "data/top-level-folders") + org_policies = optional(string, "data/org-policies") + stage_3 = optional(string, "data/stage-3") + top_level_folders = optional(string, "data/top-level-folders") + logging_metrics_alerts = optional(string, "data/logging-alerts") }) nullable = false default = {} diff --git a/fast/stages/2-network-security/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-network-security/data/logging-alerts/compliance-alerts.yaml new file mode 100644 index 0000000000..aafd306475 --- /dev/null +++ b/fast/stages/2-network-security/data/logging-alerts/compliance-alerts.yaml @@ -0,0 +1,136 @@ +route-changes: + description: "Monitor VPC network route configuration changes inside GCP projects" + filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + display_name: "Network Route Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +network-firewall-config-changes: + description: "Monitor VPC network firewall configuration changes inside GCP projects" + filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" + display_name: "VPC Network Firewall Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +vpc-network-config-changes: + description: "Monitor VPC network configuration changes inside GCP projects" + filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + display_name: "VPC Network Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudsql-changes: + description: "Monitor Cloud SQL configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + display_name: "CloudSQL Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudstorage-changes: + description: "Monitor Cloud Storage configuration changes inside GCP projects" + filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + display_name: "CloudStorage IAM Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: "gcs_bucket" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +customrole-changes: + description: "Monitor IAM Custom Role configuration changes inside GCP projects" + filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + display_name: "IAM Custom Role Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +audit-changes: + description: "Monitor Audit configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + display_name: "Audit Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +iam-owner-changes: + description: "Monitor IAM Owner configuration changes inside GCP projects" + filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + display_name: "Owner IAM Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_DELTA + cross_series_reducer: REDUCE_SUM + alignment_period: "600s" diff --git a/fast/stages/2-network-security/main.tf b/fast/stages/2-network-security/main.tf index 946dba43f3..1d2ffc370f 100644 --- a/fast/stages/2-network-security/main.tf +++ b/fast/stages/2-network-security/main.tf @@ -36,6 +36,10 @@ module "ngfw-quota-project" { ? "net-ngfw-0" : var.ngfw_enterprise_config.quota_project_id ) + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email billing_account = ( local.create_quota_project ? var.billing_account.id diff --git a/fast/stages/2-network-security/variables-fast.tf b/fast/stages/2-network-security/variables-fast.tf index 45fb1c0fbd..104b3ebf7b 100644 --- a/fast/stages/2-network-security/variables-fast.tf +++ b/fast/stages/2-network-security/variables-fast.tf @@ -37,6 +37,12 @@ variable "billing_account" { } } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + nullable = false +} + variable "folder_ids" { # tfdoc:variable:source 1-resman description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." diff --git a/fast/stages/2-network-security/variables.tf b/fast/stages/2-network-security/variables.tf index 94f00b6fed..0fc1750d02 100644 --- a/fast/stages/2-network-security/variables.tf +++ b/fast/stages/2-network-security/variables.tf @@ -22,6 +22,7 @@ variable "factories_config" { dev = string prod = string })) + logging_metrics_alerts = optional(string, "data/logging-alerts") }) nullable = false default = { diff --git a/fast/stages/2-networking-a-simple/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-networking-a-simple/data/logging-alerts/compliance-alerts.yaml new file mode 100644 index 0000000000..aafd306475 --- /dev/null +++ b/fast/stages/2-networking-a-simple/data/logging-alerts/compliance-alerts.yaml @@ -0,0 +1,136 @@ +route-changes: + description: "Monitor VPC network route configuration changes inside GCP projects" + filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + display_name: "Network Route Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +network-firewall-config-changes: + description: "Monitor VPC network firewall configuration changes inside GCP projects" + filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" + display_name: "VPC Network Firewall Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +vpc-network-config-changes: + description: "Monitor VPC network configuration changes inside GCP projects" + filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + display_name: "VPC Network Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudsql-changes: + description: "Monitor Cloud SQL configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + display_name: "CloudSQL Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudstorage-changes: + description: "Monitor Cloud Storage configuration changes inside GCP projects" + filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + display_name: "CloudStorage IAM Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: "gcs_bucket" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +customrole-changes: + description: "Monitor IAM Custom Role configuration changes inside GCP projects" + filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + display_name: "IAM Custom Role Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +audit-changes: + description: "Monitor Audit configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + display_name: "Audit Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +iam-owner-changes: + description: "Monitor IAM Owner configuration changes inside GCP projects" + filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + display_name: "Owner IAM Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_DELTA + cross_series_reducer: REDUCE_SUM + alignment_period: "600s" diff --git a/fast/stages/2-networking-a-simple/net-dev.tf b/fast/stages/2-networking-a-simple/net-dev.tf index ad14b36224..1015415f0c 100644 --- a/fast/stages/2-networking-a-simple/net-dev.tf +++ b/fast/stages/2-networking-a-simple/net-dev.tf @@ -20,6 +20,10 @@ module "dev-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "dev-net-spoke-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.networking-dev, var.folder_ids.networking diff --git a/fast/stages/2-networking-a-simple/net-landing.tf b/fast/stages/2-networking-a-simple/net-landing.tf index 67e1c9d920..6bc96976bc 100644 --- a/fast/stages/2-networking-a-simple/net-landing.tf +++ b/fast/stages/2-networking-a-simple/net-landing.tf @@ -20,6 +20,10 @@ module "landing-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-landing-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.networking-prod, var.folder_ids.networking diff --git a/fast/stages/2-networking-a-simple/net-prod.tf b/fast/stages/2-networking-a-simple/net-prod.tf index cfef3f425c..c6d4f77605 100644 --- a/fast/stages/2-networking-a-simple/net-prod.tf +++ b/fast/stages/2-networking-a-simple/net-prod.tf @@ -20,6 +20,10 @@ module "prod-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-spoke-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.networking-prod, var.folder_ids.networking diff --git a/fast/stages/2-networking-a-simple/variables-fast.tf b/fast/stages/2-networking-a-simple/variables-fast.tf index dd7f96bf58..dcb51d8ffb 100644 --- a/fast/stages/2-networking-a-simple/variables-fast.tf +++ b/fast/stages/2-networking-a-simple/variables-fast.tf @@ -46,6 +46,12 @@ variable "custom_roles" { default = null } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + nullable = false +} + variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/2-networking-a-simple/variables.tf b/fast/stages/2-networking-a-simple/variables.tf index d04ad9a045..f677f1637b 100644 --- a/fast/stages/2-networking-a-simple/variables.tf +++ b/fast/stages/2-networking-a-simple/variables.tf @@ -71,9 +71,10 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - data_dir = optional(string, "data") - dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") - firewall_policy_name = optional(string, "net-default") + data_dir = optional(string, "data") + dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") + firewall_policy_name = optional(string, "net-default") + logging_metrics_alerts = optional(string, "data/logging-alerts") }) default = { data_dir = "data" diff --git a/fast/stages/2-networking-b-nva/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-networking-b-nva/data/logging-alerts/compliance-alerts.yaml new file mode 100644 index 0000000000..aafd306475 --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/logging-alerts/compliance-alerts.yaml @@ -0,0 +1,136 @@ +route-changes: + description: "Monitor VPC network route configuration changes inside GCP projects" + filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + display_name: "Network Route Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +network-firewall-config-changes: + description: "Monitor VPC network firewall configuration changes inside GCP projects" + filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" + display_name: "VPC Network Firewall Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +vpc-network-config-changes: + description: "Monitor VPC network configuration changes inside GCP projects" + filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + display_name: "VPC Network Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudsql-changes: + description: "Monitor Cloud SQL configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + display_name: "CloudSQL Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudstorage-changes: + description: "Monitor Cloud Storage configuration changes inside GCP projects" + filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + display_name: "CloudStorage IAM Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: "gcs_bucket" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +customrole-changes: + description: "Monitor IAM Custom Role configuration changes inside GCP projects" + filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + display_name: "IAM Custom Role Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +audit-changes: + description: "Monitor Audit configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + display_name: "Audit Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +iam-owner-changes: + description: "Monitor IAM Owner configuration changes inside GCP projects" + filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + display_name: "Owner IAM Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_DELTA + cross_series_reducer: REDUCE_SUM + alignment_period: "600s" diff --git a/fast/stages/2-networking-b-nva/net-dev.tf b/fast/stages/2-networking-b-nva/net-dev.tf index eec1d55861..50b8a89530 100644 --- a/fast/stages/2-networking-b-nva/net-dev.tf +++ b/fast/stages/2-networking-b-nva/net-dev.tf @@ -20,6 +20,10 @@ module "dev-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "dev-net-spoke-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.networking-dev, var.folder_ids.networking diff --git a/fast/stages/2-networking-b-nva/net-landing.tf b/fast/stages/2-networking-b-nva/net-landing.tf index 5bd65e0b8d..05df1fe529 100644 --- a/fast/stages/2-networking-b-nva/net-landing.tf +++ b/fast/stages/2-networking-b-nva/net-landing.tf @@ -20,6 +20,10 @@ module "landing-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-landing-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.networking-prod, var.folder_ids.networking diff --git a/fast/stages/2-networking-b-nva/net-prod.tf b/fast/stages/2-networking-b-nva/net-prod.tf index bb59e58a4d..d4d0b87b8f 100644 --- a/fast/stages/2-networking-b-nva/net-prod.tf +++ b/fast/stages/2-networking-b-nva/net-prod.tf @@ -20,6 +20,10 @@ module "prod-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-spoke-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.networking-prod, var.folder_ids.networking diff --git a/fast/stages/2-networking-b-nva/variables-fast.tf b/fast/stages/2-networking-b-nva/variables-fast.tf index dd7f96bf58..dcb51d8ffb 100644 --- a/fast/stages/2-networking-b-nva/variables-fast.tf +++ b/fast/stages/2-networking-b-nva/variables-fast.tf @@ -46,6 +46,12 @@ variable "custom_roles" { default = null } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + nullable = false +} + variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/2-networking-b-nva/variables.tf b/fast/stages/2-networking-b-nva/variables.tf index cc8e8065d9..32a313b6c6 100644 --- a/fast/stages/2-networking-b-nva/variables.tf +++ b/fast/stages/2-networking-b-nva/variables.tf @@ -71,9 +71,10 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - data_dir = optional(string, "data") - dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") - firewall_policy_name = optional(string, "net-default") + data_dir = optional(string, "data") + dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") + firewall_policy_name = optional(string, "net-default") + logging_metrics_alerts = optional(string, "data/logging-alerts") }) default = { data_dir = "data" diff --git a/fast/stages/2-networking-c-separate-envs/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-networking-c-separate-envs/data/logging-alerts/compliance-alerts.yaml new file mode 100644 index 0000000000..aafd306475 --- /dev/null +++ b/fast/stages/2-networking-c-separate-envs/data/logging-alerts/compliance-alerts.yaml @@ -0,0 +1,136 @@ +route-changes: + description: "Monitor VPC network route configuration changes inside GCP projects" + filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + display_name: "Network Route Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +network-firewall-config-changes: + description: "Monitor VPC network firewall configuration changes inside GCP projects" + filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" + display_name: "VPC Network Firewall Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +vpc-network-config-changes: + description: "Monitor VPC network configuration changes inside GCP projects" + filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + display_name: "VPC Network Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudsql-changes: + description: "Monitor Cloud SQL configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + display_name: "CloudSQL Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudstorage-changes: + description: "Monitor Cloud Storage configuration changes inside GCP projects" + filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + display_name: "CloudStorage IAM Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: "gcs_bucket" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +customrole-changes: + description: "Monitor IAM Custom Role configuration changes inside GCP projects" + filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + display_name: "IAM Custom Role Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +audit-changes: + description: "Monitor Audit configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + display_name: "Audit Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +iam-owner-changes: + description: "Monitor IAM Owner configuration changes inside GCP projects" + filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + display_name: "Owner IAM Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_DELTA + cross_series_reducer: REDUCE_SUM + alignment_period: "600s" diff --git a/fast/stages/2-networking-c-separate-envs/net-dev.tf b/fast/stages/2-networking-c-separate-envs/net-dev.tf index 185db01ff3..58d56c7b05 100644 --- a/fast/stages/2-networking-c-separate-envs/net-dev.tf +++ b/fast/stages/2-networking-c-separate-envs/net-dev.tf @@ -20,6 +20,10 @@ module "dev-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "dev-net-spoke-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.networking-dev, var.folder_ids.networking diff --git a/fast/stages/2-networking-c-separate-envs/net-prod.tf b/fast/stages/2-networking-c-separate-envs/net-prod.tf index f25c9f5560..a501e435e7 100644 --- a/fast/stages/2-networking-c-separate-envs/net-prod.tf +++ b/fast/stages/2-networking-c-separate-envs/net-prod.tf @@ -20,6 +20,10 @@ module "prod-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-spoke-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.networking-prod, var.folder_ids.networking diff --git a/fast/stages/2-networking-c-separate-envs/variables-fast.tf b/fast/stages/2-networking-c-separate-envs/variables-fast.tf index dd7f96bf58..dcb51d8ffb 100644 --- a/fast/stages/2-networking-c-separate-envs/variables-fast.tf +++ b/fast/stages/2-networking-c-separate-envs/variables-fast.tf @@ -46,6 +46,12 @@ variable "custom_roles" { default = null } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + nullable = false +} + variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/2-networking-c-separate-envs/variables.tf b/fast/stages/2-networking-c-separate-envs/variables.tf index aabaab2d98..8463cf737c 100644 --- a/fast/stages/2-networking-c-separate-envs/variables.tf +++ b/fast/stages/2-networking-c-separate-envs/variables.tf @@ -66,9 +66,10 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - data_dir = optional(string, "data") - dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") - firewall_policy_name = optional(string, "net-default") + data_dir = optional(string, "data") + dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") + firewall_policy_name = optional(string, "net-default") + logging_metrics_alerts = optional(string, "data/logging-alerts") }) default = { data_dir = "data" diff --git a/fast/stages/2-project-factory/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-project-factory/data/logging-alerts/compliance-alerts.yaml new file mode 100644 index 0000000000..aafd306475 --- /dev/null +++ b/fast/stages/2-project-factory/data/logging-alerts/compliance-alerts.yaml @@ -0,0 +1,136 @@ +route-changes: + description: "Monitor VPC network route configuration changes inside GCP projects" + filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + display_name: "Network Route Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +network-firewall-config-changes: + description: "Monitor VPC network firewall configuration changes inside GCP projects" + filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" + display_name: "VPC Network Firewall Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +vpc-network-config-changes: + description: "Monitor VPC network configuration changes inside GCP projects" + filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + display_name: "VPC Network Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudsql-changes: + description: "Monitor Cloud SQL configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + display_name: "CloudSQL Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudstorage-changes: + description: "Monitor Cloud Storage configuration changes inside GCP projects" + filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + display_name: "CloudStorage IAM Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: "gcs_bucket" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +customrole-changes: + description: "Monitor IAM Custom Role configuration changes inside GCP projects" + filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + display_name: "IAM Custom Role Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +audit-changes: + description: "Monitor Audit configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + display_name: "Audit Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +iam-owner-changes: + description: "Monitor IAM Owner configuration changes inside GCP projects" + filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + display_name: "Owner IAM Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_DELTA + cross_series_reducer: REDUCE_SUM + alignment_period: "600s" diff --git a/fast/stages/2-project-factory/main.tf b/fast/stages/2-project-factory/main.tf index 12fd9f2bda..8fbd6cc615 100644 --- a/fast/stages/2-project-factory/main.tf +++ b/fast/stages/2-project-factory/main.tf @@ -17,7 +17,8 @@ # tfdoc:file:description Project factory. module "projects" { - source = "../../../modules/project-factory" + source = "../../../modules/project-factory" + default_alerts_email = var.default_alerts_email data_defaults = { # more defaults are available, check the project factory variables billing_account = var.billing_account.id @@ -32,6 +33,7 @@ module "projects" { prefix = var.prefix } factories_config = merge(var.factories_config, { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts context = { folder_ids = merge( { for k, v in var.folder_ids : k => v if v != null }, diff --git a/fast/stages/2-project-factory/variables-fast.tf b/fast/stages/2-project-factory/variables-fast.tf index 39aac8e468..97bd5fd8be 100644 --- a/fast/stages/2-project-factory/variables-fast.tf +++ b/fast/stages/2-project-factory/variables-fast.tf @@ -27,6 +27,12 @@ variable "billing_account" { } } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + nullable = false +} + variable "folder_ids" { # tfdoc:variable:source 1-resman description = "Folders created in the resource management stage." diff --git a/fast/stages/2-project-factory/variables.tf b/fast/stages/2-project-factory/variables.tf index a0bc5d7fdf..9e49f08673 100644 --- a/fast/stages/2-project-factory/variables.tf +++ b/fast/stages/2-project-factory/variables.tf @@ -17,8 +17,9 @@ variable "factories_config" { description = "Configuration for YAML-based factories." type = object({ - folders_data_path = optional(string, "data/hierarchy") - projects_data_path = optional(string, "data/projects") + folders_data_path = optional(string, "data/hierarchy") + projects_data_path = optional(string, "data/projects") + logging_metrics_alerts = optional(string, "data/logging-alerts") budgets = optional(object({ billing_account = string budgets_data_path = optional(string, "data/budgets") diff --git a/fast/stages/2-security/core-dev.tf b/fast/stages/2-security/core-dev.tf index 2c089db122..3c3392c6db 100644 --- a/fast/stages/2-security/core-dev.tf +++ b/fast/stages/2-security/core-dev.tf @@ -24,6 +24,10 @@ locals { module "dev-sec-project" { source = "../../../modules/project" name = "dev-sec-core-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.security-dev, var.folder_ids.security ) diff --git a/fast/stages/2-security/core-prod.tf b/fast/stages/2-security/core-prod.tf index b74a5376a2..9c3f692387 100644 --- a/fast/stages/2-security/core-prod.tf +++ b/fast/stages/2-security/core-prod.tf @@ -24,6 +24,10 @@ locals { module "prod-sec-project" { source = "../../../modules/project" name = "prod-sec-core-0" + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.security-prod, var.folder_ids.security ) diff --git a/fast/stages/2-security/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-security/data/logging-alerts/compliance-alerts.yaml new file mode 100644 index 0000000000..aafd306475 --- /dev/null +++ b/fast/stages/2-security/data/logging-alerts/compliance-alerts.yaml @@ -0,0 +1,136 @@ +route-changes: + description: "Monitor VPC network route configuration changes inside GCP projects" + filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + display_name: "Network Route Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +network-firewall-config-changes: + description: "Monitor VPC network firewall configuration changes inside GCP projects" + filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" + display_name: "VPC Network Firewall Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +vpc-network-config-changes: + description: "Monitor VPC network configuration changes inside GCP projects" + filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + display_name: "VPC Network Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudsql-changes: + description: "Monitor Cloud SQL configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + display_name: "CloudSQL Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +cloudstorage-changes: + description: "Monitor Cloud Storage configuration changes inside GCP projects" + filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + display_name: "CloudStorage IAM Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: "gcs_bucket" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +customrole-changes: + description: "Monitor IAM Custom Role configuration changes inside GCP projects" + filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + display_name: "IAM Custom Role Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +audit-changes: + description: "Monitor Audit configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + display_name: "Audit Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + +iam-owner-changes: + description: "Monitor IAM Owner configuration changes inside GCP projects" + filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + display_name: "Owner IAM Configuration Changes" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + trigger_count: 1 + combiner: OR + aggregations: + per_series_aligner: ALIGN_DELTA + cross_series_reducer: REDUCE_SUM + alignment_period: "600s" diff --git a/fast/stages/2-security/variables-fast.tf b/fast/stages/2-security/variables-fast.tf index b8a664e52d..097ff492b0 100644 --- a/fast/stages/2-security/variables-fast.tf +++ b/fast/stages/2-security/variables-fast.tf @@ -44,6 +44,12 @@ variable "custom_roles" { default = null } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + nullable = false +} + variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/2-security/variables.tf b/fast/stages/2-security/variables.tf index b13967c0dd..24377700d9 100644 --- a/fast/stages/2-security/variables.tf +++ b/fast/stages/2-security/variables.tf @@ -181,6 +181,14 @@ variable "essential_contacts" { default = null } +variable "factories_config" { + description = "Configuration for network resource factories." + type = object({ + logging_metrics_alerts = optional(string, "data/logging-alerts") + }) + nullable = false +} + variable "kms_keys" { description = "KMS keys to create, keyed by name." type = map(object({ diff --git a/modules/project-factory/main.tf b/modules/project-factory/main.tf index c806a75939..61aba640d3 100644 --- a/modules/project-factory/main.tf +++ b/modules/project-factory/main.tf @@ -40,9 +40,13 @@ module "projects" { parent = lookup( local.context.folder_ids, each.value.parent, each.value.parent ) - prefix = each.value.prefix - auto_create_network = try(each.value.auto_create_network, false) - compute_metadata = try(each.value.compute_metadata, {}) + factories_config = { + logging_metrics_alerts = var.factories_config.logging_metrics_alerts + } + default_alerts_email = "" + prefix = each.value.prefix + auto_create_network = try(each.value.auto_create_network, false) + compute_metadata = try(each.value.compute_metadata, {}) # TODO: concat lists for each key contacts = merge( each.value.contacts, var.data_merges.contacts diff --git a/modules/project-factory/variables.tf b/modules/project-factory/variables.tf index bd364e1b47..3296ef128d 100644 --- a/modules/project-factory/variables.tf +++ b/modules/project-factory/variables.tf @@ -97,11 +97,18 @@ variable "data_overrides" { default = {} } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + default = null +} + variable "factories_config" { description = "Path to folder with YAML resource description data files." type = object({ - folders_data_path = optional(string) - projects_data_path = optional(string) + folders_data_path = optional(string) + projects_data_path = optional(string) + logging_metrics_alerts = optional(string) budgets = optional(object({ billing_account = string budgets_data_path = string diff --git a/modules/project/variables.tf b/modules/project/variables.tf index a8cefa9365..f49dac6196 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -78,6 +78,12 @@ variable "descriptive_name" { default = null } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + default = null +} + variable "factories_config" { description = "Paths to data files and folders that enable factory functionality." type = object({ @@ -315,9 +321,3 @@ variable "vpc_sc" { }) default = null } - -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} From 5b768a3a5ecad0340dbde53f5095efb9c7084650 Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Mon, 9 Dec 2024 11:27:25 +0000 Subject: [PATCH 03/33] Documentation & FMT --- modules/project-factory/README.md | 3 ++- modules/project/README.md | 44 +++++++++++++++++-------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index 03d47043d0..c2242c3a39 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -440,10 +440,11 @@ update_rules: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [factories_config](variables.tf#L100) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | +| [factories_config](variables.tf#L106) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | | [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | object({…}) | | {} | | [data_merges](variables.tf#L54) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | | [data_overrides](variables.tf#L73) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | +| [default_alerts_email](variables.tf#L100) | Default email address for alerting. | string | | null | ## Outputs diff --git a/modules/project/README.md b/modules/project/README.md index 62bb6fea6d..4db45d1135 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -1366,6 +1366,7 @@ module "bucket" { | name | description | resources | |---|---|---| +| [alert-metrics.tf](./alert-metrics.tf) | None | google_logging_metric · google_monitoring_alert_policy · google_monitoring_notification_channel | | [cmek.tf](./cmek.tf) | Service Agent IAM Bindings for CMEK | google_kms_crypto_key_iam_member | | [iam.tf](./iam.tf) | IAM bindings. | google_project_iam_binding · google_project_iam_custom_role · google_project_iam_member | | [logging.tf](./logging.tf) | Log sinks and supporting resources. | google_bigquery_dataset_iam_member · google_logging_project_exclusion · google_logging_project_sink · google_project_iam_audit_config · google_project_iam_member · google_pubsub_topic_iam_member · google_storage_bucket_iam_member | @@ -1377,6 +1378,7 @@ module "bucket" { | [shared-vpc.tf](./shared-vpc.tf) | Shared VPC project-level configuration. | google_compute_shared_vpc_host_project · google_compute_shared_vpc_service_project · google_compute_subnetwork_iam_member · google_project_iam_member | | [tags.tf](./tags.tf) | None | google_tags_tag_binding · google_tags_tag_key · google_tags_tag_key_iam_binding · google_tags_tag_key_iam_member · google_tags_tag_value · google_tags_tag_value_iam_binding · google_tags_tag_value_iam_member | | [variables-iam.tf](./variables-iam.tf) | None | | +| [variables-metrics-alerts.tf](./variables-metrics-alerts.tf) | None | | | [variables-quotas.tf](./variables-quotas.tf) | None | | | [variables-tags.tf](./variables-tags.tf) | None | | | [variables.tf](./variables.tf) | Module variables. | | @@ -1387,42 +1389,44 @@ module "bucket" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L165) | Project name and id suffix. | string | ✓ | | +| [name](variables.tf#L172) | Project name and id suffix. | string | ✓ | | | [auto_create_network](variables.tf#L17) | Whether to create the default network for the project. | bool | | false | | [billing_account](variables.tf#L23) | Billing account id. | string | | null | | [compute_metadata](variables.tf#L29) | Optional compute metadata key/values. Only usable if compute API has been enabled. | map(string) | | {} | | [contacts](variables.tf#L36) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | | [custom_roles](variables.tf#L43) | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | +| [default_alerts_email](variables.tf#L81) | Default email address for alerting. | string | | null | | [default_service_account](variables.tf#L50) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | string | | "keep" | | [deletion_policy](variables.tf#L64) | Deletion policy setting for this project. | string | | "DELETE" | | [descriptive_name](variables.tf#L75) | Name of the project name. Used for project name instead of `name` variable. | string | | null | -| [factories_config](variables.tf#L81) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | +| [factories_config](variables.tf#L87) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | | [iam](variables-iam.tf#L17) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | | [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | -| [labels](variables.tf#L92) | Resource labels. | map(string) | | {} | -| [lien_reason](variables.tf#L99) | If non-empty, creates a project lien with this description. | string | | null | -| [logging_data_access](variables.tf#L105) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | -| [logging_exclusions](variables.tf#L120) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | -| [logging_sinks](variables.tf#L127) | Logging sinks to create for this project. | map(object({…})) | | {} | -| [metric_scopes](variables.tf#L158) | List of projects that will act as metric scopes for this project. | list(string) | | [] | +| [labels](variables.tf#L99) | Resource labels. | map(string) | | {} | +| [lien_reason](variables.tf#L106) | If non-empty, creates a project lien with this description. | string | | null | +| [logging_data_access](variables.tf#L112) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | +| [logging_exclusions](variables.tf#L127) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | +| [logging_metrics_alerts](variables-metrics-alerts.tf#L1) | Logging metrics alerts configuration. | map(object({…})) | | {} | +| [logging_sinks](variables.tf#L134) | Logging sinks to create for this project. | map(object({…})) | | {} | +| [metric_scopes](variables.tf#L165) | List of projects that will act as metric scopes for this project. | list(string) | | [] | | [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [org_policies](variables.tf#L170) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | -| [parent](variables.tf#L197) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | -| [prefix](variables.tf#L207) | Optional prefix used to generate project id and name. | string | | null | -| [project_create](variables.tf#L217) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | +| [org_policies](variables.tf#L177) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | +| [parent](variables.tf#L204) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +| [prefix](variables.tf#L214) | Optional prefix used to generate project id and name. | string | | null | +| [project_create](variables.tf#L224) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | | [quotas](variables-quotas.tf#L17) | Service quota configuration. | map(object({…})) | | {} | -| [service_agents_config](variables.tf#L223) | Automatic service agent configuration options. | object({…}) | | {} | -| [service_config](variables.tf#L234) | Configure service API activation. | object({…}) | | {…} | -| [service_encryption_key_ids](variables.tf#L246) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | -| [services](variables.tf#L253) | Service APIs to enable. | list(string) | | [] | -| [shared_vpc_host_config](variables.tf#L259) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | -| [shared_vpc_service_config](variables.tf#L268) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | -| [skip_delete](variables.tf#L296) | Deprecated. Use deletion_policy. | bool | | null | +| [service_agents_config](variables.tf#L230) | Automatic service agent configuration options. | object({…}) | | {} | +| [service_config](variables.tf#L241) | Configure service API activation. | object({…}) | | {…} | +| [service_encryption_key_ids](variables.tf#L253) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | +| [services](variables.tf#L260) | Service APIs to enable. | list(string) | | [] | +| [shared_vpc_host_config](variables.tf#L266) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | +| [shared_vpc_service_config](variables.tf#L275) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | +| [skip_delete](variables.tf#L303) | Deprecated. Use deletion_policy. | bool | | null | | [tag_bindings](variables-tags.tf#L81) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | | [tags](variables-tags.tf#L88) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [vpc_sc](variables.tf#L308) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | +| [vpc_sc](variables.tf#L315) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | ## Outputs From 9ba92c3b1e4b945cdadeaff8b9818f59bb07eb3b Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Tue, 10 Dec 2024 17:11:07 +0000 Subject: [PATCH 04/33] Convert To Multiple Factories --- fast/stages/0-bootstrap/automation.tf | 4 +- fast/stages/0-bootstrap/billing.tf | 4 +- .../0-bootstrap/data/alerts/compliance.yaml | 151 ++++++++++++++++ .../0-bootstrap/data/channels/compliance.yaml | 6 + .../logging-alerts/compliance-alerts.yaml | 123 ------------- .../data/logging-metrics/compliance.yaml | 55 ++++++ fast/stages/0-bootstrap/log-export.tf | 4 +- fast/stages/0-bootstrap/outputs.tf | 6 +- fast/stages/0-bootstrap/variables.tf | 11 +- fast/stages/1-resman/data/alerts | 1 + fast/stages/1-resman/data/channels | 1 + .../logging-alerts/compliance-alerts.yaml | 136 -------------- fast/stages/1-resman/data/logging-metrics | 1 + fast/stages/1-resman/tenant-root.tf | 4 +- fast/stages/1-resman/variables.tf | 10 +- fast/stages/2-network-security/data/alerts | 1 + fast/stages/2-network-security/data/channels | 1 + .../logging-alerts/compliance-alerts.yaml | 136 -------------- .../2-network-security/data/logging-metrics | 1 + fast/stages/2-network-security/main.tf | 4 +- fast/stages/2-network-security/variables.tf | 4 +- fast/stages/2-networking-a-simple/data/alerts | 1 + .../2-networking-a-simple/data/channels | 1 + .../logging-alerts/compliance-alerts.yaml | 136 -------------- .../data/logging-metrics | 1 + fast/stages/2-networking-a-simple/net-dev.tf | 4 +- .../2-networking-a-simple/net-landing.tf | 4 +- fast/stages/2-networking-a-simple/net-prod.tf | 4 +- .../stages/2-networking-a-simple/variables.tf | 10 +- fast/stages/2-networking-b-nva/data/alerts | 1 + fast/stages/2-networking-b-nva/data/channels | 1 + .../logging-alerts/compliance-alerts.yaml | 136 -------------- .../2-networking-b-nva/data/logging-metrics | 1 + fast/stages/2-networking-b-nva/net-dev.tf | 4 +- fast/stages/2-networking-b-nva/net-landing.tf | 4 +- fast/stages/2-networking-b-nva/net-prod.tf | 4 +- fast/stages/2-networking-b-nva/variables.tf | 10 +- .../2-networking-c-separate-envs/data/alerts | 1 + .../data/channels | 1 + .../logging-alerts/compliance-alerts.yaml | 136 -------------- .../data/logging-metrics | 1 + .../2-networking-c-separate-envs/net-dev.tf | 4 +- .../2-networking-c-separate-envs/net-prod.tf | 4 +- .../2-networking-c-separate-envs/variables.tf | 10 +- fast/stages/2-project-factory/data/alerts | 1 + fast/stages/2-project-factory/data/channels | 1 + .../logging-alerts/compliance-alerts.yaml | 136 -------------- .../2-project-factory/data/logging-metrics | 1 + fast/stages/2-project-factory/main.tf | 4 +- fast/stages/2-project-factory/variables.tf | 8 +- fast/stages/2-security/core-dev.tf | 4 +- fast/stages/2-security/core-prod.tf | 4 +- fast/stages/2-security/data/alerts | 1 + fast/stages/2-security/data/channels | 1 + .../logging-alerts/compliance-alerts.yaml | 136 -------------- fast/stages/2-security/data/logging-metrics | 1 + fast/stages/2-security/variables.tf | 4 +- modules/project-factory/main.tf | 4 +- modules/project-factory/variables.tf | 8 +- modules/project/alert-metrics.tf | 87 --------- modules/project/alerts-factory.tf | 166 ++++++++++++++++++ modules/project/logging-metrics-factory.tf | 98 +++++++++++ modules/project/notifications-factory.tf | 70 ++++++++ modules/project/variables-metrics-alerts.tf | 142 ++++++++++++++- modules/project/variables.tf | 10 +- 65 files changed, 812 insertions(+), 1218 deletions(-) create mode 100644 fast/stages/0-bootstrap/data/alerts/compliance.yaml create mode 100644 fast/stages/0-bootstrap/data/channels/compliance.yaml create mode 100644 fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml create mode 120000 fast/stages/1-resman/data/alerts create mode 120000 fast/stages/1-resman/data/channels delete mode 100644 fast/stages/1-resman/data/logging-alerts/compliance-alerts.yaml create mode 120000 fast/stages/1-resman/data/logging-metrics create mode 120000 fast/stages/2-network-security/data/alerts create mode 120000 fast/stages/2-network-security/data/channels delete mode 100644 fast/stages/2-network-security/data/logging-alerts/compliance-alerts.yaml create mode 120000 fast/stages/2-network-security/data/logging-metrics create mode 120000 fast/stages/2-networking-a-simple/data/alerts create mode 120000 fast/stages/2-networking-a-simple/data/channels delete mode 100644 fast/stages/2-networking-a-simple/data/logging-alerts/compliance-alerts.yaml create mode 120000 fast/stages/2-networking-a-simple/data/logging-metrics create mode 120000 fast/stages/2-networking-b-nva/data/alerts create mode 120000 fast/stages/2-networking-b-nva/data/channels delete mode 100644 fast/stages/2-networking-b-nva/data/logging-alerts/compliance-alerts.yaml create mode 120000 fast/stages/2-networking-b-nva/data/logging-metrics create mode 120000 fast/stages/2-networking-c-separate-envs/data/alerts create mode 120000 fast/stages/2-networking-c-separate-envs/data/channels delete mode 100644 fast/stages/2-networking-c-separate-envs/data/logging-alerts/compliance-alerts.yaml create mode 120000 fast/stages/2-networking-c-separate-envs/data/logging-metrics create mode 120000 fast/stages/2-project-factory/data/alerts create mode 120000 fast/stages/2-project-factory/data/channels delete mode 100644 fast/stages/2-project-factory/data/logging-alerts/compliance-alerts.yaml create mode 120000 fast/stages/2-project-factory/data/logging-metrics create mode 120000 fast/stages/2-security/data/alerts create mode 120000 fast/stages/2-security/data/channels delete mode 100644 fast/stages/2-security/data/logging-alerts/compliance-alerts.yaml create mode 120000 fast/stages/2-security/data/logging-metrics delete mode 100644 modules/project/alert-metrics.tf create mode 100644 modules/project/alerts-factory.tf create mode 100644 modules/project/logging-metrics-factory.tf create mode 100644 modules/project/notifications-factory.tf diff --git a/fast/stages/0-bootstrap/automation.tf b/fast/stages/0-bootstrap/automation.tf index ed9d88a2d8..071f467868 100644 --- a/fast/stages/0-bootstrap/automation.tf +++ b/fast/stages/0-bootstrap/automation.tf @@ -42,7 +42,9 @@ module "automation-project" { org_policies = ( var.bootstrap_user != null ? null : var.factories_config.org_policies_iac ) - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email # human (groups) IAM bindings diff --git a/fast/stages/0-bootstrap/billing.tf b/fast/stages/0-bootstrap/billing.tf index f2d869536c..17135efcde 100644 --- a/fast/stages/0-bootstrap/billing.tf +++ b/fast/stages/0-bootstrap/billing.tf @@ -42,7 +42,9 @@ module "billing-export-project" { count = local.billing_mode == "org" ? 1 : 0 billing_account = var.billing_account.id factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + alerts = var.factories_config.alerts + channels = var.factories_config.channels + logging_metrics = var.factories_config.logging_metrics } default_alerts_email = var.default_alerts_email name = "billing-exp-0" diff --git a/fast/stages/0-bootstrap/data/alerts/compliance.yaml b/fast/stages/0-bootstrap/data/alerts/compliance.yaml new file mode 100644 index 0000000000..117887fb02 --- /dev/null +++ b/fast/stages/0-bootstrap/data/alerts/compliance.yaml @@ -0,0 +1,151 @@ +route-changes: + display_name: "Network Route Changes" + combiner: OR + alert_strategy: + auto_close: 604800s + conditions: + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: global + trigger: + count: 1 + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + notification_channels: + compliance-default + +network-firewall-config-changes: + display_name: "VPC Network Firewall Changes" + combiner: OR + alert_strategy: + auto_close: 604800s + conditions: + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: global + trigger: + count: 1 + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + notification_channels: + compliance-default + +vpc-network-config-changes: + display_name: "VPC Network Changes" + combiner: OR + alert_strategy: + auto_close: 604800s + conditions: + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: global + trigger: + count: 1 + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + notification_channels: + compliance-default + +cloudsql-changes: + display_name: "CloudSQL Changes" + combiner: OR + alert_strategy: + auto_close: 604800s + conditions: + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: global + trigger: + count: 1 + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + notification_channels: + compliance-default + +cloudstorage-changes: + display_name: "Cloud Storage Changes" + combiner: OR + alert_strategy: + auto_close: 604800s + conditions: + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: "gcs_bucket" + trigger: + count: 1 + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + notification_channels: + compliance-default + +customrole-changes: + display_name: "IAM Custom Role Changes" + combiner: OR + alert_strategy: + auto_close: 604800s + conditions: + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: global + trigger: + count: 1 + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + notification_channels: + compliance-default + +audit-changes: + display_name: "Audit Configuration Changes" + combiner: OR + alert_strategy: + auto_close: 604800s + conditions: + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: global + trigger: + count: 1 + aggregations: + per_series_aligner: ALIGN_MEAN + cross_series_reducer: REDUCE_COUNT + alignment_period: "600s" + notification_channels: + compliance-default + +iam-owner-changes: + display_name: "Owner IAM Configuration Changes" + combiner: OR + alert_strategy: + auto_close: 604800s + conditions: + condition_threshold: + comparison: COMPARISON_GT + duration: "0s" + resource_type: global + trigger: + count: 1 + aggregations: + per_series_aligner: ALIGN_DELTA + cross_series_reducer: REDUCE_SUM + alignment_period: "600s" + notification_channels: + compliance-default diff --git a/fast/stages/0-bootstrap/data/channels/compliance.yaml b/fast/stages/0-bootstrap/data/channels/compliance.yaml new file mode 100644 index 0000000000..c256694908 --- /dev/null +++ b/fast/stages/0-bootstrap/data/channels/compliance.yaml @@ -0,0 +1,6 @@ +compliance-default: + type: email + display_name: "Default Email Notifications" + email_address: test@company.com + labels: + email_address: "fake_email@blahblah.com" diff --git a/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml b/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml index aafd306475..b2f7f7d196 100644 --- a/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml +++ b/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml @@ -1,130 +1,7 @@ -route-changes: - description: "Monitor VPC network route configuration changes inside GCP projects" - filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" - display_name: "Network Route Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -network-firewall-config-changes: - description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" - display_name: "VPC Network Firewall Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" -vpc-network-config-changes: - description: "Monitor VPC network configuration changes inside GCP projects" - filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" - display_name: "VPC Network Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudsql-changes: - description: "Monitor Cloud SQL configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" - display_name: "CloudSQL Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudstorage-changes: - description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" - display_name: "CloudStorage IAM Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: "gcs_bucket" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -customrole-changes: - description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" - display_name: "IAM Custom Role Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -audit-changes: - description: "Monitor Audit configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" - display_name: "Audit Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" iam-owner-changes: - description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" display_name: "Owner IAM Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" condition_threshold: comparison: COMPARISON_GT duration: "0s" diff --git a/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml b/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml new file mode 100644 index 0000000000..6b2d95b2da --- /dev/null +++ b/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml @@ -0,0 +1,55 @@ +route-changes: + description: "Monitor VPC network route configuration changes inside GCP projects" + filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + +network-firewall-config-changes: + description: "Monitor VPC network firewall configuration changes inside GCP projects" + filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.delete\" OR protoPayload.methodName:\"compute.firewalls.insert\")" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + +vpc-network-config-changes: + description: "Monitor VPC network configuration changes inside GCP projects" + filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + +cloudsql-changes: + description: "Monitor CloudSQL configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + +cloudstorage-changes: + description: "Monitor Cloud Storage configuration changes inside GCP projects" + filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + +customrole-changes: + description: "Monitor IAM Custom Role configuration changes inside GCP projects" + filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + +audit-changes: + description: "Monitor Audit configuration changes inside GCP projects" + filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" + +iam-owner-changes: + description: "Monitor IAM Owner configuration changes inside GCP projects" + filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + metric_descriptor: + metric_kind: DELTA + value_type: "INT64" diff --git a/fast/stages/0-bootstrap/log-export.tf b/fast/stages/0-bootstrap/log-export.tf index 9a6f83467e..ba220c3b49 100644 --- a/fast/stages/0-bootstrap/log-export.tf +++ b/fast/stages/0-bootstrap/log-export.tf @@ -43,7 +43,9 @@ module "log-export-project" { var.project_parent_ids.logging, "organizations/${var.organization.id}" ) factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + alerts = var.factories_config.alerts + channels = var.factories_config.channels + logging_metrics = var.factories_config.logging_metrics } default_alerts_email = var.default_alerts_email prefix = local.prefix diff --git a/fast/stages/0-bootstrap/outputs.tf b/fast/stages/0-bootstrap/outputs.tf index 262131f1ea..d74cd20512 100644 --- a/fast/stages/0-bootstrap/outputs.tf +++ b/fast/stages/0-bootstrap/outputs.tf @@ -23,7 +23,7 @@ locals { # If users give a list of custom audiences we set by default the first element. # If no audiences are given, we set https://iam.googleapis.com/{PROVIDER_NAME} audiences = try( - local.cicd_providers[v["identity_provider"]].audiences, "" + local.cicd_providers[v["identity_provider"]].audiences, "e" ) identity_provider = try( local.cicd_providers[v["identity_provider"]].name, "" @@ -217,8 +217,8 @@ output "tfvars" { output "tfvars_globals" { description = "Terraform Globals variable files for the following stages." - sensitive = true - value = local.tfvars_globals + sensitive = false + value = jsonencode(local.tfvars_globals) } output "workforce_identity_pool" { diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index 6471a30bf1..8c3ae0b75b 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -139,15 +139,16 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for the resource factories or external data." type = object({ - custom_roles = optional(string, "data/custom-roles") - org_policies = optional(string, "data/org-policies") - org_policies_iac = optional(string, "data/org-policies-iac") - logging_metrics_alerts = optional(string, "data/logging-alerts") + custom_roles = optional(string, "data/custom-roles") + org_policies = optional(string, "data/org-policies") + org_policies_iac = optional(string, "data/org-policies-iac") + logging_metrics = optional(string, "data/logging-metrics") + channels = optional(string, "data/channels") + alerts = optional(string, "data/alerts") }) nullable = false default = {} } - variable "groups" { # https://cloud.google.com/docs/enterprise/setup-checklist description = "Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated." diff --git a/fast/stages/1-resman/data/alerts b/fast/stages/1-resman/data/alerts new file mode 120000 index 0000000000..03f9d9702f --- /dev/null +++ b/fast/stages/1-resman/data/alerts @@ -0,0 +1 @@ +../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/1-resman/data/channels b/fast/stages/1-resman/data/channels new file mode 120000 index 0000000000..652bcb00ad --- /dev/null +++ b/fast/stages/1-resman/data/channels @@ -0,0 +1 @@ +../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/1-resman/data/logging-alerts/compliance-alerts.yaml b/fast/stages/1-resman/data/logging-alerts/compliance-alerts.yaml deleted file mode 100644 index aafd306475..0000000000 --- a/fast/stages/1-resman/data/logging-alerts/compliance-alerts.yaml +++ /dev/null @@ -1,136 +0,0 @@ -route-changes: - description: "Monitor VPC network route configuration changes inside GCP projects" - filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" - display_name: "Network Route Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -network-firewall-config-changes: - description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" - display_name: "VPC Network Firewall Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -vpc-network-config-changes: - description: "Monitor VPC network configuration changes inside GCP projects" - filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" - display_name: "VPC Network Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudsql-changes: - description: "Monitor Cloud SQL configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" - display_name: "CloudSQL Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudstorage-changes: - description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" - display_name: "CloudStorage IAM Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: "gcs_bucket" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -customrole-changes: - description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" - display_name: "IAM Custom Role Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -audit-changes: - description: "Monitor Audit configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" - display_name: "Audit Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -iam-owner-changes: - description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" - display_name: "Owner IAM Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_DELTA - cross_series_reducer: REDUCE_SUM - alignment_period: "600s" diff --git a/fast/stages/1-resman/data/logging-metrics b/fast/stages/1-resman/data/logging-metrics new file mode 120000 index 0000000000..24c8aad4b7 --- /dev/null +++ b/fast/stages/1-resman/data/logging-metrics @@ -0,0 +1 @@ +../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/1-resman/tenant-root.tf b/fast/stages/1-resman/tenant-root.tf index 771907d164..2805d68ca5 100644 --- a/fast/stages/1-resman/tenant-root.tf +++ b/fast/stages/1-resman/tenant-root.tf @@ -39,7 +39,9 @@ module "automation-project" { # do not assign tagViewer or tagUser roles here on tag keys and values as # they are managed authoritatively and will break multitenant stages factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email tags = merge(local.tags, { diff --git a/fast/stages/1-resman/variables.tf b/fast/stages/1-resman/variables.tf index faa8a8f3ae..3c8fe2e2fc 100644 --- a/fast/stages/1-resman/variables.tf +++ b/fast/stages/1-resman/variables.tf @@ -20,10 +20,12 @@ variable "factories_config" { description = "Configuration for the resource factories or external data." type = object({ - org_policies = optional(string, "data/org-policies") - stage_3 = optional(string, "data/stage-3") - top_level_folders = optional(string, "data/top-level-folders") - logging_metrics_alerts = optional(string, "data/logging-alerts") + org_policies = optional(string, "data/org-policies") + stage_3 = optional(string, "data/stage-3") + top_level_folders = optional(string, "data/top-level-folders") + logging_metrics = optional(string, "data/logging-metrics") + channels = optional(string, "data/channels") + alerts = optional(string, "data/alerts") }) nullable = false default = {} diff --git a/fast/stages/2-network-security/data/alerts b/fast/stages/2-network-security/data/alerts new file mode 120000 index 0000000000..03f9d9702f --- /dev/null +++ b/fast/stages/2-network-security/data/alerts @@ -0,0 +1 @@ +../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-network-security/data/channels b/fast/stages/2-network-security/data/channels new file mode 120000 index 0000000000..652bcb00ad --- /dev/null +++ b/fast/stages/2-network-security/data/channels @@ -0,0 +1 @@ +../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-network-security/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-network-security/data/logging-alerts/compliance-alerts.yaml deleted file mode 100644 index aafd306475..0000000000 --- a/fast/stages/2-network-security/data/logging-alerts/compliance-alerts.yaml +++ /dev/null @@ -1,136 +0,0 @@ -route-changes: - description: "Monitor VPC network route configuration changes inside GCP projects" - filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" - display_name: "Network Route Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -network-firewall-config-changes: - description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" - display_name: "VPC Network Firewall Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -vpc-network-config-changes: - description: "Monitor VPC network configuration changes inside GCP projects" - filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" - display_name: "VPC Network Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudsql-changes: - description: "Monitor Cloud SQL configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" - display_name: "CloudSQL Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudstorage-changes: - description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" - display_name: "CloudStorage IAM Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: "gcs_bucket" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -customrole-changes: - description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" - display_name: "IAM Custom Role Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -audit-changes: - description: "Monitor Audit configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" - display_name: "Audit Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -iam-owner-changes: - description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" - display_name: "Owner IAM Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_DELTA - cross_series_reducer: REDUCE_SUM - alignment_period: "600s" diff --git a/fast/stages/2-network-security/data/logging-metrics b/fast/stages/2-network-security/data/logging-metrics new file mode 120000 index 0000000000..24c8aad4b7 --- /dev/null +++ b/fast/stages/2-network-security/data/logging-metrics @@ -0,0 +1 @@ +../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-network-security/main.tf b/fast/stages/2-network-security/main.tf index 1d2ffc370f..6b01e21112 100644 --- a/fast/stages/2-network-security/main.tf +++ b/fast/stages/2-network-security/main.tf @@ -37,7 +37,9 @@ module "ngfw-quota-project" { : var.ngfw_enterprise_config.quota_project_id ) factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email billing_account = ( diff --git a/fast/stages/2-network-security/variables.tf b/fast/stages/2-network-security/variables.tf index 0fc1750d02..4528f9a1b0 100644 --- a/fast/stages/2-network-security/variables.tf +++ b/fast/stages/2-network-security/variables.tf @@ -22,7 +22,9 @@ variable "factories_config" { dev = string prod = string })) - logging_metrics_alerts = optional(string, "data/logging-alerts") + logging_metrics = optional(string, "data/logging-metrics") + channels = optional(string, "data/channels") + alerts = optional(string, "data/alerts") }) nullable = false default = { diff --git a/fast/stages/2-networking-a-simple/data/alerts b/fast/stages/2-networking-a-simple/data/alerts new file mode 120000 index 0000000000..03f9d9702f --- /dev/null +++ b/fast/stages/2-networking-a-simple/data/alerts @@ -0,0 +1 @@ +../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-networking-a-simple/data/channels b/fast/stages/2-networking-a-simple/data/channels new file mode 120000 index 0000000000..652bcb00ad --- /dev/null +++ b/fast/stages/2-networking-a-simple/data/channels @@ -0,0 +1 @@ +../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-networking-a-simple/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-networking-a-simple/data/logging-alerts/compliance-alerts.yaml deleted file mode 100644 index aafd306475..0000000000 --- a/fast/stages/2-networking-a-simple/data/logging-alerts/compliance-alerts.yaml +++ /dev/null @@ -1,136 +0,0 @@ -route-changes: - description: "Monitor VPC network route configuration changes inside GCP projects" - filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" - display_name: "Network Route Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -network-firewall-config-changes: - description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" - display_name: "VPC Network Firewall Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -vpc-network-config-changes: - description: "Monitor VPC network configuration changes inside GCP projects" - filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" - display_name: "VPC Network Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudsql-changes: - description: "Monitor Cloud SQL configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" - display_name: "CloudSQL Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudstorage-changes: - description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" - display_name: "CloudStorage IAM Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: "gcs_bucket" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -customrole-changes: - description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" - display_name: "IAM Custom Role Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -audit-changes: - description: "Monitor Audit configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" - display_name: "Audit Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -iam-owner-changes: - description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" - display_name: "Owner IAM Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_DELTA - cross_series_reducer: REDUCE_SUM - alignment_period: "600s" diff --git a/fast/stages/2-networking-a-simple/data/logging-metrics b/fast/stages/2-networking-a-simple/data/logging-metrics new file mode 120000 index 0000000000..24c8aad4b7 --- /dev/null +++ b/fast/stages/2-networking-a-simple/data/logging-metrics @@ -0,0 +1 @@ +../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-networking-a-simple/net-dev.tf b/fast/stages/2-networking-a-simple/net-dev.tf index 1015415f0c..fc79b1721e 100644 --- a/fast/stages/2-networking-a-simple/net-dev.tf +++ b/fast/stages/2-networking-a-simple/net-dev.tf @@ -21,7 +21,9 @@ module "dev-spoke-project" { billing_account = var.billing_account.id name = "dev-net-spoke-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-networking-a-simple/net-landing.tf b/fast/stages/2-networking-a-simple/net-landing.tf index 6bc96976bc..f602ec5b79 100644 --- a/fast/stages/2-networking-a-simple/net-landing.tf +++ b/fast/stages/2-networking-a-simple/net-landing.tf @@ -21,7 +21,9 @@ module "landing-project" { billing_account = var.billing_account.id name = "prod-net-landing-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-networking-a-simple/net-prod.tf b/fast/stages/2-networking-a-simple/net-prod.tf index c6d4f77605..28093e210f 100644 --- a/fast/stages/2-networking-a-simple/net-prod.tf +++ b/fast/stages/2-networking-a-simple/net-prod.tf @@ -21,7 +21,9 @@ module "prod-spoke-project" { billing_account = var.billing_account.id name = "prod-net-spoke-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-networking-a-simple/variables.tf b/fast/stages/2-networking-a-simple/variables.tf index f677f1637b..a7c0e272c4 100644 --- a/fast/stages/2-networking-a-simple/variables.tf +++ b/fast/stages/2-networking-a-simple/variables.tf @@ -71,10 +71,12 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - data_dir = optional(string, "data") - dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") - firewall_policy_name = optional(string, "net-default") - logging_metrics_alerts = optional(string, "data/logging-alerts") + data_dir = optional(string, "data") + dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") + firewall_policy_name = optional(string, "net-default") + logging_metrics = optional(string, "data/logging-metrics") + channels = optional(string, "data/channels") + alerts = optional(string, "data/alerts") }) default = { data_dir = "data" diff --git a/fast/stages/2-networking-b-nva/data/alerts b/fast/stages/2-networking-b-nva/data/alerts new file mode 120000 index 0000000000..03f9d9702f --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/alerts @@ -0,0 +1 @@ +../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-networking-b-nva/data/channels b/fast/stages/2-networking-b-nva/data/channels new file mode 120000 index 0000000000..652bcb00ad --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/channels @@ -0,0 +1 @@ +../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-networking-b-nva/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-networking-b-nva/data/logging-alerts/compliance-alerts.yaml deleted file mode 100644 index aafd306475..0000000000 --- a/fast/stages/2-networking-b-nva/data/logging-alerts/compliance-alerts.yaml +++ /dev/null @@ -1,136 +0,0 @@ -route-changes: - description: "Monitor VPC network route configuration changes inside GCP projects" - filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" - display_name: "Network Route Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -network-firewall-config-changes: - description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" - display_name: "VPC Network Firewall Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -vpc-network-config-changes: - description: "Monitor VPC network configuration changes inside GCP projects" - filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" - display_name: "VPC Network Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudsql-changes: - description: "Monitor Cloud SQL configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" - display_name: "CloudSQL Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudstorage-changes: - description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" - display_name: "CloudStorage IAM Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: "gcs_bucket" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -customrole-changes: - description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" - display_name: "IAM Custom Role Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -audit-changes: - description: "Monitor Audit configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" - display_name: "Audit Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -iam-owner-changes: - description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" - display_name: "Owner IAM Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_DELTA - cross_series_reducer: REDUCE_SUM - alignment_period: "600s" diff --git a/fast/stages/2-networking-b-nva/data/logging-metrics b/fast/stages/2-networking-b-nva/data/logging-metrics new file mode 120000 index 0000000000..24c8aad4b7 --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/logging-metrics @@ -0,0 +1 @@ +../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-networking-b-nva/net-dev.tf b/fast/stages/2-networking-b-nva/net-dev.tf index 50b8a89530..91b25eada4 100644 --- a/fast/stages/2-networking-b-nva/net-dev.tf +++ b/fast/stages/2-networking-b-nva/net-dev.tf @@ -21,7 +21,9 @@ module "dev-spoke-project" { billing_account = var.billing_account.id name = "dev-net-spoke-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-networking-b-nva/net-landing.tf b/fast/stages/2-networking-b-nva/net-landing.tf index 05df1fe529..88f250391e 100644 --- a/fast/stages/2-networking-b-nva/net-landing.tf +++ b/fast/stages/2-networking-b-nva/net-landing.tf @@ -21,7 +21,9 @@ module "landing-project" { billing_account = var.billing_account.id name = "prod-net-landing-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-networking-b-nva/net-prod.tf b/fast/stages/2-networking-b-nva/net-prod.tf index d4d0b87b8f..4a50fc6e72 100644 --- a/fast/stages/2-networking-b-nva/net-prod.tf +++ b/fast/stages/2-networking-b-nva/net-prod.tf @@ -21,7 +21,9 @@ module "prod-spoke-project" { billing_account = var.billing_account.id name = "prod-net-spoke-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-networking-b-nva/variables.tf b/fast/stages/2-networking-b-nva/variables.tf index 32a313b6c6..038962e504 100644 --- a/fast/stages/2-networking-b-nva/variables.tf +++ b/fast/stages/2-networking-b-nva/variables.tf @@ -71,10 +71,12 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - data_dir = optional(string, "data") - dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") - firewall_policy_name = optional(string, "net-default") - logging_metrics_alerts = optional(string, "data/logging-alerts") + data_dir = optional(string, "data") + dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") + firewall_policy_name = optional(string, "net-default") + logging_metrics = optional(string, "data/logging-metrics") + channels = optional(string, "data/channels") + alerts = optional(string, "data/alerts") }) default = { data_dir = "data" diff --git a/fast/stages/2-networking-c-separate-envs/data/alerts b/fast/stages/2-networking-c-separate-envs/data/alerts new file mode 120000 index 0000000000..03f9d9702f --- /dev/null +++ b/fast/stages/2-networking-c-separate-envs/data/alerts @@ -0,0 +1 @@ +../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-networking-c-separate-envs/data/channels b/fast/stages/2-networking-c-separate-envs/data/channels new file mode 120000 index 0000000000..652bcb00ad --- /dev/null +++ b/fast/stages/2-networking-c-separate-envs/data/channels @@ -0,0 +1 @@ +../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-networking-c-separate-envs/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-networking-c-separate-envs/data/logging-alerts/compliance-alerts.yaml deleted file mode 100644 index aafd306475..0000000000 --- a/fast/stages/2-networking-c-separate-envs/data/logging-alerts/compliance-alerts.yaml +++ /dev/null @@ -1,136 +0,0 @@ -route-changes: - description: "Monitor VPC network route configuration changes inside GCP projects" - filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" - display_name: "Network Route Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -network-firewall-config-changes: - description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" - display_name: "VPC Network Firewall Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -vpc-network-config-changes: - description: "Monitor VPC network configuration changes inside GCP projects" - filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" - display_name: "VPC Network Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudsql-changes: - description: "Monitor Cloud SQL configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" - display_name: "CloudSQL Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudstorage-changes: - description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" - display_name: "CloudStorage IAM Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: "gcs_bucket" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -customrole-changes: - description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" - display_name: "IAM Custom Role Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -audit-changes: - description: "Monitor Audit configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" - display_name: "Audit Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -iam-owner-changes: - description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" - display_name: "Owner IAM Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_DELTA - cross_series_reducer: REDUCE_SUM - alignment_period: "600s" diff --git a/fast/stages/2-networking-c-separate-envs/data/logging-metrics b/fast/stages/2-networking-c-separate-envs/data/logging-metrics new file mode 120000 index 0000000000..24c8aad4b7 --- /dev/null +++ b/fast/stages/2-networking-c-separate-envs/data/logging-metrics @@ -0,0 +1 @@ +../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-networking-c-separate-envs/net-dev.tf b/fast/stages/2-networking-c-separate-envs/net-dev.tf index 58d56c7b05..7cdcbacae5 100644 --- a/fast/stages/2-networking-c-separate-envs/net-dev.tf +++ b/fast/stages/2-networking-c-separate-envs/net-dev.tf @@ -21,7 +21,9 @@ module "dev-spoke-project" { billing_account = var.billing_account.id name = "dev-net-spoke-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-networking-c-separate-envs/net-prod.tf b/fast/stages/2-networking-c-separate-envs/net-prod.tf index a501e435e7..a058b3aea4 100644 --- a/fast/stages/2-networking-c-separate-envs/net-prod.tf +++ b/fast/stages/2-networking-c-separate-envs/net-prod.tf @@ -21,7 +21,9 @@ module "prod-spoke-project" { billing_account = var.billing_account.id name = "prod-net-spoke-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-networking-c-separate-envs/variables.tf b/fast/stages/2-networking-c-separate-envs/variables.tf index 8463cf737c..7c3da6907a 100644 --- a/fast/stages/2-networking-c-separate-envs/variables.tf +++ b/fast/stages/2-networking-c-separate-envs/variables.tf @@ -66,10 +66,12 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - data_dir = optional(string, "data") - dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") - firewall_policy_name = optional(string, "net-default") - logging_metrics_alerts = optional(string, "data/logging-alerts") + data_dir = optional(string, "data") + dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") + firewall_policy_name = optional(string, "net-default") + logging_metrics = optional(string, "data/logging-metrics") + channels = optional(string, "data/channels") + alerts = optional(string, "data/alerts") }) default = { data_dir = "data" diff --git a/fast/stages/2-project-factory/data/alerts b/fast/stages/2-project-factory/data/alerts new file mode 120000 index 0000000000..03f9d9702f --- /dev/null +++ b/fast/stages/2-project-factory/data/alerts @@ -0,0 +1 @@ +../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-project-factory/data/channels b/fast/stages/2-project-factory/data/channels new file mode 120000 index 0000000000..652bcb00ad --- /dev/null +++ b/fast/stages/2-project-factory/data/channels @@ -0,0 +1 @@ +../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-project-factory/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-project-factory/data/logging-alerts/compliance-alerts.yaml deleted file mode 100644 index aafd306475..0000000000 --- a/fast/stages/2-project-factory/data/logging-alerts/compliance-alerts.yaml +++ /dev/null @@ -1,136 +0,0 @@ -route-changes: - description: "Monitor VPC network route configuration changes inside GCP projects" - filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" - display_name: "Network Route Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -network-firewall-config-changes: - description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" - display_name: "VPC Network Firewall Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -vpc-network-config-changes: - description: "Monitor VPC network configuration changes inside GCP projects" - filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" - display_name: "VPC Network Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudsql-changes: - description: "Monitor Cloud SQL configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" - display_name: "CloudSQL Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudstorage-changes: - description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" - display_name: "CloudStorage IAM Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: "gcs_bucket" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -customrole-changes: - description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" - display_name: "IAM Custom Role Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -audit-changes: - description: "Monitor Audit configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" - display_name: "Audit Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -iam-owner-changes: - description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" - display_name: "Owner IAM Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_DELTA - cross_series_reducer: REDUCE_SUM - alignment_period: "600s" diff --git a/fast/stages/2-project-factory/data/logging-metrics b/fast/stages/2-project-factory/data/logging-metrics new file mode 120000 index 0000000000..24c8aad4b7 --- /dev/null +++ b/fast/stages/2-project-factory/data/logging-metrics @@ -0,0 +1 @@ +../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-project-factory/main.tf b/fast/stages/2-project-factory/main.tf index 8fbd6cc615..5ac8350d04 100644 --- a/fast/stages/2-project-factory/main.tf +++ b/fast/stages/2-project-factory/main.tf @@ -33,7 +33,9 @@ module "projects" { prefix = var.prefix } factories_config = merge(var.factories_config, { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts context = { folder_ids = merge( { for k, v in var.folder_ids : k => v if v != null }, diff --git a/fast/stages/2-project-factory/variables.tf b/fast/stages/2-project-factory/variables.tf index 9e49f08673..ffaa69cbb6 100644 --- a/fast/stages/2-project-factory/variables.tf +++ b/fast/stages/2-project-factory/variables.tf @@ -17,9 +17,11 @@ variable "factories_config" { description = "Configuration for YAML-based factories." type = object({ - folders_data_path = optional(string, "data/hierarchy") - projects_data_path = optional(string, "data/projects") - logging_metrics_alerts = optional(string, "data/logging-alerts") + folders_data_path = optional(string, "data/hierarchy") + projects_data_path = optional(string, "data/projects") + logging_metrics = optional(string, "data/logging-metrics") + channels = optional(string, "data/channels") + alerts = optional(string, "data/alerts") budgets = optional(object({ billing_account = string budgets_data_path = optional(string, "data/budgets") diff --git a/fast/stages/2-security/core-dev.tf b/fast/stages/2-security/core-dev.tf index 3c3392c6db..7e06caa53d 100644 --- a/fast/stages/2-security/core-dev.tf +++ b/fast/stages/2-security/core-dev.tf @@ -25,7 +25,9 @@ module "dev-sec-project" { source = "../../../modules/project" name = "dev-sec-core-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-security/core-prod.tf b/fast/stages/2-security/core-prod.tf index 9c3f692387..dfbde78173 100644 --- a/fast/stages/2-security/core-prod.tf +++ b/fast/stages/2-security/core-prod.tf @@ -25,7 +25,9 @@ module "prod-sec-project" { source = "../../../modules/project" name = "prod-sec-core-0" factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email parent = coalesce( diff --git a/fast/stages/2-security/data/alerts b/fast/stages/2-security/data/alerts new file mode 120000 index 0000000000..03f9d9702f --- /dev/null +++ b/fast/stages/2-security/data/alerts @@ -0,0 +1 @@ +../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-security/data/channels b/fast/stages/2-security/data/channels new file mode 120000 index 0000000000..652bcb00ad --- /dev/null +++ b/fast/stages/2-security/data/channels @@ -0,0 +1 @@ +../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-security/data/logging-alerts/compliance-alerts.yaml b/fast/stages/2-security/data/logging-alerts/compliance-alerts.yaml deleted file mode 100644 index aafd306475..0000000000 --- a/fast/stages/2-security/data/logging-alerts/compliance-alerts.yaml +++ /dev/null @@ -1,136 +0,0 @@ -route-changes: - description: "Monitor VPC network route configuration changes inside GCP projects" - filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" - display_name: "Network Route Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -network-firewall-config-changes: - description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.insert\" OR protoPayload.methodName:\"compute.firewalls.patch\" OR protoPayload.methodName:\"compute.firewalls.delete\")" - display_name: "VPC Network Firewall Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -vpc-network-config-changes: - description: "Monitor VPC network configuration changes inside GCP projects" - filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" - display_name: "VPC Network Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudsql-changes: - description: "Monitor Cloud SQL configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" - display_name: "CloudSQL Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -cloudstorage-changes: - description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" - display_name: "CloudStorage IAM Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: "gcs_bucket" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -customrole-changes: - description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" - display_name: "IAM Custom Role Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -audit-changes: - description: "Monitor Audit configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" - display_name: "Audit Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - -iam-owner-changes: - description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" - display_name: "Owner IAM Configuration Changes" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_DELTA - cross_series_reducer: REDUCE_SUM - alignment_period: "600s" diff --git a/fast/stages/2-security/data/logging-metrics b/fast/stages/2-security/data/logging-metrics new file mode 120000 index 0000000000..24c8aad4b7 --- /dev/null +++ b/fast/stages/2-security/data/logging-metrics @@ -0,0 +1 @@ +../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-security/variables.tf b/fast/stages/2-security/variables.tf index 24377700d9..0f735e1db3 100644 --- a/fast/stages/2-security/variables.tf +++ b/fast/stages/2-security/variables.tf @@ -184,7 +184,9 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - logging_metrics_alerts = optional(string, "data/logging-alerts") + logging_metrics = optional(string, "data/logging-metrics") + channels = optional(string, "data/channels") + alerts = optional(string, "data/alerts") }) nullable = false } diff --git a/modules/project-factory/main.tf b/modules/project-factory/main.tf index 61aba640d3..9dbb4839c8 100644 --- a/modules/project-factory/main.tf +++ b/modules/project-factory/main.tf @@ -41,7 +41,9 @@ module "projects" { local.context.folder_ids, each.value.parent, each.value.parent ) factories_config = { - logging_metrics_alerts = var.factories_config.logging_metrics_alerts + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts } default_alerts_email = "" prefix = each.value.prefix diff --git a/modules/project-factory/variables.tf b/modules/project-factory/variables.tf index 3296ef128d..60adf0d0d5 100644 --- a/modules/project-factory/variables.tf +++ b/modules/project-factory/variables.tf @@ -106,9 +106,11 @@ variable "default_alerts_email" { variable "factories_config" { description = "Path to folder with YAML resource description data files." type = object({ - folders_data_path = optional(string) - projects_data_path = optional(string) - logging_metrics_alerts = optional(string) + folders_data_path = optional(string) + projects_data_path = optional(string) + logging_metrics = optional(string) + channels = optional(string) + alerts = optional(string) budgets = optional(object({ billing_account = string budgets_data_path = string diff --git a/modules/project/alert-metrics.tf b/modules/project/alert-metrics.tf deleted file mode 100644 index b69a4eef72..0000000000 --- a/modules/project/alert-metrics.tf +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2024 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. - */ - -locals { - _logging_metrics_alerts_factory_data_raw = merge([ - for f in try(fileset(var.factories_config.logging_metrics_alerts, "*.yaml"), []) : - yamldecode(file("${var.factories_config.logging_metrics_alerts}/${f}")) - ]...) - _logging_metrics_alerts_factory_data = { - for k, v in local._logging_metrics_alerts_factory_data_raw : - k => merge({ - name = k - description = null - filter = null - display_name = null - metric_descriptor = {} - condition_threshold = {} - trigger_count = null - aggregations = {} - combiner = null - notification_channels = [] - }, v) - } - logging_metrics_alerts = merge(local._logging_metrics_alerts_factory_data, var.logging_metrics_alerts) -} - -resource "google_monitoring_notification_channel" "default" { - project = local.project.project_id - display_name = "Default Email Notification" - type = "email" - labels = { - email_address = try(var.default_alerts_email) - } -} - -resource "google_logging_metric" "default" { - for_each = local.logging_metrics_alerts - project = local.project.project_id - filter = each.value.filter - name = each.value.name - description = each.value.description - metric_descriptor { - metric_kind = each.value.metric_descriptor.metric_kind - value_type = each.value.metric_descriptor.value_type - } -} - -resource "google_monitoring_alert_policy" "default" { - for_each = local.logging_metrics_alerts - project = local.project.project_id - combiner = each.value.combiner - display_name = each.value.display_name - conditions { - display_name = each.value.display_name - condition_threshold { - comparison = each.value.condition_threshold.comparison - duration = each.value.condition_threshold.duration - filter = try("resource.type = \"${each.value.condition_threshold.resource_type}\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.default[each.value.name].name}\"", "resource.type = \"global\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.default[each.value.name].name}\"") - trigger { - count = each.value.trigger_count - } - aggregations { - per_series_aligner = each.value.aggregations.per_series_aligner - cross_series_reducer = each.value.aggregations.cross_series_reducer - alignment_period = each.value.aggregations.alignment_period - } - } - } - notification_channels = try(split(",", google_monitoring_notification_channel.default.id), - each.value.notification_channels) - alert_strategy { - auto_close = "604800s" - } -} diff --git a/modules/project/alerts-factory.tf b/modules/project/alerts-factory.tf new file mode 100644 index 0000000000..6570dd3654 --- /dev/null +++ b/modules/project/alerts-factory.tf @@ -0,0 +1,166 @@ +/** + * Copyright 2024 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. + */ + +locals { + _alerts_factory_data_raw = merge([ + for f in try(fileset(var.factories_config.alerts, "*.yaml"), []) : + yamldecode(file("${var.factories_config.alerts}/${f}")) + ]...) + _alerts_factory_data = { + for k, v in local._alerts_factory_data_raw : + k => merge({ + name = k + filter = null + display_name = null + condition_threshold = {} + trigger_count = null + combiner = null + notification_channels = [] + conditions = { + display_name = null + condition_matched_log = null + condition_monitoring_query_language = null + condition_threshold = {} + condition_absent = null + } + alert_strategy = null + documentation = null + }, v) + } + alerts = merge(local._alerts_factory_data, var.logging_metrics) +} + +resource "google_monitoring_alert_policy" "default" { + for_each = local.alerts + project = "jetstack-joshua-wright" + # project = local.project.project_id + combiner = each.value.combiner + display_name = each.value.display_name + conditions { + display_name = each.value.display_name + dynamic "condition_matched_log" { + for_each = lookup(each.value.conditions, "condition_matched_log", null)[*] + content { + filter = each.value.conditions.condition_matched_log.filter + label_extractors = each.value.conditions.condition_matched_log.label_extractors + } + } + dynamic "condition_monitoring_query_language" { + for_each = lookup(each.value.conditions, "condition_monitoring_query_language", null)[*] + content { + query = each.value.conditions.condition_monitoring_query_language.query + duration = each.value.conditions.condition_monitoring_query_language.duration + trigger { + count = try(each.value.conditions.condition_monitoring_query_language.trigger.count, null) + percent = try(each.value.conditions.condition_monitoring_query_language.trigger.percent, null) + } + evaluation_missing_data = each.value.conditions.condition_monitoring_query_language.evaluation_missing_data + } + } + dynamic "condition_absent" { + for_each = lookup(each.value.conditions, "condition_absent", null)[*] + content { + filter = each.value.conditions.condition_absent.filter + trigger { + count = try(each.value.conditions.condition_absent.trigger.count, null) + percent = try(each.value.conditions.condition_absent.trigger.percent, null) + } + aggregations { + per_series_aligner = try(each.value.conditions.condition_absent.aggregations.per_series_aligner, null) + group_by_fields = try(each.value.conditions.condition_absent.aggregations.group_by_fields, []) + cross_series_reducer = try(each.value.conditions.condition_absent.aggregations.cross_series_reducer, null) + alignment_period = try(each.value.conditions.condition_absent.aggregations.alignment_period, null) + } + duration = each.value.conditions.condition_absent.duration + } + } + dynamic "condition_threshold" { + for_each = lookup(each.value.conditions, "condition_threshold", null)[*] + content { + threshold_value = try(each.value.condition_threshold.threshold_value, null) + denominator_filter = try(each.value.condition_threshold.denominator_filter, null) + dynamic "denominator_aggregations" { + for_each = try(each.value.condition_threshold.denominator_aggregations, null)[*] + content { + per_series_aligner = try(each.value.conditions.condition_threshold.denominator_aggregations.per_series_aligner, null) + group_by_fields = try(each.value.conditions.condition_threshold.denominator_aggregations.group_by_fields, []) + alignment_period = try(each.value.conditions.condition_threshold.denominator_aggregations.alignment_period, null) + } + } + dynamic "forecast_options" { + for_each = try(each.value.conditions.condition_threshold.forecast_options, null)[*] + content { + forecast_horizon = try(each.value.conditions.condition_threshold.forecast_options.forecast_horizon, null) + } + } + comparison = try(each.value.conditions.condition_threshold.comparison, null) + duration = try(each.value.conditions.condition_threshold.duration, null) + evaluation_missing_data = try(each.value.conditions.condition_threshold.evaluation_missing_data, null) + filter = try("resource.type = \"${each.value.conditions.condition_threshold.resource_type}\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.default[each.value.name].name}\"", each.value.conditions.condition_threshold.filter) + trigger { + count = try(each.value.conditions.condition_threshold.trigger.count, null) + percent = try(each.value.conditions.condition_threshold.trigger.percent, null) + } + aggregations { + per_series_aligner = try(each.value.conditions.condition_threshold.aggregations.per_series_aligner, null) + group_by_fields = try(each.value.conditions.condition_threshold.aggregations.group_by_fields, []) + cross_series_reducer = try(each.value.conditions.condition_threshold.aggregations.cross_series_reducer, null) + alignment_period = try(each.value.conditions.condition_threshold.aggregations.alignment_period, null) + } + } + } + } + notification_channels = [ + try(google_monitoring_notification_channel.this[each.value.notification_channels].id, + each.value.notification_channels), + try(google_monitoring_notification_channel.default[0].id, null) + ] + dynamic "alert_strategy" { + for_each = lookup(each.value, "alert_strategy", null)[*] + content { + auto_close = try(each.value.alert_strategy.auto_close, "604800s") + notification_prompts = try(each.value.alert_strategy.notification_prompts, null) + dynamic "notification_rate_limit" { + for_each = try(each.value.alert_strategy.notification_rate_limit, null)[*] + content { + period = try(each.value.alert_strategy.notification_rate_limit.period, null) + } + } + dynamic "notification_channel_strategy" { + for_each = try(each.value.alert_strategy.notification_channel_strategy, null)[*] + content { + notification_channel_names = try(each.value.alert_strategy.notification_channel_strategy.notification_channel_names, []) + renotify_interval = try(each.value.alert_strategy.notification_channel_strategy.renotify_interval, null) + } + } + } + } + dynamic "documentation" { + for_each = lookup(each.value, "documentation", null)[*] + content { + content = try(each.value.documentation.content, null) + mime_type = try(each.value.documentation.mime_type, null) + subject = try(each.value.documentation.subject, null) + dynamic "links" { + for_each = try(each.value.documentation.links, []) + content { + display_name = try(links.value.display_name, null) + url = try(links.value.url, null) + } + } + } + } +} diff --git a/modules/project/logging-metrics-factory.tf b/modules/project/logging-metrics-factory.tf new file mode 100644 index 0000000000..86bf14899e --- /dev/null +++ b/modules/project/logging-metrics-factory.tf @@ -0,0 +1,98 @@ +/** + * Copyright 2024 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. + */ + +locals { + _logging_metrics_factory_data_raw = merge([ + for f in try(fileset(var.factories_config.logging_metrics, "*.yaml"), []) : + yamldecode(file("${var.factories_config.logging_metrics}/${f}")) + ]...) + _logging_metrics_factory_data = { + for k, v in local._logging_metrics_factory_data_raw : + k => merge({ + name = k + description = null + filter = null + value_extractor = null + label_extractors = null + bucket_name = null + disabled = null + metric_descriptor = { + metric_kind = null + value_type = null + labels = { + key = null + description = null + value_type = null + } + } + bucket_options = {} + }, v) + } + metrics = merge(local._logging_metrics_factory_data, var.logging_metrics) +} + +resource "google_logging_metric" "default" { + for_each = local.metrics + project = "jetstack-joshua-wright" + # project = local.project.project_id + filter = each.value.filter + name = each.value.name + disabled = each.value.disabled + description = each.value.description + bucket_name = each.value.bucket_name + metric_descriptor { + metric_kind = try(each.value.metric_descriptor.metric_kind, null) + value_type = try(each.value.metric_descriptor.value_type, null) + unit = try(each.value.metric_descriptor.unit, 1) + dynamic "labels" { + for_each = try(each.value.metric_descriptor.labels, {}) + content { + key = labels.value.key + description = labels.value.description + value_type = labels.value.value_type + } + } + } + value_extractor = each.value.value_extractor + label_extractors = each.value.label_extractors + dynamic "bucket_options" { + for_each = try(each.value.bucket_options, {}) + content { + dynamic "linear_buckets" { + for_each = lookup(each.value.bucket_options, "linear_buckets", null)[*] + content { + num_finite_buckets = each.value.bucket_options.linear_buckets.num_finite_buckets + width = each.value.bucket_options.linear_buckets.width + offset = each.value.bucket_options.linear_buckets.offset + } + } + dynamic "exponential_buckets" { + for_each = lookup(each.value.bucket_options, "exponential_buckets", null)[*] + content { + num_finite_buckets = each.value.bucket_options.exponential_buckets.num_finite_buckets + growth_factor = each.value.bucket_options.exponential_buckets.growth_factor + scale = each.value.bucket_options.exponential_buckets.scale + } + } + dynamic "explicit_buckets" { + for_each = lookup(each.value.bucket_options, "explicit_buckets", null)[*] + content { + bounds = each.value.bucket_options.explicit_buckets.bounds + } + } + } + } +} diff --git a/modules/project/notifications-factory.tf b/modules/project/notifications-factory.tf new file mode 100644 index 0000000000..a8339ac663 --- /dev/null +++ b/modules/project/notifications-factory.tf @@ -0,0 +1,70 @@ +/** + * Copyright 2024 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. + */ + +locals { + _channels_factory_data_raw = merge([ + for f in try(fileset(var.factories_config.channels, "*.yaml"), []) : + yamldecode(file("${var.factories_config.channels}/${f}")) + ]...) + _channels_factory_data = { + for k, v in local._channels_factory_data_raw : + k => merge({ + name = k + type = null + email_address = null + display_name = null + labels = {} + sensitive_labels = [] + user_labels = {} + enabled = null + description = null + }, v) + } + channels = merge(local._channels_factory_data, var.channels) +} + +resource "google_monitoring_notification_channel" "default" { + count = var.default_alerts_email != null ? 1 : 0 + project = "jetstack-joshua-wright" + # project = local.project.project_id + display_name = "Default Email Notification" + type = "email" + labels = { + email_address = var.default_alerts_email + } +} + +resource "google_monitoring_notification_channel" "this" { + for_each = local.channels + project = "jetstack-joshua-wright" + # project = local.project.project_id + enabled = each.value.enabled + display_name = each.value.display_name + type = each.value.type + labels = each.value.labels + user_labels = each.value.user_labels + description = each.value.description + dynamic "sensitive_labels" { + for_each = lookup(each.value, "sensitive_labels", null)[*] + content { + auth_token = try(each.value.sensitive_labels.auth_token, null) + password = try(each.value.sensitive_labels.password, null) + service_key = try(each.value.sensitive_labels.service_key, null) + } + } +} + + diff --git a/modules/project/variables-metrics-alerts.tf b/modules/project/variables-metrics-alerts.tf index 0da9c1b849..2a450df851 100644 --- a/modules/project/variables-metrics-alerts.tf +++ b/modules/project/variables-metrics-alerts.tf @@ -1,16 +1,152 @@ -variable "logging_metrics_alerts" { +variable "alerts" { description = "Logging metrics alerts configuration." type = map(object({ name = optional(string) description = optional(string) filter = optional(string) display_name = optional(string) - metric_descriptor = optional(map(string)) condition_threshold = optional(map(string)) trigger_count = optional(number) - aggregations = optional(map(string)) combiner = optional(string) notification_channels = optional(list(string)) + documentation = optional(object({ + content = optional(string) + mime_type = optional(string) + subject = optional(string) + links = optional(list(object({ + display_name = optional(string) + url = optional(string) + }))) + })) + alert_strategy = optional(object({ + auto_close = optional(string) + notification_prompts = optional(string) + notification_rate_limit = optional(object({ + period = optional(string) + })) + notification_channel_strategy = optional(object({ + notification_channel_names = optional(list(string)) + renotify_interval = optional(string) + })) + })) + conditions = optional(object({ + display_name = optional(string) + condition_matched_log = optional(object({ + filter = optional(string) + label_extractors = optional(map(string)) + })) + condition_monitoring_query_language = optional(object({ + query = optional(string) + duration = optional(string) + trigger = optional(object({ + count = optional(number) + percent = optional(number) + })) + evaluation_missing_data = optional(string) + })) + condition_threshold = optional(object({ + threshold_value = optional(number) + denominator_filter = optional(string) + denominator_aggregations = optional(object({ + per_series_aligner = optional(string) + group_by_fields = optional(list(string)) + cross_series_reducer = optional(string) + alignment_period = optional(string) + })) + forecast_options = optional(object({ + forecast_horizon = optional(string) + })) + comparison = optional(string) + duration = optional(string) + filter = optional(string) + evaluation_missing_data = optional(string) + resource_type = optional(string) + trigger = optional(object({ + count = optional(number) + percent = optional(number) + })) + aggregations = optional(object({ + per_series_aligner = optional(string) + group_by_fields = optional(list(string)) + cross_series_reducer = optional(string) + alignment_period = optional(string) + })) + })) + condition_absent = optional(object({ + filter = optional(string) + aggregations = optional(object({ + per_series_aligner = optional(string) + group_by_fields = optional(list(string)) + cross_series_reducer = optional(string) + alignment_period = optional(string) + })) + trigger = optional(object({ + count = optional(number) + percent = optional(number) + })) + duration = optional(string) + })) + + })) + })) + nullable = false + default = {} +} + +variable "logging_metrics" { + description = "Logging metrics alerts configuration." + type = map(object({ + name = optional(string) + description = optional(string) + filter = optional(string) + bucket_name = optional(string) + value_extractor = optional(string) + label_extractors = optional(map(string)) + disabled = optional(bool) + metric_descriptor = optional(map(object({ + metric_kind = optional(string) + value_type = optional(string) + labels = list(object({ + key = optional(string) + description = optional(string) + value_type = optional(string) + })) + }))) + bucket_options = optional(object({ + linear_buckets = optional(object({ + num_finite_buckets = optional(number) + width = optional(number) + offset = optional(number) + })) + exponential_buckets = optional(object({ + num_finite_buckets = optional(number) + growth_factor = optional(number) + scale = optional(number) + })) + explicit_buckets = optional(object({ + bounds = optional(list(number)) + })) + })) + })) + nullable = false + default = {} +} + +variable "channels" { + description = "Logging metrics alerts configuration." + type = map(object({ + type = optional(string) + email_address = optional(string) + labels = optional(map(string)) + description = optional(string) + display_name = optional(string) + user_labels = optional(map(string)) + sensitive_labels = optional(list(object({ + auth_token = optional(string) + password = optional(string) + service_key = optional(string) + }))) + enabled = optional(bool) })) nullable = false default = {} diff --git a/modules/project/variables.tf b/modules/project/variables.tf index f49dac6196..fb3759ee81 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -87,10 +87,12 @@ variable "default_alerts_email" { variable "factories_config" { description = "Paths to data files and folders that enable factory functionality." type = object({ - custom_roles = optional(string) - org_policies = optional(string) - quotas = optional(string) - logging_metrics_alerts = optional(string) + custom_roles = optional(string) + org_policies = optional(string) + quotas = optional(string) + alerts = optional(string) + channels = optional(string) + logging_metrics = optional(string) }) nullable = false default = {} From 4e8a745d9c8af2f4fa9ea98ed64b62062ddcd667 Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Tue, 10 Dec 2024 17:12:32 +0000 Subject: [PATCH 05/33] Correct Project --- modules/project/alerts-factory.tf | 5 ++--- modules/project/logging-metrics-factory.tf | 5 ++--- modules/project/notifications-factory.tf | 10 ++++------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/modules/project/alerts-factory.tf b/modules/project/alerts-factory.tf index 6570dd3654..aa119dbfde 100644 --- a/modules/project/alerts-factory.tf +++ b/modules/project/alerts-factory.tf @@ -44,9 +44,8 @@ locals { } resource "google_monitoring_alert_policy" "default" { - for_each = local.alerts - project = "jetstack-joshua-wright" - # project = local.project.project_id + for_each = local.alerts + project = local.project.project_id combiner = each.value.combiner display_name = each.value.display_name conditions { diff --git a/modules/project/logging-metrics-factory.tf b/modules/project/logging-metrics-factory.tf index 86bf14899e..56993f3fc2 100644 --- a/modules/project/logging-metrics-factory.tf +++ b/modules/project/logging-metrics-factory.tf @@ -45,9 +45,8 @@ locals { } resource "google_logging_metric" "default" { - for_each = local.metrics - project = "jetstack-joshua-wright" - # project = local.project.project_id + for_each = local.metrics + project = local.project.project_id filter = each.value.filter name = each.value.name disabled = each.value.disabled diff --git a/modules/project/notifications-factory.tf b/modules/project/notifications-factory.tf index a8339ac663..7d1914bfc4 100644 --- a/modules/project/notifications-factory.tf +++ b/modules/project/notifications-factory.tf @@ -37,9 +37,8 @@ locals { } resource "google_monitoring_notification_channel" "default" { - count = var.default_alerts_email != null ? 1 : 0 - project = "jetstack-joshua-wright" - # project = local.project.project_id + count = var.default_alerts_email != null ? 1 : 0 + project = local.project.project_id display_name = "Default Email Notification" type = "email" labels = { @@ -48,9 +47,8 @@ resource "google_monitoring_notification_channel" "default" { } resource "google_monitoring_notification_channel" "this" { - for_each = local.channels - project = "jetstack-joshua-wright" - # project = local.project.project_id + for_each = local.channels + project = local.project.project_id enabled = each.value.enabled display_name = each.value.display_name type = each.value.type From 8f16176c2182e178cac2f66fbde6566eef8dcb77 Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Tue, 10 Dec 2024 17:25:04 +0000 Subject: [PATCH 06/33] Update Documentation --- fast/stages/0-bootstrap/README.md | 57 ++++++++++--------- fast/stages/1-resman/README.md | 23 ++++---- fast/stages/2-network-security/README.md | 19 ++++--- fast/stages/2-networking-a-simple/README.md | 23 ++++---- fast/stages/2-networking-b-nva/README.md | 27 ++++----- .../2-networking-c-separate-envs/README.md | 23 ++++---- fast/stages/2-project-factory/README.md | 17 +++--- fast/stages/2-security/README.md | 20 ++++--- modules/project-factory/README.md | 2 +- modules/project/README.md | 56 +++++++++--------- modules/project/variables.tf | 12 ++-- 11 files changed, 146 insertions(+), 133 deletions(-) diff --git a/fast/stages/0-bootstrap/README.md b/fast/stages/0-bootstrap/README.md index 14f61f818b..8300ab764c 100644 --- a/fast/stages/0-bootstrap/README.md +++ b/fast/stages/0-bootstrap/README.md @@ -661,40 +661,41 @@ The remaining configuration is manual, as it regards the repositories themselves | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| | [billing_account](variables.tf#L17) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | | -| [organization](variables.tf#L275) | Organization details. | object({…}) | ✓ | | | -| [prefix](variables.tf#L290) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | +| [default_alerts_email](variables.tf#L94) | Default email address for alerting. | string | ✓ | | | +| [organization](variables.tf#L283) | Organization details. | object({…}) | ✓ | | | +| [prefix](variables.tf#L298) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | | [bootstrap_user](variables.tf#L27) | Email of the nominal user running this stage for the first time. | string | | null | | | [cicd_repositories](variables.tf#L33) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | null | | | [custom_roles](variables.tf#L87) | Map of role names => list of permissions to additionally create at the organization level. | map(list(string)) | | {} | | -| [environments](variables.tf#L94) | Environment names. | map(object({…})) | | {…} | | -| [essential_contacts](variables.tf#L127) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L133) | Configuration for the resource factories or external data. | object({…}) | | {} | | -| [groups](variables.tf#L144) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | -| [iam](variables.tf#L160) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | -| [iam_bindings_additive](variables.tf#L167) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | -| [iam_by_principals](variables.tf#L182) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | -| [locations](variables.tf#L189) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | -| [log_sinks](variables.tf#L203) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | -| [org_policies_config](variables.tf#L256) | Organization policies customization. | object({…}) | | {} | | -| [outputs_location](variables.tf#L284) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [project_parent_ids](variables.tf#L299) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | -| [workforce_identity_providers](variables.tf#L310) | Workforce Identity Federation pools. | map(object({…})) | | {} | | -| [workload_identity_providers](variables.tf#L326) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | +| [environments](variables.tf#L100) | Environment names. | map(object({…})) | | {…} | | +| [essential_contacts](variables.tf#L133) | Email used for essential contacts, unset if null. | string | | null | | +| [factories_config](variables.tf#L139) | Configuration for the resource factories or external data. | object({…}) | | {} | | +| [groups](variables.tf#L152) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | +| [iam](variables.tf#L168) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | +| [iam_bindings_additive](variables.tf#L175) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | +| [iam_by_principals](variables.tf#L190) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | +| [locations](variables.tf#L197) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | +| [log_sinks](variables.tf#L211) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | +| [org_policies_config](variables.tf#L264) | Organization policies customization. | object({…}) | | {} | | +| [outputs_location](variables.tf#L292) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [project_parent_ids](variables.tf#L307) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | +| [workforce_identity_providers](variables.tf#L318) | Workforce Identity Federation pools. | map(object({…})) | | {} | | +| [workload_identity_providers](variables.tf#L334) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [automation](outputs.tf#L153) | Automation resources. | | | -| [billing_dataset](outputs.tf#L158) | BigQuery dataset prepared for billing export. | | | -| [cicd_repositories](outputs.tf#L163) | CI/CD repository configurations. | | | -| [custom_roles](outputs.tf#L175) | Organization-level custom roles. | | | -| [outputs_bucket](outputs.tf#L180) | GCS bucket where generated output files are stored. | | | -| [project_ids](outputs.tf#L185) | Projects created by this stage. | | | -| [providers](outputs.tf#L195) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | -| [service_accounts](outputs.tf#L202) | Automation service accounts created by this stage. | | | -| [tfvars](outputs.tf#L211) | Terraform variable files for the following stages. | ✓ | | -| [tfvars_globals](outputs.tf#L217) | Terraform Globals variable files for the following stages. | ✓ | | -| [workforce_identity_pool](outputs.tf#L223) | Workforce Identity Federation pool. | | | -| [workload_identity_pool](outputs.tf#L232) | Workload Identity Federation pool and providers. | | | +| [automation](outputs.tf#L154) | Automation resources. | | | +| [billing_dataset](outputs.tf#L159) | BigQuery dataset prepared for billing export. | | | +| [cicd_repositories](outputs.tf#L164) | CI/CD repository configurations. | | | +| [custom_roles](outputs.tf#L176) | Organization-level custom roles. | | | +| [outputs_bucket](outputs.tf#L181) | GCS bucket where generated output files are stored. | | | +| [project_ids](outputs.tf#L186) | Projects created by this stage. | | | +| [providers](outputs.tf#L196) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | +| [service_accounts](outputs.tf#L203) | Automation service accounts created by this stage. | | | +| [tfvars](outputs.tf#L212) | Terraform variable files for the following stages. | ✓ | | +| [tfvars_globals](outputs.tf#L218) | Terraform Globals variable files for the following stages. | ✓ | | +| [workforce_identity_pool](outputs.tf#L224) | Workforce Identity Federation pool. | | | +| [workload_identity_pool](outputs.tf#L233) | Workload Identity Federation pool and providers. | | | diff --git a/fast/stages/1-resman/README.md b/fast/stages/1-resman/README.md index f70125fa59..97f2517ce2 100644 --- a/fast/stages/1-resman/README.md +++ b/fast/stages/1-resman/README.md @@ -267,20 +267,21 @@ terraform apply |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L42) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | 0-bootstrap | -| [environments](variables-fast.tf#L71) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [logging](variables-fast.tf#L116) | Logging configuration for tenants. | object({…}) | ✓ | | 1-tenant-factory | -| [organization](variables-fast.tf#L129) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables-fast.tf#L147) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [default_alerts_email](variables-fast.tf#L71) | Default email address for alerting. | string | ✓ | | | +| [environments](variables-fast.tf#L77) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [logging](variables-fast.tf#L122) | Logging configuration for tenants. | object({…}) | ✓ | | 1-tenant-factory | +| [organization](variables-fast.tf#L135) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables-fast.tf#L153) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [custom_roles](variables-fast.tf#L53) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [factories_config](variables.tf#L20) | Configuration for the resource factories or external data. | object({…}) | | {} | | +| [factories_config](variables.tf#L20) | Configuration for the resource factories or external data. | object({…}) | | {} | | | [fast_stage_2](variables-stages.tf#L17) | FAST stages 2 configurations. | object({…}) | | {} | | | [fast_stage_3](variables-stages.tf#L97) | FAST stages 3 configurations. | map(object({…})) | | {} | | -| [groups](variables-fast.tf#L88) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | -| [locations](variables-fast.tf#L103) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | -| [outputs_location](variables.tf#L31) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [root_node](variables-fast.tf#L153) | Root node for the hierarchy, if running in tenant mode. | string | | null | 0-bootstrap | -| [tag_names](variables.tf#L37) | Customized names for resource management tags. | object({…}) | | {} | | -| [tags](variables.tf#L51) | Custom secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | +| [groups](variables-fast.tf#L94) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | +| [locations](variables-fast.tf#L109) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | +| [outputs_location](variables.tf#L34) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [root_node](variables-fast.tf#L159) | Root node for the hierarchy, if running in tenant mode. | string | | null | 0-bootstrap | +| [tag_names](variables.tf#L40) | Customized names for resource management tags. | object({…}) | | {} | | +| [tags](variables.tf#L54) | Custom secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | | [top_level_folders](variables-toplevel-folders.tf#L17) | Additional top-level folders. Keys are used for service account and bucket names, values implement the folders module interface with the addition of the 'automation' attribute. | map(object({…})) | | {} | | ## Outputs diff --git a/fast/stages/2-network-security/README.md b/fast/stages/2-network-security/README.md index ebad676156..1e205ec814 100644 --- a/fast/stages/2-network-security/README.md +++ b/fast/stages/2-network-security/README.md @@ -180,15 +180,16 @@ Make sure the CAs and the trusted configs created for NGFW Enterprise in the [2- |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L27) | 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-fast.tf#L40) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables-fast.tf#L82) | Organization details. | object({…}) | ✓ | | 00-globals | -| [prefix](variables-fast.tf#L92) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | -| [vpc_self_links](variables-fast.tf#L102) | Self link for the shared VPC. | object({…}) | ✓ | | 2-networking | -| [factories_config](variables.tf#L17) | Configuration for network resource factories. | object({…}) | | {…} | | -| [host_project_ids](variables-fast.tf#L51) | Host project for the shared VPC. | object({…}) | | {} | 2-networking | -| [ngfw_enterprise_config](variables.tf#L35) | NGFW Enterprise configuration. | object({…}) | | {…} | | -| [ngfw_tls_configs](variables-fast.tf#L62) | The NGFW Enterprise TLS configurations. | object({…}) | | {…} | 2-security | -| [outputs_location](variables.tf#L51) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [default_alerts_email](variables-fast.tf#L40) | Default email address for alerting. | string | ✓ | | | +| [folder_ids](variables-fast.tf#L46) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables-fast.tf#L88) | Organization details. | object({…}) | ✓ | | 00-globals | +| [prefix](variables-fast.tf#L98) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [vpc_self_links](variables-fast.tf#L108) | Self link for the shared VPC. | object({…}) | ✓ | | 2-networking | +| [factories_config](variables.tf#L17) | Configuration for network resource factories. | object({…}) | | {…} | | +| [host_project_ids](variables-fast.tf#L57) | Host project for the shared VPC. | object({…}) | | {} | 2-networking | +| [ngfw_enterprise_config](variables.tf#L38) | NGFW Enterprise configuration. | object({…}) | | {…} | | +| [ngfw_tls_configs](variables-fast.tf#L68) | The NGFW Enterprise TLS configurations. | object({…}) | | {…} | 2-security | +| [outputs_location](variables.tf#L54) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | ## Outputs diff --git a/fast/stages/2-networking-a-simple/README.md b/fast/stages/2-networking-a-simple/README.md index 574689ef2d..be27767b36 100644 --- a/fast/stages/2-networking-a-simple/README.md +++ b/fast/stages/2-networking-a-simple/README.md @@ -501,23 +501,24 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L27) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [environments](variables-fast.tf#L49) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [folder_ids](variables-fast.tf#L66) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | -| [prefix](variables-fast.tf#L76) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [default_alerts_email](variables-fast.tf#L49) | Default email address for alerting. | string | ✓ | | | +| [environments](variables-fast.tf#L55) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [folder_ids](variables-fast.tf#L72) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | +| [prefix](variables-fast.tf#L82) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [create_test_instances](variables.tf#L42) | Enables the creation of test VMs in each VPC, useful to test and troubleshoot connectivity. | bool | | false | | | [custom_roles](variables-fast.tf#L40) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L48) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L58) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L65) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L71) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L92) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L98) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | {} | | -| [regions](variables.tf#L118) | Region definitions. | object({…}) | | {…} | | -| [spoke_configs](variables.tf#L130) | Spoke connectivity configurations. | object({…}) | | {…} | | -| [stage_config](variables-fast.tf#L86) | FAST stage configuration. | object({…}) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L100) | Root-level tag values. | map(string) | | {} | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L199) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [factories_config](variables.tf#L71) | Configuration for network resource factories. | object({…}) | | {…} | | +| [outputs_location](variables.tf#L95) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L101) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | {} | | +| [regions](variables.tf#L121) | Region definitions. | object({…}) | | {…} | | +| [spoke_configs](variables.tf#L133) | Spoke connectivity configurations. | object({…}) | | {…} | | +| [stage_config](variables-fast.tf#L92) | FAST stage configuration. | object({…}) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L106) | Root-level tag values. | map(string) | | {} | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L202) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-b-nva/README.md b/fast/stages/2-networking-b-nva/README.md index c8f9b78bb1..c64fa6d669 100644 --- a/fast/stages/2-networking-b-nva/README.md +++ b/fast/stages/2-networking-b-nva/README.md @@ -562,25 +562,26 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L27) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [environments](variables-fast.tf#L49) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [folder_ids](variables-fast.tf#L66) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | -| [prefix](variables-fast.tf#L76) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [default_alerts_email](variables-fast.tf#L49) | Default email address for alerting. | string | ✓ | | | +| [environments](variables-fast.tf#L55) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [folder_ids](variables-fast.tf#L72) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | +| [prefix](variables-fast.tf#L82) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [create_test_instances](variables.tf#L42) | Enables the creation of test VMs in each VPC, useful to test and troubleshoot connectivity. | bool | | false | | | [custom_roles](variables-fast.tf#L40) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L48) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L58) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L65) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L71) | Configuration for network resource factories. | object({…}) | | {…} | | -| [gcp_ranges](variables.tf#L92) | GCP address ranges in name => range format. | map(string) | | {…} | | -| [network_mode](variables.tf#L109) | Selection of the network design to deploy. | string | | "simple" | | -| [outputs_location](variables.tf#L120) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L126) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | {} | | -| [regions](variables.tf#L146) | Region definitions. | object({…}) | | {…} | | -| [stage_config](variables-fast.tf#L86) | FAST stage configuration. | object({…}) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L100) | Root-level tag values. | map(string) | | {} | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L158) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | -| [vpn_onprem_secondary_config](variables.tf#L201) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | +| [factories_config](variables.tf#L71) | Configuration for network resource factories. | object({…}) | | {…} | | +| [gcp_ranges](variables.tf#L95) | GCP address ranges in name => range format. | map(string) | | {…} | | +| [network_mode](variables.tf#L112) | Selection of the network design to deploy. | string | | "simple" | | +| [outputs_location](variables.tf#L123) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L129) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | {} | | +| [regions](variables.tf#L149) | Region definitions. | object({…}) | | {…} | | +| [stage_config](variables-fast.tf#L92) | FAST stage configuration. | object({…}) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L106) | Root-level tag values. | map(string) | | {} | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L161) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_secondary_config](variables.tf#L204) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-c-separate-envs/README.md b/fast/stages/2-networking-c-separate-envs/README.md index 71d8487c66..757977d87a 100644 --- a/fast/stages/2-networking-c-separate-envs/README.md +++ b/fast/stages/2-networking-c-separate-envs/README.md @@ -360,22 +360,23 @@ Regions are defined via the `regions` variable which sets up a mapping between t |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L27) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [environments](variables-fast.tf#L49) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [folder_ids](variables-fast.tf#L66) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | -| [prefix](variables-fast.tf#L76) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [default_alerts_email](variables-fast.tf#L49) | Default email address for alerting. | string | ✓ | | | +| [environments](variables-fast.tf#L55) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [folder_ids](variables-fast.tf#L72) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | +| [prefix](variables-fast.tf#L82) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [custom_roles](variables-fast.tf#L40) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L42) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L53) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L60) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L66) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L87) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L93) | IP ranges used for Private Service Access (e.g. CloudSQL). | object({…}) | | {} | | -| [regions](variables.tf#L113) | Region definitions. | object({…}) | | {…} | | -| [stage_config](variables-fast.tf#L86) | FAST stage configuration. | object({…}) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L100) | Root-level tag values. | map(string) | | {} | 1-resman | -| [vpn_onprem_dev_primary_config](variables.tf#L123) | VPN gateway configuration for onprem interconnection from dev in the primary region. | object({…}) | | null | | -| [vpn_onprem_prod_primary_config](variables.tf#L166) | VPN gateway configuration for onprem interconnection from prod in the primary region. | object({…}) | | null | | +| [factories_config](variables.tf#L66) | Configuration for network resource factories. | object({…}) | | {…} | | +| [outputs_location](variables.tf#L90) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L96) | IP ranges used for Private Service Access (e.g. CloudSQL). | object({…}) | | {} | | +| [regions](variables.tf#L116) | Region definitions. | object({…}) | | {…} | | +| [stage_config](variables-fast.tf#L92) | FAST stage configuration. | object({…}) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L106) | Root-level tag values. | map(string) | | {} | 1-resman | +| [vpn_onprem_dev_primary_config](variables.tf#L126) | VPN gateway configuration for onprem interconnection from dev in the primary region. | object({…}) | | null | | +| [vpn_onprem_prod_primary_config](variables.tf#L169) | VPN gateway configuration for onprem interconnection from prod in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-project-factory/README.md b/fast/stages/2-project-factory/README.md index 159fa54fde..9edbbc1f66 100644 --- a/fast/stages/2-project-factory/README.md +++ b/fast/stages/2-project-factory/README.md @@ -354,14 +354,15 @@ The approach is not shown here but reasonably easy to implement. The main projec | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| | [billing_account](variables-fast.tf#L17) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables-fast.tf#L65) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | -| [factories_config](variables.tf#L17) | Configuration for YAML-based factories. | object({…}) | | {} | | -| [folder_ids](variables-fast.tf#L30) | Folders created in the resource management stage. | map(string) | | {} | 1-resman | -| [groups](variables-fast.tf#L38) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | map(string) | | {} | 0-bootstrap | -| [host_project_ids](variables-fast.tf#L47) | Host project for the shared VPC. | map(string) | | {} | 2-networking | -| [locations](variables-fast.tf#L55) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | -| [service_accounts](variables-fast.tf#L75) | Automation service accounts in name => email format. | map(string) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L83) | FAST-managed resource manager tag values. | map(string) | | {} | 1-resman | +| [default_alerts_email](variables-fast.tf#L30) | Default email address for alerting. | string | ✓ | | | +| [prefix](variables-fast.tf#L71) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [factories_config](variables.tf#L17) | Configuration for YAML-based factories. | object({…}) | | {} | | +| [folder_ids](variables-fast.tf#L36) | Folders created in the resource management stage. | map(string) | | {} | 1-resman | +| [groups](variables-fast.tf#L44) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | map(string) | | {} | 0-bootstrap | +| [host_project_ids](variables-fast.tf#L53) | Host project for the shared VPC. | map(string) | | {} | 2-networking | +| [locations](variables-fast.tf#L61) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | +| [service_accounts](variables-fast.tf#L81) | Automation service accounts in name => email format. | map(string) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L89) | FAST-managed resource manager tag values. | map(string) | | {} | 1-resman | ## Outputs diff --git a/fast/stages/2-security/README.md b/fast/stages/2-security/README.md index aa13a0a152..9faa028b36 100644 --- a/fast/stages/2-security/README.md +++ b/fast/stages/2-security/README.md @@ -299,18 +299,20 @@ tls_inspection = { |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [environments](variables-fast.tf#L47) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [folder_ids](variables-fast.tf#L64) | Folder name => id mappings, the 'security' folder name must exist. | object({…}) | ✓ | | 1-resman | -| [prefix](variables-fast.tf#L74) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [default_alerts_email](variables-fast.tf#L47) | Default email address for alerting. | string | ✓ | | | +| [environments](variables-fast.tf#L53) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [factories_config](variables.tf#L184) | Configuration for network resource factories. | object({…}) | ✓ | | | +| [folder_ids](variables-fast.tf#L70) | Folder name => id mappings, the 'security' folder name must exist. | object({…}) | ✓ | | 1-resman | +| [prefix](variables-fast.tf#L80) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | | [cas_configs](variables.tf#L17) | The CAS CAs to add to each environment. | object({…}) | | {…} | | | [custom_roles](variables-fast.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [essential_contacts](variables.tf#L178) | Email used for essential contacts, unset if null. | string | | null | | -| [kms_keys](variables.tf#L184) | KMS keys to create, keyed by name. | map(object({…})) | | {} | | -| [ngfw_tls_configs](variables.tf#L223) | The CAS and trust configurations key names to be used for NGFW Enterprise. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L249) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | -| [stage_config](variables-fast.tf#L84) | FAST stage configuration. | object({…}) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L98) | Root-level tag values. | map(string) | | {} | 1-resman | -| [trust_configs](variables.tf#L255) | The trust configs grouped by environment. | object({…}) | | {…} | | +| [kms_keys](variables.tf#L194) | KMS keys to create, keyed by name. | map(object({…})) | | {} | | +| [ngfw_tls_configs](variables.tf#L233) | The CAS and trust configurations key names to be used for NGFW Enterprise. | object({…}) | | {…} | | +| [outputs_location](variables.tf#L259) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | +| [stage_config](variables-fast.tf#L90) | FAST stage configuration. | object({…}) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L104) | Root-level tag values. | map(string) | | {} | 1-resman | +| [trust_configs](variables.tf#L265) | The trust configs grouped by environment. | object({…}) | | {…} | | ## Outputs diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index c2242c3a39..bb0ff1de75 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -440,7 +440,7 @@ update_rules: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [factories_config](variables.tf#L106) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | +| [factories_config](variables.tf#L106) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | | [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | object({…}) | | {} | | [data_merges](variables.tf#L54) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | | [data_overrides](variables.tf#L73) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | diff --git a/modules/project/README.md b/modules/project/README.md index 4db45d1135..46277cbc28 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -1366,11 +1366,13 @@ module "bucket" { | name | description | resources | |---|---|---| -| [alert-metrics.tf](./alert-metrics.tf) | None | google_logging_metric · google_monitoring_alert_policy · google_monitoring_notification_channel | +| [alerts-factory.tf](./alerts-factory.tf) | None | google_monitoring_alert_policy | | [cmek.tf](./cmek.tf) | Service Agent IAM Bindings for CMEK | google_kms_crypto_key_iam_member | | [iam.tf](./iam.tf) | IAM bindings. | google_project_iam_binding · google_project_iam_custom_role · google_project_iam_member | +| [logging-metrics-factory.tf](./logging-metrics-factory.tf) | None | google_logging_metric | | [logging.tf](./logging.tf) | Log sinks and supporting resources. | google_bigquery_dataset_iam_member · google_logging_project_exclusion · google_logging_project_sink · google_project_iam_audit_config · google_project_iam_member · google_pubsub_topic_iam_member · google_storage_bucket_iam_member | | [main.tf](./main.tf) | Module-level locals and resources. | google_compute_project_metadata_item · google_essential_contacts_contact · google_monitoring_monitored_project · google_project · google_project_service · google_resource_manager_lien | +| [notifications-factory.tf](./notifications-factory.tf) | None | google_monitoring_notification_channel | | [organization-policies.tf](./organization-policies.tf) | Project-level organization policies. | google_org_policy_policy | | [outputs.tf](./outputs.tf) | Module outputs. | | | [quotas.tf](./quotas.tf) | None | google_cloud_quotas_quota_preference | @@ -1389,44 +1391,46 @@ module "bucket" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L172) | Project name and id suffix. | string | ✓ | | +| [name](variables.tf#L174) | Project name and id suffix. | string | ✓ | | +| [alerts](variables-metrics-alerts.tf#L1) | Logging metrics alerts configuration. | map(object({…})) | | {} | | [auto_create_network](variables.tf#L17) | Whether to create the default network for the project. | bool | | false | | [billing_account](variables.tf#L23) | Billing account id. | string | | null | +| [channels](variables-metrics-alerts.tf#L135) | Logging metrics alerts configuration. | map(object({…})) | | {} | | [compute_metadata](variables.tf#L29) | Optional compute metadata key/values. Only usable if compute API has been enabled. | map(string) | | {} | | [contacts](variables.tf#L36) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | | [custom_roles](variables.tf#L43) | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | -| [default_alerts_email](variables.tf#L81) | Default email address for alerting. | string | | null | -| [default_service_account](variables.tf#L50) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | string | | "keep" | -| [deletion_policy](variables.tf#L64) | Deletion policy setting for this project. | string | | "DELETE" | -| [descriptive_name](variables.tf#L75) | Name of the project name. Used for project name instead of `name` variable. | string | | null | -| [factories_config](variables.tf#L87) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | +| [default_alerts_email](variables.tf#L50) | Default email address for alerting. | string | | null | +| [default_service_account](variables.tf#L56) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | string | | "keep" | +| [deletion_policy](variables.tf#L70) | Deletion policy setting for this project. | string | | "DELETE" | +| [descriptive_name](variables.tf#L81) | Name of the project name. Used for project name instead of `name` variable. | string | | null | +| [factories_config](variables.tf#L87) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | | [iam](variables-iam.tf#L17) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | | [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | -| [labels](variables.tf#L99) | Resource labels. | map(string) | | {} | -| [lien_reason](variables.tf#L106) | If non-empty, creates a project lien with this description. | string | | null | -| [logging_data_access](variables.tf#L112) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | -| [logging_exclusions](variables.tf#L127) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | -| [logging_metrics_alerts](variables-metrics-alerts.tf#L1) | Logging metrics alerts configuration. | map(object({…})) | | {} | -| [logging_sinks](variables.tf#L134) | Logging sinks to create for this project. | map(object({…})) | | {} | -| [metric_scopes](variables.tf#L165) | List of projects that will act as metric scopes for this project. | list(string) | | [] | +| [labels](variables.tf#L101) | Resource labels. | map(string) | | {} | +| [lien_reason](variables.tf#L108) | If non-empty, creates a project lien with this description. | string | | null | +| [logging_data_access](variables.tf#L114) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | +| [logging_exclusions](variables.tf#L129) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | +| [logging_metrics](variables-metrics-alerts.tf#L96) | Logging metrics alerts configuration. | map(object({…})) | | {} | +| [logging_sinks](variables.tf#L136) | Logging sinks to create for this project. | map(object({…})) | | {} | +| [metric_scopes](variables.tf#L167) | List of projects that will act as metric scopes for this project. | list(string) | | [] | | [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [org_policies](variables.tf#L177) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | -| [parent](variables.tf#L204) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | -| [prefix](variables.tf#L214) | Optional prefix used to generate project id and name. | string | | null | -| [project_create](variables.tf#L224) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | +| [org_policies](variables.tf#L179) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | +| [parent](variables.tf#L206) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +| [prefix](variables.tf#L216) | Optional prefix used to generate project id and name. | string | | null | +| [project_create](variables.tf#L226) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | | [quotas](variables-quotas.tf#L17) | Service quota configuration. | map(object({…})) | | {} | -| [service_agents_config](variables.tf#L230) | Automatic service agent configuration options. | object({…}) | | {} | -| [service_config](variables.tf#L241) | Configure service API activation. | object({…}) | | {…} | -| [service_encryption_key_ids](variables.tf#L253) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | -| [services](variables.tf#L260) | Service APIs to enable. | list(string) | | [] | -| [shared_vpc_host_config](variables.tf#L266) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | -| [shared_vpc_service_config](variables.tf#L275) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | -| [skip_delete](variables.tf#L303) | Deprecated. Use deletion_policy. | bool | | null | +| [service_agents_config](variables.tf#L232) | Automatic service agent configuration options. | object({…}) | | {} | +| [service_config](variables.tf#L243) | Configure service API activation. | object({…}) | | {…} | +| [service_encryption_key_ids](variables.tf#L255) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | +| [services](variables.tf#L262) | Service APIs to enable. | list(string) | | [] | +| [shared_vpc_host_config](variables.tf#L268) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | +| [shared_vpc_service_config](variables.tf#L277) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | +| [skip_delete](variables.tf#L305) | Deprecated. Use deletion_policy. | bool | | null | | [tag_bindings](variables-tags.tf#L81) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | | [tags](variables-tags.tf#L88) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [vpc_sc](variables.tf#L315) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | +| [vpc_sc](variables.tf#L317) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | ## Outputs diff --git a/modules/project/variables.tf b/modules/project/variables.tf index fb3759ee81..8234c530ce 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -47,6 +47,12 @@ variable "custom_roles" { nullable = false } +variable "default_alerts_email" { + description = "Default email address for alerting." + type = string + default = null +} + variable "default_service_account" { description = "Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`." default = "keep" @@ -78,12 +84,6 @@ variable "descriptive_name" { default = null } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "factories_config" { description = "Paths to data files and folders that enable factory functionality." type = object({ From a81081a431e707ed024edaaae919dc59663cba71 Mon Sep 17 00:00:00 2001 From: Joshua Wright <19779568+joshw123@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:34:43 +0000 Subject: [PATCH 07/33] Update modules/project/alerts-factory.tf Co-authored-by: Julio Castillo --- modules/project/alerts-factory.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/project/alerts-factory.tf b/modules/project/alerts-factory.tf index aa119dbfde..b8abfeaaa8 100644 --- a/modules/project/alerts-factory.tf +++ b/modules/project/alerts-factory.tf @@ -40,7 +40,7 @@ locals { documentation = null }, v) } - alerts = merge(local._alerts_factory_data, var.logging_metrics) + alerts = merge(local._alerts_factory_data, var.alerts) } resource "google_monitoring_alert_policy" "default" { From c5f25d0e0bb108b0cc666250d6df2bb6380eef2b Mon Sep 17 00:00:00 2001 From: Joshua Wright <19779568+joshw123@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:35:38 +0000 Subject: [PATCH 08/33] Update fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml Co-authored-by: Julio Castillo --- fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml b/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml index 6b2d95b2da..cf6a7f208f 100644 --- a/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml +++ b/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml @@ -1,6 +1,7 @@ route-changes: description: "Monitor VPC network route configuration changes inside GCP projects" - filter: "resource.type=\"gce_route\" AND (protoPayload.methodName:\"compute.routes.delete\" OR protoPayload.methodName:\"compute.routes.insert\")" + filter: | + resource.type="gce_route" AND (protoPayload.methodName:"compute.routes.delete" OR protoPayload.methodName:"compute.routes.insert") metric_descriptor: metric_kind: DELTA value_type: "INT64" From a832ddb28880d214f38c133afc2045cdc84baa68 Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Fri, 13 Dec 2024 12:49:44 +0000 Subject: [PATCH 09/33] Update Tests, Resources --- fast/stages/0-bootstrap/billing.tf | 16 +- .../0-bootstrap/data/alerts/compliance.yaml | 14 ++ .../0-bootstrap/data/channels/compliance.yaml | 14 ++ .../logging-alerts/compliance-alerts.yaml | 13 - .../data/logging-metrics/compliance.yaml | 14 ++ fast/stages/0-bootstrap/log-export.tf | 10 +- fast/stages/0-bootstrap/variables.tf | 8 +- fast/stages/1-resman/tenant-root.tf | 6 +- fast/stages/1-resman/variables-fast.tf | 2 +- fast/stages/1-resman/variables.tf | 6 +- .../2-network-security/variables-fast.tf | 2 +- fast/stages/2-network-security/variables.tf | 6 +- fast/stages/2-networking-a-simple/net-dev.tf | 10 +- .../2-networking-a-simple/net-landing.tf | 12 +- fast/stages/2-networking-a-simple/net-prod.tf | 10 +- .../2-networking-a-simple/variables-fast.tf | 2 +- .../stages/2-networking-a-simple/variables.tf | 4 +- fast/stages/2-networking-b-nva/net-dev.tf | 10 +- fast/stages/2-networking-b-nva/net-landing.tf | 10 +- fast/stages/2-networking-b-nva/net-prod.tf | 10 +- .../2-networking-b-nva/variables-fast.tf | 2 +- fast/stages/2-networking-b-nva/variables.tf | 4 +- .../2-networking-c-separate-envs/net-dev.tf | 10 +- .../2-networking-c-separate-envs/net-prod.tf | 10 +- .../variables-fast.tf | 2 +- .../2-networking-c-separate-envs/variables.tf | 4 +- .../2-project-factory/variables-fast.tf | 2 +- fast/stages/2-project-factory/variables.tf | 10 +- fast/stages/2-security/variables-fast.tf | 2 +- fast/stages/2-security/variables.tf | 4 +- modules/project-factory/main.tf | 10 +- modules/project-factory/variables.tf | 10 +- modules/project/README.md | 203 +++++++++++++++ .../project/{alerts-factory.tf => alerts.tf} | 2 +- ...-metrics-factory.tf => logging-metrics.tf} | 0 ...ifications-factory.tf => notifications.tf} | 2 +- modules/project/variables-metrics-alerts.tf | 153 ------------ modules/project/variables-observability.tf | 236 ++++++++++++++++++ modules/project/variables.tf | 6 +- tests/fast/stages/s0_bootstrap/simple.yaml | 5 +- .../stages/s2_network_security/simple.yaml | 5 +- .../fast/stages/s2_network_security/tls.yaml | 5 +- .../stages/s2_networking_a_simple/ncc.yaml | 6 +- .../stages/s2_networking_a_simple/simple.yaml | 6 +- .../stages/s2_networking_a_simple/vpn.yaml | 6 +- .../stages/s2_networking_b_nva/ncc-ra.yaml | 6 +- .../stages/s2_networking_b_nva/regional.yaml | 6 +- .../stages/s2_networking_b_nva/simple.yaml | 6 +- .../s2_networking_c_separate_envs/simple.yaml | 6 +- .../stages/s2_project_factory/simple.yaml | 5 +- tests/fast/stages/s2_security/simple.yaml | 3 + .../project_factory/examples/example.yaml | 4 +- 52 files changed, 632 insertions(+), 288 deletions(-) delete mode 100644 fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml rename modules/project/{alerts-factory.tf => alerts.tf} (99%) rename modules/project/{logging-metrics-factory.tf => logging-metrics.tf} (100%) rename modules/project/{notifications-factory.tf => notifications.tf} (96%) delete mode 100644 modules/project/variables-metrics-alerts.tf create mode 100644 modules/project/variables-observability.tf diff --git a/fast/stages/0-bootstrap/billing.tf b/fast/stages/0-bootstrap/billing.tf index 17135efcde..5dd0a29552 100644 --- a/fast/stages/0-bootstrap/billing.tf +++ b/fast/stages/0-bootstrap/billing.tf @@ -38,14 +38,9 @@ locals { # billing account in same org (IAM is in the organization.tf file) module "billing-export-project" { - source = "../../../modules/project" - count = local.billing_mode == "org" ? 1 : 0 - billing_account = var.billing_account.id - factories_config = { - alerts = var.factories_config.alerts - channels = var.factories_config.channels - logging_metrics = var.factories_config.logging_metrics - } + source = "../../../modules/project" + count = local.billing_mode == "org" ? 1 : 0 + billing_account = var.billing_account.id default_alerts_email = var.default_alerts_email name = "billing-exp-0" parent = coalesce( @@ -57,6 +52,11 @@ module "billing-export-project" { ? {} : { (var.essential_contacts) = ["ALL"] } ) + factories_config = { + alerts = var.factories_config.alerts + channels = var.factories_config.channels + logging_metrics = var.factories_config.logging_metrics + } iam = { "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] "roles/viewer" = [module.automation-tf-bootstrap-r-sa.iam_email] diff --git a/fast/stages/0-bootstrap/data/alerts/compliance.yaml b/fast/stages/0-bootstrap/data/alerts/compliance.yaml index 117887fb02..a9830b8ccc 100644 --- a/fast/stages/0-bootstrap/data/alerts/compliance.yaml +++ b/fast/stages/0-bootstrap/data/alerts/compliance.yaml @@ -1,3 +1,17 @@ +# Copyright 2024 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. + route-changes: display_name: "Network Route Changes" combiner: OR diff --git a/fast/stages/0-bootstrap/data/channels/compliance.yaml b/fast/stages/0-bootstrap/data/channels/compliance.yaml index c256694908..8d3298a016 100644 --- a/fast/stages/0-bootstrap/data/channels/compliance.yaml +++ b/fast/stages/0-bootstrap/data/channels/compliance.yaml @@ -1,3 +1,17 @@ +# Copyright 2024 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. + compliance-default: type: email display_name: "Default Email Notifications" diff --git a/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml b/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml deleted file mode 100644 index b2f7f7d196..0000000000 --- a/fast/stages/0-bootstrap/data/logging-alerts/compliance-alerts.yaml +++ /dev/null @@ -1,13 +0,0 @@ - - -iam-owner-changes: - display_name: "Owner IAM Configuration Changes" - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - trigger_count: 1 - combiner: OR - aggregations: - per_series_aligner: ALIGN_DELTA - cross_series_reducer: REDUCE_SUM - alignment_period: "600s" diff --git a/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml b/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml index cf6a7f208f..88724f2b67 100644 --- a/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml +++ b/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml @@ -1,3 +1,17 @@ +# Copyright 2024 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. + route-changes: description: "Monitor VPC network route configuration changes inside GCP projects" filter: | diff --git a/fast/stages/0-bootstrap/log-export.tf b/fast/stages/0-bootstrap/log-export.tf index ba220c3b49..9e1d330d5a 100644 --- a/fast/stages/0-bootstrap/log-export.tf +++ b/fast/stages/0-bootstrap/log-export.tf @@ -42,11 +42,6 @@ module "log-export-project" { parent = coalesce( var.project_parent_ids.logging, "organizations/${var.organization.id}" ) - factories_config = { - alerts = var.factories_config.alerts - channels = var.factories_config.channels - logging_metrics = var.factories_config.logging_metrics - } default_alerts_email = var.default_alerts_email prefix = local.prefix billing_account = var.billing_account.id @@ -55,6 +50,11 @@ module "log-export-project" { ? {} : { (var.essential_contacts) = ["ALL"] } ) + factories_config = { + alerts = var.factories_config.alerts + channels = var.factories_config.channels + logging_metrics = var.factories_config.logging_metrics + } iam = { "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] "roles/viewer" = [module.automation-tf-bootstrap-r-sa.iam_email] diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index 8c3ae0b75b..9f41b44844 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -94,7 +94,7 @@ variable "custom_roles" { variable "default_alerts_email" { description = "Default email address for alerting." type = string - nullable = false + default = null } variable "environments" { @@ -139,12 +139,12 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for the resource factories or external data." type = object({ + alerts = optional(string, "data/alerts") + channels = optional(string, "data/channels") custom_roles = optional(string, "data/custom-roles") + logging_metrics = optional(string, "data/logging-metrics") org_policies = optional(string, "data/org-policies") org_policies_iac = optional(string, "data/org-policies-iac") - logging_metrics = optional(string, "data/logging-metrics") - channels = optional(string, "data/channels") - alerts = optional(string, "data/alerts") }) nullable = false default = {} diff --git a/fast/stages/1-resman/tenant-root.tf b/fast/stages/1-resman/tenant-root.tf index 2805d68ca5..2a33baf875 100644 --- a/fast/stages/1-resman/tenant-root.tf +++ b/fast/stages/1-resman/tenant-root.tf @@ -38,12 +38,12 @@ module "automation-project" { project_create = false # do not assign tagViewer or tagUser roles here on tag keys and values as # they are managed authoritatively and will break multitenant stages + default_alerts_email = var.default_alerts_email factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels alerts = var.factories_config.alerts + channels = var.factories_config.channels + logging_metrics = var.factories_config.logging_metrics } - default_alerts_email = var.default_alerts_email tags = merge(local.tags, { (var.tag_names.context) = { description = "Resource management context." diff --git a/fast/stages/1-resman/variables-fast.tf b/fast/stages/1-resman/variables-fast.tf index 2501ce33dd..f5ae8037b7 100644 --- a/fast/stages/1-resman/variables-fast.tf +++ b/fast/stages/1-resman/variables-fast.tf @@ -71,7 +71,7 @@ variable "custom_roles" { variable "default_alerts_email" { description = "Default email address for alerting." type = string - nullable = false + default = null } variable "environments" { diff --git a/fast/stages/1-resman/variables.tf b/fast/stages/1-resman/variables.tf index 3c8fe2e2fc..21427e4115 100644 --- a/fast/stages/1-resman/variables.tf +++ b/fast/stages/1-resman/variables.tf @@ -20,12 +20,12 @@ variable "factories_config" { description = "Configuration for the resource factories or external data." type = object({ + alerts = optional(string, "data/alerts") + channels = optional(string, "data/channels") + logging_metrics = optional(string, "data/logging-metrics") org_policies = optional(string, "data/org-policies") stage_3 = optional(string, "data/stage-3") top_level_folders = optional(string, "data/top-level-folders") - logging_metrics = optional(string, "data/logging-metrics") - channels = optional(string, "data/channels") - alerts = optional(string, "data/alerts") }) nullable = false default = {} diff --git a/fast/stages/2-network-security/variables-fast.tf b/fast/stages/2-network-security/variables-fast.tf index 104b3ebf7b..5419e1a343 100644 --- a/fast/stages/2-network-security/variables-fast.tf +++ b/fast/stages/2-network-security/variables-fast.tf @@ -40,7 +40,7 @@ variable "billing_account" { variable "default_alerts_email" { description = "Default email address for alerting." type = string - nullable = false + default = null } variable "folder_ids" { diff --git a/fast/stages/2-network-security/variables.tf b/fast/stages/2-network-security/variables.tf index 4528f9a1b0..353a3e2568 100644 --- a/fast/stages/2-network-security/variables.tf +++ b/fast/stages/2-network-security/variables.tf @@ -17,14 +17,14 @@ variable "factories_config" { description = "Configuration for network resource factories." type = object({ - cidrs = optional(string, "data/cidrs.yaml") + alerts = optional(string, "data/alerts") + channels = optional(string, "data/channels") + cidrs = optional(string, "data/cidrs.yaml") firewall_policy_rules = optional(object({ dev = string prod = string })) logging_metrics = optional(string, "data/logging-metrics") - channels = optional(string, "data/channels") - alerts = optional(string, "data/alerts") }) nullable = false default = { diff --git a/fast/stages/2-networking-a-simple/net-dev.tf b/fast/stages/2-networking-a-simple/net-dev.tf index fc79b1721e..5bf66a37ba 100644 --- a/fast/stages/2-networking-a-simple/net-dev.tf +++ b/fast/stages/2-networking-a-simple/net-dev.tf @@ -20,17 +20,17 @@ module "dev-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "dev-net-spoke-0" + parent = coalesce( + var.folder_ids.networking-dev, + var.folder_ids.networking + ) + prefix = var.prefix factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email - parent = coalesce( - var.folder_ids.networking-dev, - var.folder_ids.networking - ) - prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-a-simple/net-landing.tf b/fast/stages/2-networking-a-simple/net-landing.tf index f602ec5b79..5e544a2ed2 100644 --- a/fast/stages/2-networking-a-simple/net-landing.tf +++ b/fast/stages/2-networking-a-simple/net-landing.tf @@ -20,12 +20,6 @@ module "landing-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-landing-0" - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } - default_alerts_email = var.default_alerts_email parent = coalesce( var.folder_ids.networking-prod, var.folder_ids.networking @@ -39,6 +33,12 @@ module "landing-project" { "networkmanagement.googleapis.com", "stackdriver.googleapis.com" ] + factories_config = { + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts + } + default_alerts_email = var.default_alerts_email shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-a-simple/net-prod.tf b/fast/stages/2-networking-a-simple/net-prod.tf index 28093e210f..0f271456f8 100644 --- a/fast/stages/2-networking-a-simple/net-prod.tf +++ b/fast/stages/2-networking-a-simple/net-prod.tf @@ -20,17 +20,17 @@ module "prod-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-spoke-0" + parent = coalesce( + var.folder_ids.networking-prod, + var.folder_ids.networking + ) + prefix = var.prefix factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email - parent = coalesce( - var.folder_ids.networking-prod, - var.folder_ids.networking - ) - prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-a-simple/variables-fast.tf b/fast/stages/2-networking-a-simple/variables-fast.tf index dcb51d8ffb..070c57a4af 100644 --- a/fast/stages/2-networking-a-simple/variables-fast.tf +++ b/fast/stages/2-networking-a-simple/variables-fast.tf @@ -49,7 +49,7 @@ variable "custom_roles" { variable "default_alerts_email" { description = "Default email address for alerting." type = string - nullable = false + default = null } variable "environments" { diff --git a/fast/stages/2-networking-a-simple/variables.tf b/fast/stages/2-networking-a-simple/variables.tf index a7c0e272c4..dc6f754bc4 100644 --- a/fast/stages/2-networking-a-simple/variables.tf +++ b/fast/stages/2-networking-a-simple/variables.tf @@ -71,12 +71,12 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ + alerts = optional(string, "data/alerts") + channels = optional(string, "data/channels") data_dir = optional(string, "data") dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") firewall_policy_name = optional(string, "net-default") logging_metrics = optional(string, "data/logging-metrics") - channels = optional(string, "data/channels") - alerts = optional(string, "data/alerts") }) default = { data_dir = "data" diff --git a/fast/stages/2-networking-b-nva/net-dev.tf b/fast/stages/2-networking-b-nva/net-dev.tf index 91b25eada4..0093e6bd0b 100644 --- a/fast/stages/2-networking-b-nva/net-dev.tf +++ b/fast/stages/2-networking-b-nva/net-dev.tf @@ -20,17 +20,17 @@ module "dev-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "dev-net-spoke-0" + parent = coalesce( + var.folder_ids.networking-dev, + var.folder_ids.networking + ) + prefix = var.prefix factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email - parent = coalesce( - var.folder_ids.networking-dev, - var.folder_ids.networking - ) - prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-b-nva/net-landing.tf b/fast/stages/2-networking-b-nva/net-landing.tf index 88f250391e..3746d90f4d 100644 --- a/fast/stages/2-networking-b-nva/net-landing.tf +++ b/fast/stages/2-networking-b-nva/net-landing.tf @@ -20,17 +20,17 @@ module "landing-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-landing-0" + parent = coalesce( + var.folder_ids.networking-prod, + var.folder_ids.networking + ) + prefix = var.prefix factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email - parent = coalesce( - var.folder_ids.networking-prod, - var.folder_ids.networking - ) - prefix = var.prefix services = [ "compute.googleapis.com", "dns.googleapis.com", diff --git a/fast/stages/2-networking-b-nva/net-prod.tf b/fast/stages/2-networking-b-nva/net-prod.tf index 4a50fc6e72..d22e74b89e 100644 --- a/fast/stages/2-networking-b-nva/net-prod.tf +++ b/fast/stages/2-networking-b-nva/net-prod.tf @@ -20,17 +20,17 @@ module "prod-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-spoke-0" + parent = coalesce( + var.folder_ids.networking-prod, + var.folder_ids.networking + ) + prefix = var.prefix factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email - parent = coalesce( - var.folder_ids.networking-prod, - var.folder_ids.networking - ) - prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-b-nva/variables-fast.tf b/fast/stages/2-networking-b-nva/variables-fast.tf index dcb51d8ffb..070c57a4af 100644 --- a/fast/stages/2-networking-b-nva/variables-fast.tf +++ b/fast/stages/2-networking-b-nva/variables-fast.tf @@ -49,7 +49,7 @@ variable "custom_roles" { variable "default_alerts_email" { description = "Default email address for alerting." type = string - nullable = false + default = null } variable "environments" { diff --git a/fast/stages/2-networking-b-nva/variables.tf b/fast/stages/2-networking-b-nva/variables.tf index 038962e504..6bc49cfc4c 100644 --- a/fast/stages/2-networking-b-nva/variables.tf +++ b/fast/stages/2-networking-b-nva/variables.tf @@ -71,12 +71,12 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ + alerts = optional(string, "data/alerts") + channels = optional(string, "data/channels") data_dir = optional(string, "data") dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") firewall_policy_name = optional(string, "net-default") logging_metrics = optional(string, "data/logging-metrics") - channels = optional(string, "data/channels") - alerts = optional(string, "data/alerts") }) default = { data_dir = "data" diff --git a/fast/stages/2-networking-c-separate-envs/net-dev.tf b/fast/stages/2-networking-c-separate-envs/net-dev.tf index 7cdcbacae5..7b9f171e9b 100644 --- a/fast/stages/2-networking-c-separate-envs/net-dev.tf +++ b/fast/stages/2-networking-c-separate-envs/net-dev.tf @@ -20,17 +20,17 @@ module "dev-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "dev-net-spoke-0" + parent = coalesce( + var.folder_ids.networking-dev, + var.folder_ids.networking + ) + prefix = var.prefix factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email - parent = coalesce( - var.folder_ids.networking-dev, - var.folder_ids.networking - ) - prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-c-separate-envs/net-prod.tf b/fast/stages/2-networking-c-separate-envs/net-prod.tf index a058b3aea4..8a7a0e9a96 100644 --- a/fast/stages/2-networking-c-separate-envs/net-prod.tf +++ b/fast/stages/2-networking-c-separate-envs/net-prod.tf @@ -20,17 +20,17 @@ module "prod-spoke-project" { source = "../../../modules/project" billing_account = var.billing_account.id name = "prod-net-spoke-0" + parent = coalesce( + var.folder_ids.networking-prod, + var.folder_ids.networking + ) + prefix = var.prefix factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } default_alerts_email = var.default_alerts_email - parent = coalesce( - var.folder_ids.networking-prod, - var.folder_ids.networking - ) - prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-c-separate-envs/variables-fast.tf b/fast/stages/2-networking-c-separate-envs/variables-fast.tf index dcb51d8ffb..070c57a4af 100644 --- a/fast/stages/2-networking-c-separate-envs/variables-fast.tf +++ b/fast/stages/2-networking-c-separate-envs/variables-fast.tf @@ -49,7 +49,7 @@ variable "custom_roles" { variable "default_alerts_email" { description = "Default email address for alerting." type = string - nullable = false + default = null } variable "environments" { diff --git a/fast/stages/2-networking-c-separate-envs/variables.tf b/fast/stages/2-networking-c-separate-envs/variables.tf index 7c3da6907a..29167801f7 100644 --- a/fast/stages/2-networking-c-separate-envs/variables.tf +++ b/fast/stages/2-networking-c-separate-envs/variables.tf @@ -66,12 +66,12 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ + alerts = optional(string, "data/alerts") + channels = optional(string, "data/channels") data_dir = optional(string, "data") dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") firewall_policy_name = optional(string, "net-default") logging_metrics = optional(string, "data/logging-metrics") - channels = optional(string, "data/channels") - alerts = optional(string, "data/alerts") }) default = { data_dir = "data" diff --git a/fast/stages/2-project-factory/variables-fast.tf b/fast/stages/2-project-factory/variables-fast.tf index 97bd5fd8be..304100e902 100644 --- a/fast/stages/2-project-factory/variables-fast.tf +++ b/fast/stages/2-project-factory/variables-fast.tf @@ -30,7 +30,7 @@ variable "billing_account" { variable "default_alerts_email" { description = "Default email address for alerting." type = string - nullable = false + default = null } variable "folder_ids" { diff --git a/fast/stages/2-project-factory/variables.tf b/fast/stages/2-project-factory/variables.tf index ffaa69cbb6..a551c9c72b 100644 --- a/fast/stages/2-project-factory/variables.tf +++ b/fast/stages/2-project-factory/variables.tf @@ -17,11 +17,7 @@ variable "factories_config" { description = "Configuration for YAML-based factories." type = object({ - folders_data_path = optional(string, "data/hierarchy") - projects_data_path = optional(string, "data/projects") - logging_metrics = optional(string, "data/logging-metrics") - channels = optional(string, "data/channels") - alerts = optional(string, "data/alerts") + alerts = optional(string, "data/alerts") budgets = optional(object({ billing_account = string budgets_data_path = optional(string, "data/budgets") @@ -34,6 +30,10 @@ variable "factories_config" { tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) }), {}) + channels = optional(string, "data/channels") + folders_data_path = optional(string, "data/hierarchy") + logging_metrics = optional(string, "data/logging-metrics") + projects_data_path = optional(string, "data/projects") }) nullable = false default = {} diff --git a/fast/stages/2-security/variables-fast.tf b/fast/stages/2-security/variables-fast.tf index 097ff492b0..53590c4b81 100644 --- a/fast/stages/2-security/variables-fast.tf +++ b/fast/stages/2-security/variables-fast.tf @@ -47,7 +47,7 @@ variable "custom_roles" { variable "default_alerts_email" { description = "Default email address for alerting." type = string - nullable = false + default = null } variable "environments" { diff --git a/fast/stages/2-security/variables.tf b/fast/stages/2-security/variables.tf index 0f735e1db3..ef31df9265 100644 --- a/fast/stages/2-security/variables.tf +++ b/fast/stages/2-security/variables.tf @@ -184,9 +184,9 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - logging_metrics = optional(string, "data/logging-metrics") - channels = optional(string, "data/channels") alerts = optional(string, "data/alerts") + channels = optional(string, "data/channels") + logging_metrics = optional(string, "data/logging-metrics") }) nullable = false } diff --git a/modules/project-factory/main.tf b/modules/project-factory/main.tf index 9dbb4839c8..8f33016022 100644 --- a/modules/project-factory/main.tf +++ b/modules/project-factory/main.tf @@ -40,11 +40,6 @@ module "projects" { parent = lookup( local.context.folder_ids, each.value.parent, each.value.parent ) - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } default_alerts_email = "" prefix = each.value.prefix auto_create_network = try(each.value.auto_create_network, false) @@ -53,6 +48,11 @@ module "projects" { contacts = merge( each.value.contacts, var.data_merges.contacts ) + factories_config = { + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts + } default_service_account = try(each.value.default_service_account, "keep") descriptive_name = try(each.value.descriptive_name, null) iam = { diff --git a/modules/project-factory/variables.tf b/modules/project-factory/variables.tf index 60adf0d0d5..721bfc3e4a 100644 --- a/modules/project-factory/variables.tf +++ b/modules/project-factory/variables.tf @@ -106,17 +106,14 @@ variable "default_alerts_email" { variable "factories_config" { description = "Path to folder with YAML resource description data files." type = object({ - folders_data_path = optional(string) - projects_data_path = optional(string) - logging_metrics = optional(string) - channels = optional(string) - alerts = optional(string) + alerts = optional(string) budgets = optional(object({ billing_account = string budgets_data_path = string # TODO: allow defining notification channels via YAML files notification_channels = optional(map(any), {}) })) + channels = optional(string) context = optional(object({ # TODO: add KMS keys folder_ids = optional(map(string), {}) @@ -124,6 +121,9 @@ variable "factories_config" { tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) }), {}) + folders_data_path = optional(string) + projects_data_path = optional(string) + logging_metrics = optional(string) }) nullable = false } diff --git a/modules/project/README.md b/modules/project/README.md index 46277cbc28..59bd9811f6 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -1360,8 +1360,211 @@ module "bucket" { # tftest inventory=data.yaml e2e ``` +## Notification Channels + +Project Notification Channels can be managed via the `notification_channels` variable +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + notification_channels = { + channel-1 = { + display_name = "channel-1" + enabled = true + type = "email" + labels = { + email_address = "hello@company.com" + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] + } + } + } +} +``` + +## Notification Channels factory + +Notification Channels can be also specified via a factory in a similar way to organization policies, policy constraints and custom roles by pointing to a directory containing YAML files where each file defines one or more Notification Channels. The structure of the YAML files is exactly the same as the `notification_channels` variable. + +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + factories_config = { + notification_channels = "data/notification_channels" + } + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] +} +``` + +```yaml +channel-1: + display_name: channel-1 + enabled: true + type: email + labels: + email_address: hello@company.com +``` + + + + +## Alerts + +Project alerts can be managed via the `alerts` variable +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + alerts = { + alert-1 = { + display_name = "alert-1" + enabled = true + conditions = { + display_name = "condition-1" + condition_threshold = { + filter = "metric.type=\"compute.googleapis.com/instance/disk/write_bytes_count\"" + comparison = "COMPARISON_GT" + threshold_value = 100 + duration = "60s" + aggregations = { + alignment_period = "60s" + per_series_aligner = "ALIGN_RATE" + } + } + } + notification_channels = ["projects/${var.project_id}/notificationChannels/${channel_id}"] + } + } + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] +} +``` + +Notification channels can also reference notification channels created in the same module, just specify the channel name in the `notification_channels` attribute e.g. + +```hcl +notification_channels = ["channel-1"] +``` + +## Alerts factory + +Alerts can be also specified via a factory in a similar way to organization policies, policy constraints and custom roles by pointing to a directory containing YAML files where each file defines one or more Alerts. The structure of the YAML files is exactly the same as the `Alerts` variable. + +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + factories_config = { + alerts = "data/alerts" + } + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] +} +``` + +```yaml +alert-1: + display_name: alert-1 + enabled: true + conditions: + display_name: condition-1 + condition_threshold: + filter: metric.type="compute.googleapis.com/instance/disk/write_bytes_count" + comparison: COMPARISON_GT + threshold_value: 100 + duration: 60s + aggregations: + alignment_period: 60s + per_series_aligner: ALIGN_RATE + notification_channels: + - projects/${var.project_id}/notificationChannels/${channel_id} +``` +## Logging Metrics + +Project Logging Based Metrics can be managed via the `logging_metrics` variable +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + logging_metrics = { + metric-1 = { + name = "metric-1" + filter = "resource.type=\"gce_instance\"" + description = "This is a metric" + metric_descriptor = { + metric_kind = "GAUGE" + value_type = "DOUBLE" + unit = "ms" + } + } + } + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] +} +``` + +## Logging Metrics factory + +Logging Metrics can be also specified via a factory in a similar way to organization policies, policy constraints and custom roles by pointing to a directory containing YAML files where each file defines one or more Logging Metrics. The structure of the YAML files is exactly the same as the `logging_metrics` variable. + +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + factories_config = { + logging_metrics = "data/logging_metrics" + } + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] +} +``` + +```yaml +metric-1: + name: metric-1 + filter: resource.type="gce_instance" + description: This is a metric + metric_descriptor: + metric_kind: GAUGE + value_type: DOUBLE + unit: ms +``` + + ## Files | name | description | resources | diff --git a/modules/project/alerts-factory.tf b/modules/project/alerts.tf similarity index 99% rename from modules/project/alerts-factory.tf rename to modules/project/alerts.tf index b8abfeaaa8..ba23e9056b 100644 --- a/modules/project/alerts-factory.tf +++ b/modules/project/alerts.tf @@ -125,7 +125,7 @@ resource "google_monitoring_alert_policy" "default" { notification_channels = [ try(google_monitoring_notification_channel.this[each.value.notification_channels].id, each.value.notification_channels), - try(google_monitoring_notification_channel.default[0].id, null) + try(google_monitoring_notification_channel.default[0].id, "") ] dynamic "alert_strategy" { for_each = lookup(each.value, "alert_strategy", null)[*] diff --git a/modules/project/logging-metrics-factory.tf b/modules/project/logging-metrics.tf similarity index 100% rename from modules/project/logging-metrics-factory.tf rename to modules/project/logging-metrics.tf diff --git a/modules/project/notifications-factory.tf b/modules/project/notifications.tf similarity index 96% rename from modules/project/notifications-factory.tf rename to modules/project/notifications.tf index 7d1914bfc4..82c1ac40cb 100644 --- a/modules/project/notifications-factory.tf +++ b/modules/project/notifications.tf @@ -33,7 +33,7 @@ locals { description = null }, v) } - channels = merge(local._channels_factory_data, var.channels) + channels = merge(local._channels_factory_data, var.notification_channels) } resource "google_monitoring_notification_channel" "default" { diff --git a/modules/project/variables-metrics-alerts.tf b/modules/project/variables-metrics-alerts.tf deleted file mode 100644 index 2a450df851..0000000000 --- a/modules/project/variables-metrics-alerts.tf +++ /dev/null @@ -1,153 +0,0 @@ -variable "alerts" { - description = "Logging metrics alerts configuration." - type = map(object({ - name = optional(string) - description = optional(string) - filter = optional(string) - display_name = optional(string) - condition_threshold = optional(map(string)) - trigger_count = optional(number) - combiner = optional(string) - notification_channels = optional(list(string)) - documentation = optional(object({ - content = optional(string) - mime_type = optional(string) - subject = optional(string) - links = optional(list(object({ - display_name = optional(string) - url = optional(string) - }))) - })) - alert_strategy = optional(object({ - auto_close = optional(string) - notification_prompts = optional(string) - notification_rate_limit = optional(object({ - period = optional(string) - })) - notification_channel_strategy = optional(object({ - notification_channel_names = optional(list(string)) - renotify_interval = optional(string) - })) - })) - conditions = optional(object({ - display_name = optional(string) - condition_matched_log = optional(object({ - filter = optional(string) - label_extractors = optional(map(string)) - })) - condition_monitoring_query_language = optional(object({ - query = optional(string) - duration = optional(string) - trigger = optional(object({ - count = optional(number) - percent = optional(number) - })) - evaluation_missing_data = optional(string) - })) - condition_threshold = optional(object({ - threshold_value = optional(number) - denominator_filter = optional(string) - denominator_aggregations = optional(object({ - per_series_aligner = optional(string) - group_by_fields = optional(list(string)) - cross_series_reducer = optional(string) - alignment_period = optional(string) - })) - forecast_options = optional(object({ - forecast_horizon = optional(string) - })) - comparison = optional(string) - duration = optional(string) - filter = optional(string) - evaluation_missing_data = optional(string) - resource_type = optional(string) - trigger = optional(object({ - count = optional(number) - percent = optional(number) - })) - aggregations = optional(object({ - per_series_aligner = optional(string) - group_by_fields = optional(list(string)) - cross_series_reducer = optional(string) - alignment_period = optional(string) - })) - })) - condition_absent = optional(object({ - filter = optional(string) - aggregations = optional(object({ - per_series_aligner = optional(string) - group_by_fields = optional(list(string)) - cross_series_reducer = optional(string) - alignment_period = optional(string) - })) - trigger = optional(object({ - count = optional(number) - percent = optional(number) - })) - duration = optional(string) - })) - - })) - })) - nullable = false - default = {} -} - -variable "logging_metrics" { - description = "Logging metrics alerts configuration." - type = map(object({ - name = optional(string) - description = optional(string) - filter = optional(string) - bucket_name = optional(string) - value_extractor = optional(string) - label_extractors = optional(map(string)) - disabled = optional(bool) - metric_descriptor = optional(map(object({ - metric_kind = optional(string) - value_type = optional(string) - labels = list(object({ - key = optional(string) - description = optional(string) - value_type = optional(string) - })) - }))) - bucket_options = optional(object({ - linear_buckets = optional(object({ - num_finite_buckets = optional(number) - width = optional(number) - offset = optional(number) - })) - exponential_buckets = optional(object({ - num_finite_buckets = optional(number) - growth_factor = optional(number) - scale = optional(number) - })) - explicit_buckets = optional(object({ - bounds = optional(list(number)) - })) - })) - })) - nullable = false - default = {} -} - -variable "channels" { - description = "Logging metrics alerts configuration." - type = map(object({ - type = optional(string) - email_address = optional(string) - labels = optional(map(string)) - description = optional(string) - display_name = optional(string) - user_labels = optional(map(string)) - sensitive_labels = optional(list(object({ - auth_token = optional(string) - password = optional(string) - service_key = optional(string) - }))) - enabled = optional(bool) - })) - nullable = false - default = {} -} diff --git a/modules/project/variables-observability.tf b/modules/project/variables-observability.tf new file mode 100644 index 0000000000..30104fae04 --- /dev/null +++ b/modules/project/variables-observability.tf @@ -0,0 +1,236 @@ +/** + * Copyright 2024 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. + */ +variable "alerts" { + description = "Logging metrics alerts configuration." + type = map(object({ + alert_strategy = optional(object({ + auto_close = optional(string) + notification_prompts = optional(string) + notification_rate_limit = optional(object({ + period = optional(string) + })) + notification_channel_strategy = optional(object({ + notification_channel_names = optional(list(string)) + renotify_interval = optional(string) + })) + })) + combiner = string + conditions = object({ + condition_matched_log = optional(object({ + filter = string + label_extractors = optional(map(string)) + })) + condition_monitoring_query_language = optional(object({ + query = string + duration = string + trigger = optional(object({ + count = optional(number) + percent = optional(number) + })) + display_name = string + evaluation_missing_data = optional(string) + })) + condition_threshold = optional(object({ + aggregations = optional(object({ + per_series_aligner = optional(string) + group_by_fields = optional(list(string)) + cross_series_reducer = optional(string) + alignment_period = optional(string) + })) + comparison = string + denominator_filter = optional(string) + denominator_aggregations = optional(object({ + per_series_aligner = optional(string) + group_by_fields = optional(list(string)) + cross_series_reducer = optional(string) + alignment_period = optional(string) + })) + duration = string + evaluation_missing_data = optional(string) + forecast_options = optional(object({ + forecast_horizon = string + })) + filter = optional(string) + threshold_value = optional(number) + resource_type = optional(string) + trigger = optional(object({ + count = optional(number) + percent = optional(number) + })) + })) + condition_absent = optional(object({ + aggregations = optional(object({ + per_series_aligner = optional(string) + group_by_fields = optional(list(string)) + cross_series_reducer = optional(string) + alignment_period = optional(string) + })) + duration = string + filter = optional(string) + trigger = optional(object({ + count = optional(number) + percent = optional(number) + })) + })) + }) + description = optional(string) + display_name = optional(string) + documentation = optional(object({ + content = optional(string) + mime_type = optional(string) + subject = optional(string) + links = optional(list(object({ + display_name = optional(string) + url = optional(string) + }))) + })) + filter = string + name = string + notification_channels = optional(list(string)) + trigger_count = optional(number) + })) + nullable = false + default = {} + validation { + condition = alltrue([ + for k, v in var.alerts : + contains(["AND", "OR", "AND_WITH_MATCHING_RESOURCE"], v.combiner) + ]) + error_message = "Combiner must be one of 'AND', 'OR', 'AND_WITH_MATCHING_RESOURCE'." + } + validation { + condition = alltrue([ + for k, v in var.alerts : + contains(["ALIGN_NONE", "ALIGN_DELTA", "ALIGN_RATE", "ALIGN_INTERPOLATE", "ALIGN_NEXT_OLDER", "ALIGN_MIN", "ALIGN_MAX", + "ALIGN_MEAN", "ALIGN_COUNT", "ALIGN_SUM", "ALIGN_STDDEV", "ALIGN_COUNT_TRUE", "ALIGN_COUNT_FALSE", "ALIGN_COUNT_FALSE", + "ALIGN_PERCENTILE_99", "ALIGN_PERCENTILE_95", "ALIGN_PERCENTILE_50", "ALIGN_PERCENTILE_05", "ALIGN_PERCENT_CHANGE"], v.aggregations.per_series_aligner) + ]) + error_message = "Aggregation: Per Series Aligner must be one of 'ALIGN_NONE', 'ALIGN_DELTA', 'ALIGN_RATE', 'ALIGN_INTERPOLATE', 'ALIGN_NEXT_OLDER', 'ALIGN_MIN', 'ALIGN_MAX','ALIGN_MEAN', 'ALIGN_COUNT', 'ALIGN_SUM', 'ALIGN_STDDEV', 'ALIGN_COUNT_TRUE', 'ALIGN_COUNT_FALSE', 'ALIGN_COUNT_FALSE', 'ALIGN_PERCENTILE_99', 'ALIGN_PERCENTILE_95', 'ALIGN_PERCENTILE_50', 'ALIGN_PERCENTILE_05', 'ALIGN_PERCENT_CHANGE'." + } + validation { + condition = alltrue([ + for k, v in var.alerts : + contains(["EVALUATION_MISSING_DATA_INACTIVE", "EVALUATION_MISSING_DATA_ACTIVE", "EVALUATION_MISSING_DATA_NO_OP"], v.conditions.condition_monitoring_query_language.evaluation_missing_data) + ]) + error_message = "conditions.condition_monitoring_query_language.evaluation_missing_data must be one of 'EVALUATION_MISSING_DATA_INACTIVE', 'EVALUATION_MISSING_DATA_ACTIVE', 'EVALUATION_MISSING_DATA_NO_OP'." + } + validation { + condition = alltrue([ + for k, v in var.alerts : + contains(["COMPARISON_GT", "COMPARISON_GE", "COMPARISON_LT", "COMPARISON_LE", "COMPARISON_EQ", "COMPARISON_NE"], v.conditions.condition_threshold.comparison) + ]) + error_message = "conditions.condition_threshold.comparison must be one of 'COMPARISON_GT', 'COMPARISON_GE', 'COMPARISON_LT', 'COMPARISON_LE', 'COMPARISON_EQ', 'COMPARISON_NE'." + } + validation { + condition = alltrue([ + for k, v in var.alerts : + contains(["EVALUATION_MISSING_DATA_INACTIVE", "EVALUATION_MISSING_DATA_ACTIVE", "EVALUATION_MISSING_DATA_NO_OP"], v.conditions.condition_threshold.evaluation_missing_data) + ]) + error_message = "conditions.condition_monitoring_query_language.evaluation_missing_data must be one of 'EVALUATION_MISSING_DATA_INACTIVE', 'EVALUATION_MISSING_DATA_ACTIVE', 'EVALUATION_MISSING_DATA_NO_OP'." + } +} + +variable "logging_metrics" { + description = "Logging metrics alerts configuration." + type = map(object({ + bucket_name = optional(string) + disabled = optional(bool) + description = optional(string) + filter = string + label_extractors = optional(map(string)) + labels = list(object({ + key = string + description = optional(string) + value_type = optional(string) + })) + metric_descriptor = optional(map(object({ + metric_kind = string + value_type = string + labels = list(object({ + key = string + description = optional(string) + value_type = optional(string) + })) + name = optional(string) + value_extractor = optional(string) + unit = optional(string) + }))) + bucket_options = optional(object({ + linear_buckets = optional(object({ + num_finite_buckets = number + width = number + offset = number + })) + exponential_buckets = optional(object({ + num_finite_buckets = number + growth_factor = number + scale = number + })) + explicit_buckets = object({ + bounds = list(number) + }) + })) + })) + nullable = false + default = {} + validation { + condition = alltrue([ + for k, v in var.logging_metrics : + contains(["INT64", "DOUBLE", "DISTRIBUTION"], v.metric_descriptor.unit) + ]) + error_message = "metric_descriptor.unit must be one of 'INT64', 'DOUBLE', 'DISTRIBUTION'." + } + validation { + condition = alltrue([ + for k, v in var.logging_metrics : + contains(["BOOL", "INT64", "DOUBLE", "STRING", "DISTRIBUTION", "MONEY"], v.metric_descriptor.value_type) + ]) + error_message = "metric_descriptor.unit must be one of 'BOOL', 'INT64', 'DOUBLE', 'STRING', 'DISTRIBUTION', 'MONEY'." + } + validation { + condition = alltrue([ + for k, v in var.logging_metrics : + contains(["DELTA", "GAUGE", "CUMULATIVE"], v.metric_descriptor.metric_kind) + ]) + error_message = "metric_descriptor.unit must be one of 'DELTA', 'GAUGE', 'CUMULATIVE'." + } + validation { + condition = alltrue([ + for k, v in var.logging_metrics : + contains(["BOOL", "INT64", "STRING"], v.labels.value_type) + ]) + error_message = "metric_descriptor.unit must be one of 'BOOL', 'INT64', 'STRING'." + } +} + +variable "notification_channels" { + description = "Logging metrics alerts configuration." + type = map(object({ + description = optional(string) + display_name = optional(string) + labels = optional(map(string)) + type = string + user_labels = optional(map(string)) + enabled = optional(bool) + sensitive_labels = optional(list(object({ + auth_token = optional(string) + password = optional(string) + service_key = optional(string) + }))) + })) + nullable = false + default = {} +} diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 8234c530ce..1acc637acb 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -87,12 +87,12 @@ variable "descriptive_name" { variable "factories_config" { description = "Paths to data files and folders that enable factory functionality." type = object({ - custom_roles = optional(string) - org_policies = optional(string) - quotas = optional(string) alerts = optional(string) channels = optional(string) + custom_roles = optional(string) logging_metrics = optional(string) + org_policies = optional(string) + quotas = optional(string) }) nullable = false default = {} diff --git a/tests/fast/stages/s0_bootstrap/simple.yaml b/tests/fast/stages/s0_bootstrap/simple.yaml index 259d2e38b2..edf4e89d2a 100644 --- a/tests/fast/stages/s0_bootstrap/simple.yaml +++ b/tests/fast/stages/s0_bootstrap/simple.yaml @@ -16,9 +16,12 @@ counts: google_bigquery_dataset: 1 google_bigquery_default_service_account: 3 google_essential_contacts_contact: 3 + google_logging_metric: 24 google_logging_organization_settings: 1 google_logging_organization_sink: 4 google_logging_project_bucket_config: 4 + google_monitoring_alert_policy: 24 + google_monitoring_notification_channel: 3 google_org_policy_policy: 24 google_organization_iam_binding: 27 google_organization_iam_custom_role: 13 @@ -41,7 +44,7 @@ counts: google_tags_tag_value: 2 local_file: 10 modules: 20 - resources: 235 + resources: 286 outputs: automation: __missing__ diff --git a/tests/fast/stages/s2_network_security/simple.yaml b/tests/fast/stages/s2_network_security/simple.yaml index c0ff76a2d5..e438f04680 100644 --- a/tests/fast/stages/s2_network_security/simple.yaml +++ b/tests/fast/stages/s2_network_security/simple.yaml @@ -16,6 +16,9 @@ counts: google_compute_network_firewall_policy: 2 google_compute_network_firewall_policy_association: 2 google_compute_network_firewall_policy_rule: 4 + google_logging_metric: 8 + google_monitoring_alert_policy: 8 + google_monitoring_notification_channel: 1 google_network_security_firewall_endpoint: 3 google_network_security_firewall_endpoint_association: 6 google_network_security_security_profile: 2 @@ -25,4 +28,4 @@ counts: google_project_service_identity: 1 google_storage_bucket_object: 1 modules: 3 - resources: 25 + resources: 42 diff --git a/tests/fast/stages/s2_network_security/tls.yaml b/tests/fast/stages/s2_network_security/tls.yaml index 8c6e0a93dc..e1c9994d2a 100644 --- a/tests/fast/stages/s2_network_security/tls.yaml +++ b/tests/fast/stages/s2_network_security/tls.yaml @@ -308,6 +308,9 @@ counts: google_compute_network_firewall_policy: 2 google_compute_network_firewall_policy_association: 2 google_compute_network_firewall_policy_rule: 4 + google_logging_metric: 8 + google_monitoring_alert_policy: 8 + google_monitoring_notification_channel: 1 google_network_security_firewall_endpoint: 3 google_network_security_firewall_endpoint_association: 6 google_network_security_security_profile: 2 @@ -317,7 +320,7 @@ counts: google_project_service_identity: 1 google_storage_bucket_object: 1 modules: 3 - resources: 25 + resources: 42 outputs: ngfw_enterprise_endpoint_ids: __missing__ diff --git a/tests/fast/stages/s2_networking_a_simple/ncc.yaml b/tests/fast/stages/s2_networking_a_simple/ncc.yaml index 65630a089d..5261c3f2e7 100644 --- a/tests/fast/stages/s2_networking_a_simple/ncc.yaml +++ b/tests/fast/stages/s2_networking_a_simple/ncc.yaml @@ -29,7 +29,9 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_monitoring_alert_policy: 2 + google_monitoring_alert_policy: 26 + google_logging_metric: 24 + google_monitoring_notification_channel: 3 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 google_network_connectivity_hub: 1 @@ -43,4 +45,4 @@ counts: google_tags_tag_binding: 3 google_vpc_access_connector: 2 modules: 24 - resources: 180 + resources: 231 diff --git a/tests/fast/stages/s2_networking_a_simple/simple.yaml b/tests/fast/stages/s2_networking_a_simple/simple.yaml index eaf9c6690a..75414a35ed 100644 --- a/tests/fast/stages/s2_networking_a_simple/simple.yaml +++ b/tests/fast/stages/s2_networking_a_simple/simple.yaml @@ -35,7 +35,9 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_monitoring_alert_policy: 2 + google_monitoring_alert_policy: 26 + google_logging_metric: 24 + google_monitoring_notification_channel: 3 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 google_project: 3 @@ -48,4 +50,4 @@ counts: google_vpc_access_connector: 2 modules: 29 random_id: 3 - resources: 199 + resources: 250 diff --git a/tests/fast/stages/s2_networking_a_simple/vpn.yaml b/tests/fast/stages/s2_networking_a_simple/vpn.yaml index d948bcb932..efb6369d40 100644 --- a/tests/fast/stages/s2_networking_a_simple/vpn.yaml +++ b/tests/fast/stages/s2_networking_a_simple/vpn.yaml @@ -33,9 +33,11 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_monitoring_alert_policy: 2 + google_logging_metric: 24 + google_monitoring_alert_policy: 26 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 + google_monitoring_notification_channel: 3 google_project: 3 google_project_iam_binding: 2 google_project_iam_member: 20 @@ -46,4 +48,4 @@ counts: google_vpc_access_connector: 2 modules: 31 random_id: 17 - resources: 244 + resources: 295 diff --git a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml index bb1a447703..bd38d2f6b5 100644 --- a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml +++ b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml @@ -36,9 +36,11 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_monitoring_alert_policy: 2 + google_logging_metric: 24 + google_monitoring_alert_policy: 26 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 + google_monitoring_notification_channel: 3 google_network_connectivity_hub: 2 google_network_connectivity_spoke: 4 google_project: 3 @@ -51,4 +53,4 @@ counts: google_vpc_access_connector: 2 modules: 39 random_id: 6 - resources: 261 + resources: 312 diff --git a/tests/fast/stages/s2_networking_b_nva/regional.yaml b/tests/fast/stages/s2_networking_b_nva/regional.yaml index bcd5a85555..ea2e711dda 100644 --- a/tests/fast/stages/s2_networking_b_nva/regional.yaml +++ b/tests/fast/stages/s2_networking_b_nva/regional.yaml @@ -40,9 +40,11 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_monitoring_alert_policy: 2 + google_logging_metric: 24 + google_monitoring_alert_policy: 26 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 + google_monitoring_notification_channel: 3 google_project: 3 google_project_iam_binding: 2 google_project_iam_member: 19 @@ -53,4 +55,4 @@ counts: google_vpc_access_connector: 2 modules: 47 random_id: 6 - resources: 269 + resources: 320 diff --git a/tests/fast/stages/s2_networking_b_nva/simple.yaml b/tests/fast/stages/s2_networking_b_nva/simple.yaml index 51c09deb68..0fc2668590 100644 --- a/tests/fast/stages/s2_networking_b_nva/simple.yaml +++ b/tests/fast/stages/s2_networking_b_nva/simple.yaml @@ -40,9 +40,11 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_monitoring_alert_policy: 2 + google_logging_metric: 24 + google_monitoring_alert_policy: 26 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 + google_monitoring_notification_channel: 3 google_project: 3 google_project_iam_binding: 2 google_project_iam_member: 19 @@ -53,4 +55,4 @@ counts: google_vpc_access_connector: 2 modules: 43 random_id: 6 - resources: 247 + resources: 298 diff --git a/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml b/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml index 1f684b5980..99d05b5c89 100644 --- a/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml +++ b/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml @@ -34,8 +34,10 @@ counts: google_dns_response_policy: 2 google_dns_response_policy_rule: 78 google_essential_contacts_contact: 1 - google_monitoring_alert_policy: 4 + google_logging_metric: 16 + google_monitoring_alert_policy: 20 google_monitoring_dashboard: 6 + google_monitoring_notification_channel: 2 google_project: 2 google_project_iam_binding: 2 google_project_iam_member: 16 @@ -46,4 +48,4 @@ counts: google_vpc_access_connector: 2 modules: 22 random_id: 6 - resources: 215 + resources: 249 diff --git a/tests/fast/stages/s2_project_factory/simple.yaml b/tests/fast/stages/s2_project_factory/simple.yaml index ac5f5dccc1..c7def2e106 100644 --- a/tests/fast/stages/s2_project_factory/simple.yaml +++ b/tests/fast/stages/s2_project_factory/simple.yaml @@ -145,9 +145,12 @@ tests/fast/stages/s2_project_factory/tftest.yaml values: counts: google_compute_shared_vpc_service_project: 4 google_folder: 6 + google_logging_metric: 32 + google_monitoring_alert_policy: 32 + google_monitoring_notification_channel: 8 google_project: 4 google_project_iam_member: 4 google_project_service: 4 google_tags_tag_binding: 4 modules: 11 - resources: 26 + resources: 98 diff --git a/tests/fast/stages/s2_security/simple.yaml b/tests/fast/stages/s2_security/simple.yaml index 5fbb91f0e2..3f7650b185 100644 --- a/tests/fast/stages/s2_security/simple.yaml +++ b/tests/fast/stages/s2_security/simple.yaml @@ -429,6 +429,9 @@ counts: google_kms_crypto_key: 8 google_kms_crypto_key_iam_binding: 8 google_kms_key_ring: 8 + google_logging_metric: 8 + google_monitoring_alert_policy: 8 + google_monitoring_notification_channel: 1 google_project: 2 google_project_iam_binding: 4 google_project_iam_member: 6 diff --git a/tests/modules/project_factory/examples/example.yaml b/tests/modules/project_factory/examples/example.yaml index a7154ae6f3..9f28a3e08f 100644 --- a/tests/modules/project_factory/examples/example.yaml +++ b/tests/modules/project_factory/examples/example.yaml @@ -377,7 +377,7 @@ counts: google_folder: 5 google_folder_iam_binding: 1 google_kms_crypto_key_iam_member: 1 - google_monitoring_notification_channel: 1 + google_monitoring_notification_channel: 4 google_project: 3 google_project_iam_binding: 2 google_project_iam_member: 14 @@ -389,4 +389,4 @@ counts: google_storage_project_service_account: 3 google_tags_tag_binding: 1 modules: 15 - resources: 56 + resources: 59 From 070484875868aa6de12e32ae834a12c6313531ed Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Fri, 13 Dec 2024 13:37:40 +0000 Subject: [PATCH 10/33] tests --- tests/fast/stages/s2_security/simple.tfvars | 1 + tests/fast/stages/s2_security/simple.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/fast/stages/s2_security/simple.tfvars b/tests/fast/stages/s2_security/simple.tfvars index 8713d2693d..feb2a5d427 100644 --- a/tests/fast/stages/s2_security/simple.tfvars +++ b/tests/fast/stages/s2_security/simple.tfvars @@ -40,6 +40,7 @@ kms_keys = { rotation_period = null } } +factories_config = {} service_accounts = { security = "foobar@iam.gserviceaccount.com" data-platform-dev = "foobar@iam.gserviceaccount.com" diff --git a/tests/fast/stages/s2_security/simple.yaml b/tests/fast/stages/s2_security/simple.yaml index 3f7650b185..d3581d98a7 100644 --- a/tests/fast/stages/s2_security/simple.yaml +++ b/tests/fast/stages/s2_security/simple.yaml @@ -429,9 +429,9 @@ counts: google_kms_crypto_key: 8 google_kms_crypto_key_iam_binding: 8 google_kms_key_ring: 8 - google_logging_metric: 8 - google_monitoring_alert_policy: 8 - google_monitoring_notification_channel: 1 + google_logging_metric: 16 + google_monitoring_alert_policy: 16 + google_monitoring_notification_channel: 2 google_project: 2 google_project_iam_binding: 4 google_project_iam_member: 6 @@ -440,4 +440,4 @@ counts: google_storage_bucket_object: 1 google_tags_tag_binding: 2 modules: 11 - resources: 66 + resources: 100 From c674b6cd2975a2a9cf8b021e83cb1d9eb1d35f07 Mon Sep 17 00:00:00 2001 From: Joshua Wright Date: Fri, 13 Dec 2024 15:11:00 +0000 Subject: [PATCH 11/33] Fix Tests --- modules/project-factory/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index bb0ff1de75..71547bef2d 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -266,7 +266,7 @@ module "project-factory" { } } } -# tftest modules=15 resources=56 files=0,1,2,3,4,5,6,7,8 inventory=example.yaml +# tftest modules=15 resources=59 files=0,1,2,3,4,5,6,7,8 inventory=example.yaml ``` A simple hierarchy of folders: @@ -479,7 +479,7 @@ module "project-factory" { projects_data_path = "data/projects" } } -# tftest modules=4 resources=22 files=test-0,test-1,test-2 +# tftest modules=4 resources=25 files=test-0,test-1,test-2 ``` ```yaml From da1274dcdfc2f4bcb1e3b68ab5b49f9c0850a26b Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 16 Dec 2024 15:28:37 +0100 Subject: [PATCH 12/33] Fix formatting --- fast/stages/0-bootstrap/billing.tf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fast/stages/0-bootstrap/billing.tf b/fast/stages/0-bootstrap/billing.tf index d905011b77..5461ce759e 100644 --- a/fast/stages/0-bootstrap/billing.tf +++ b/fast/stages/0-bootstrap/billing.tf @@ -43,11 +43,11 @@ module "billing-export-project" { local.billing_mode == "org" || var.billing_account.force_create.project == true ? 1 : 0 ) billing_account = var.billing_account.id - name = "billing-exp-0" + name = var.resource_names["project-billing"] parent = coalesce( var.project_parent_ids.billing, "organizations/${var.organization.id}" ) - prefix = local.prefix + prefix = var.prefix contacts = ( var.bootstrap_user != null || var.essential_contacts == null ? {} @@ -79,7 +79,7 @@ module "billing-export-dataset" { local.billing_mode == "org" || var.billing_account.force_create.dataset == true ? 1 : 0 ) project_id = module.billing-export-project[0].project_id - id = "billing_export" + id = var.resource_names["bq-billing"] friendly_name = "Billing export." location = local.locations.bq } From d922bbf7d6c4b87b9be001d55068396f01734978 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Wed, 18 Dec 2024 17:14:47 +0100 Subject: [PATCH 13/33] Reformat metric filters --- .../data/logging-metrics/compliance.yaml | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml b/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml index 88724f2b67..316616e0af 100644 --- a/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml +++ b/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml @@ -15,56 +15,98 @@ route-changes: description: "Monitor VPC network route configuration changes inside GCP projects" filter: | - resource.type="gce_route" AND (protoPayload.methodName:"compute.routes.delete" OR protoPayload.methodName:"compute.routes.insert") + resource.type="gce_route" + AND ( + protoPayload.methodName="compute.routes.delete" + OR protoPayload.methodName="compute.routes.insert" + ) metric_descriptor: metric_kind: DELTA value_type: "INT64" network-firewall-config-changes: description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: "resource.type=\"gce_firewall_rule\" AND (protoPayload.methodName:\"compute.firewalls.delete\" OR protoPayload.methodName:\"compute.firewalls.insert\")" + filter: | + resource.type="gce_firewall_rule" + AND ( + protoPayload.methodName="compute.firewalls.delete" + OR protoPayload.methodName="compute.firewalls.insert" + ) metric_descriptor: metric_kind: DELTA value_type: "INT64" vpc-network-config-changes: description: "Monitor VPC network configuration changes inside GCP projects" - filter: "resource.type=\"gce_network\" AND (protoPayload.methodName:\"compute.networks.insert\" OR protoPayload.methodName:\"compute.networks.patch\" OR protoPayload.methodName:\"compute.networks.delete\" OR protoPayload.methodName:\"compute.networks.removePeering\" OR protoPayload.methodName:\"compute.networks.addPeering\")" + filter: | + resource.type="gce_network" + AND ( + protoPayload.methodName="compute.networks.insert" + OR protoPayload.methodName="compute.networks.patch" + OR protoPayload.methodName="compute.networks.delete" + OR protoPayload.methodName="compute.networks.removePeering" + OR protoPayload.methodName="compute.networks.addPeering" + ) metric_descriptor: metric_kind: DELTA value_type: "INT64" cloudsql-changes: description: "Monitor CloudSQL configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"cloudsql.instances.update\" OR protoPayload.methodName=\"cloudsql.instances.create\" OR protoPayload.methodName=\"cloudsql.instances.delete\"" + filter: | + protoPayload.methodName="cloudsql.instances.update" + OR protoPayload.methodName="cloudsql.instances.create" + OR protoPayload.methodName="cloudsql.instances.delete" metric_descriptor: metric_kind: DELTA value_type: "INT64" cloudstorage-changes: description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: "resource.type=gcs_bucket AND protoPayload.methodName=\"storage.setIamPermissions\"" + filter: | + resource.type="gcs_bucket" + AND protoPayload.methodName="storage.setIamPermissions" metric_descriptor: metric_kind: DELTA value_type: "INT64" customrole-changes: description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: "resource.type=\"iam_role\" AND (protoPayload.methodName=\"google.iam.admin.v1.CreateRole\" OR protoPayload.methodName=\"google.iam.admin.v1.DeleteRole\" OR protoPayload.methodName=\"google.iam.admin.v1.UpdateRole\")" + filter: | + resource.type="iam_role" + AND ( + protoPayload.methodName="google.iam.admin.v1.CreateRole" + OR protoPayload.methodName="google.iam.admin.v1.DeleteRole" + OR protoPayload.methodName="google.iam.admin.v1.UpdateRole" + ) metric_descriptor: metric_kind: DELTA value_type: "INT64" audit-changes: description: "Monitor Audit configuration changes inside GCP projects" - filter: "protoPayload.methodName=\"SetIamPolicy\" AND protoPayload.serviceData.policyDelta.auditConfigDeltas:*" + filter: | + protoPayload.methodName="SetIamPolicy" + AND protoPayload.serviceData.policyDelta.auditConfigDeltas:* metric_descriptor: metric_kind: DELTA value_type: "INT64" iam-owner-changes: description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\") AND (ProjectOwnership OR projectOwnerInvitee) OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"REMOVE\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\") OR (protoPayload.serviceData.policyDelta.bindingDeltas.action=\"ADD\" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=\"roles/owner\")" + filter: | + ( + protoPayload.serviceName="cloudresourcemanager.googleapis.com" + AND (ProjectOwnership OR projectOwnerInvitee) + ) + OR ( + protoPayload.serviceData.policyDelta.bindingDeltas.action="REMOVE" + AND protoPayload.serviceData.policyDelta.bindingDeltas.role="roles/owner" + ) + OR ( + protoPayload.serviceData.policyDelta.bindingDeltas.action="ADD" + AND protoPayload.serviceData.policyDelta.bindingDeltas.role="roles/owner" + ) metric_descriptor: metric_kind: DELTA value_type: "INT64" From 17a17774c4c7a1de8bbd412e1bdf758ccfb1f853 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Wed, 18 Dec 2024 17:46:34 +0100 Subject: [PATCH 14/33] Formatting, reordering, and small fixes --- fast/stages/0-bootstrap/automation.tf | 2 +- fast/stages/0-bootstrap/billing.tf | 4 ++-- fast/stages/2-network-security/main.tf | 14 +++++++------- fast/stages/2-networking-a-simple/net-dev.tf | 4 ++-- fast/stages/2-networking-a-simple/net-landing.tf | 14 +++++++------- fast/stages/2-networking-a-simple/net-prod.tf | 4 ++-- fast/stages/2-networking-b-nva/net-dev.tf | 4 ++-- fast/stages/2-networking-b-nva/net-landing.tf | 4 ++-- fast/stages/2-networking-b-nva/net-prod.tf | 4 ++-- .../2-networking-c-separate-envs/net-dev.tf | 4 ++-- .../2-networking-c-separate-envs/net-prod.tf | 4 ++-- fast/stages/2-project-factory/main.tf | 6 +++--- fast/stages/2-security/core-dev.tf | 16 ++++++++-------- fast/stages/2-security/core-prod.tf | 16 ++++++++-------- fast/stages/2-security/variables.tf | 1 + 15 files changed, 51 insertions(+), 50 deletions(-) diff --git a/fast/stages/0-bootstrap/automation.tf b/fast/stages/0-bootstrap/automation.tf index 4b5ab8c70c..d9f1c2f61a 100644 --- a/fast/stages/0-bootstrap/automation.tf +++ b/fast/stages/0-bootstrap/automation.tf @@ -38,6 +38,7 @@ module "automation-project" { ? {} : { (var.essential_contacts) = ["ALL"] } ) + default_alerts_email = var.default_alerts_email factories_config = { org_policies = ( var.bootstrap_user != null ? null : var.factories_config.org_policies_iac @@ -46,7 +47,6 @@ module "automation-project" { channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email # human (groups) IAM bindings iam_by_principals = { (local.principals.gcp-devops) = [ diff --git a/fast/stages/0-bootstrap/billing.tf b/fast/stages/0-bootstrap/billing.tf index 5461ce759e..b09ba3c93f 100644 --- a/fast/stages/0-bootstrap/billing.tf +++ b/fast/stages/0-bootstrap/billing.tf @@ -47,13 +47,13 @@ module "billing-export-project" { parent = coalesce( var.project_parent_ids.billing, "organizations/${var.organization.id}" ) - prefix = var.prefix + prefix = var.prefix + default_alerts_email = var.default_alerts_email contacts = ( var.bootstrap_user != null || var.essential_contacts == null ? {} : { (var.essential_contacts) = ["ALL"] } ) - default_alerts_email = var.default_alerts_email factories_config = { alerts = var.factories_config.alerts channels = var.factories_config.channels diff --git a/fast/stages/2-network-security/main.tf b/fast/stages/2-network-security/main.tf index 6b01e21112..c492e57c4c 100644 --- a/fast/stages/2-network-security/main.tf +++ b/fast/stages/2-network-security/main.tf @@ -36,12 +36,6 @@ module "ngfw-quota-project" { ? "net-ngfw-0" : var.ngfw_enterprise_config.quota_project_id ) - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } - default_alerts_email = var.default_alerts_email billing_account = ( local.create_quota_project ? var.billing_account.id @@ -62,7 +56,13 @@ module "ngfw-quota-project" { ? true : false ) - services = ["networksecurity.googleapis.com"] + factories_config = { + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts + } + default_alerts_email = var.default_alerts_email + services = ["networksecurity.googleapis.com"] } resource "google_network_security_firewall_endpoint" "firewall_endpoint" { diff --git a/fast/stages/2-networking-a-simple/net-dev.tf b/fast/stages/2-networking-a-simple/net-dev.tf index 5bf66a37ba..89e89267f1 100644 --- a/fast/stages/2-networking-a-simple/net-dev.tf +++ b/fast/stages/2-networking-a-simple/net-dev.tf @@ -24,13 +24,13 @@ module "dev-spoke-project" { var.folder_ids.networking-dev, var.folder_ids.networking ) - prefix = var.prefix + prefix = var.prefix + default_alerts_email = var.default_alerts_email factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-a-simple/net-landing.tf b/fast/stages/2-networking-a-simple/net-landing.tf index 5e544a2ed2..2f64216de3 100644 --- a/fast/stages/2-networking-a-simple/net-landing.tf +++ b/fast/stages/2-networking-a-simple/net-landing.tf @@ -24,7 +24,13 @@ module "landing-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix + prefix = var.prefix + default_alerts_email = var.default_alerts_email + factories_config = { + logging_metrics = var.factories_config.logging_metrics + channels = var.factories_config.channels + alerts = var.factories_config.alerts + } services = [ "compute.googleapis.com", "dns.googleapis.com", @@ -33,12 +39,6 @@ module "landing-project" { "networkmanagement.googleapis.com", "stackdriver.googleapis.com" ] - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } - default_alerts_email = var.default_alerts_email shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-a-simple/net-prod.tf b/fast/stages/2-networking-a-simple/net-prod.tf index 0f271456f8..bed2f585a6 100644 --- a/fast/stages/2-networking-a-simple/net-prod.tf +++ b/fast/stages/2-networking-a-simple/net-prod.tf @@ -24,13 +24,13 @@ module "prod-spoke-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix + prefix = var.prefix + default_alerts_email = var.default_alerts_email factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-b-nva/net-dev.tf b/fast/stages/2-networking-b-nva/net-dev.tf index 0093e6bd0b..4a72d6c581 100644 --- a/fast/stages/2-networking-b-nva/net-dev.tf +++ b/fast/stages/2-networking-b-nva/net-dev.tf @@ -24,13 +24,13 @@ module "dev-spoke-project" { var.folder_ids.networking-dev, var.folder_ids.networking ) - prefix = var.prefix + prefix = var.prefix + default_alerts_email = var.default_alerts_email factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-b-nva/net-landing.tf b/fast/stages/2-networking-b-nva/net-landing.tf index 3746d90f4d..58c855b698 100644 --- a/fast/stages/2-networking-b-nva/net-landing.tf +++ b/fast/stages/2-networking-b-nva/net-landing.tf @@ -24,13 +24,13 @@ module "landing-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix + prefix = var.prefix + default_alerts_email = var.default_alerts_email factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email services = [ "compute.googleapis.com", "dns.googleapis.com", diff --git a/fast/stages/2-networking-b-nva/net-prod.tf b/fast/stages/2-networking-b-nva/net-prod.tf index d22e74b89e..1cbcf6f16a 100644 --- a/fast/stages/2-networking-b-nva/net-prod.tf +++ b/fast/stages/2-networking-b-nva/net-prod.tf @@ -24,13 +24,13 @@ module "prod-spoke-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix + prefix = var.prefix + default_alerts_email = var.default_alerts_email factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-c-separate-envs/net-dev.tf b/fast/stages/2-networking-c-separate-envs/net-dev.tf index 7b9f171e9b..ba2fa30178 100644 --- a/fast/stages/2-networking-c-separate-envs/net-dev.tf +++ b/fast/stages/2-networking-c-separate-envs/net-dev.tf @@ -24,13 +24,13 @@ module "dev-spoke-project" { var.folder_ids.networking-dev, var.folder_ids.networking ) - prefix = var.prefix + prefix = var.prefix + default_alerts_email = var.default_alerts_email factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-c-separate-envs/net-prod.tf b/fast/stages/2-networking-c-separate-envs/net-prod.tf index 8a7a0e9a96..8f19df75b3 100644 --- a/fast/stages/2-networking-c-separate-envs/net-prod.tf +++ b/fast/stages/2-networking-c-separate-envs/net-prod.tf @@ -24,13 +24,13 @@ module "prod-spoke-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix + prefix = var.prefix + default_alerts_email = var.default_alerts_email factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-project-factory/main.tf b/fast/stages/2-project-factory/main.tf index 5ac8350d04..98fe3eb41f 100644 --- a/fast/stages/2-project-factory/main.tf +++ b/fast/stages/2-project-factory/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,7 @@ # tfdoc:file:description Project factory. module "projects" { - source = "../../../modules/project-factory" - default_alerts_email = var.default_alerts_email + source = "../../../modules/project-factory" data_defaults = { # more defaults are available, check the project factory variables billing_account = var.billing_account.id @@ -32,6 +31,7 @@ module "projects" { data_overrides = { prefix = var.prefix } + default_alerts_email = var.default_alerts_email factories_config = merge(var.factories_config, { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels diff --git a/fast/stages/2-security/core-dev.tf b/fast/stages/2-security/core-dev.tf index 7e06caa53d..4398732287 100644 --- a/fast/stages/2-security/core-dev.tf +++ b/fast/stages/2-security/core-dev.tf @@ -24,19 +24,19 @@ locals { module "dev-sec-project" { source = "../../../modules/project" name = "dev-sec-core-0" + parent = coalesce( + var.folder_ids.security-dev, var.folder_ids.security + ) + prefix = var.prefix + billing_account = var.billing_account.id + default_alerts_email = var.default_alerts_email factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email - parent = coalesce( - var.folder_ids.security-dev, var.folder_ids.security - ) - prefix = var.prefix - billing_account = var.billing_account.id - labels = { environment = "dev" } - services = local.project_services + labels = { environment = "dev" } + services = local.project_services tag_bindings = local.has_env_folders ? {} : { environment = local.env_tag_values["dev"] } diff --git a/fast/stages/2-security/core-prod.tf b/fast/stages/2-security/core-prod.tf index dfbde78173..e07837095c 100644 --- a/fast/stages/2-security/core-prod.tf +++ b/fast/stages/2-security/core-prod.tf @@ -24,19 +24,19 @@ locals { module "prod-sec-project" { source = "../../../modules/project" name = "prod-sec-core-0" + parent = coalesce( + var.folder_ids.security-prod, var.folder_ids.security + ) + prefix = var.prefix + billing_account = var.billing_account.id + default_alerts_email = var.default_alerts_email factories_config = { logging_metrics = var.factories_config.logging_metrics channels = var.factories_config.channels alerts = var.factories_config.alerts } - default_alerts_email = var.default_alerts_email - parent = coalesce( - var.folder_ids.security-prod, var.folder_ids.security - ) - prefix = var.prefix - billing_account = var.billing_account.id - labels = { environment = "prod" } - services = local.project_services + labels = { environment = "prod" } + services = local.project_services tag_bindings = local.has_env_folders ? {} : { environment = local.env_tag_values["prod"] } diff --git a/fast/stages/2-security/variables.tf b/fast/stages/2-security/variables.tf index ef31df9265..45667e8f04 100644 --- a/fast/stages/2-security/variables.tf +++ b/fast/stages/2-security/variables.tf @@ -189,6 +189,7 @@ variable "factories_config" { logging_metrics = optional(string, "data/logging-metrics") }) nullable = false + default = {} } variable "kms_keys" { From 248f4d2ab65672c6b49a23e7c8331d3f7d00a1ec Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Tue, 31 Dec 2024 15:19:04 +0100 Subject: [PATCH 15/33] Bring back alerts and metrics documentation --- modules/project/README.md | 156 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 151 insertions(+), 5 deletions(-) diff --git a/modules/project/README.md b/modules/project/README.md index 5072a4d210..2d54c631a3 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -32,6 +32,10 @@ This module implements the creation and management of one GCP project including - [Managing project related configuration without creating it](#managing-project-related-configuration-without-creating-it) - [Notification Channels](#notification-channels) - [Notification Channels factory](#notification-channels-factory) +- [Alerts](#alerts) +- [Alerts factory](#alerts-factory) +- [Logging Metrics](#logging-metrics) +- [Logging Metrics factory](#logging-metrics-factory) - [Files](#files) - [Variables](#variables) - [Outputs](#outputs) @@ -1460,6 +1464,147 @@ channel-1: email_address: hello@company.com ``` +## Alerts +Project alerts can be managed via the `alerts` variable +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + alerts = { + alert-1 = { + display_name = "alert-1" + enabled = true + conditions = { + display_name = "condition-1" + condition_threshold = { + filter = "metric.type=\"compute.googleapis.com/instance/disk/write_bytes_count\"" + comparison = "COMPARISON_GT" + threshold_value = 100 + duration = "60s" + aggregations = { + alignment_period = "60s" + per_series_aligner = "ALIGN_RATE" + } + } + } + notification_channels = ["projects/${var.project_id}/notificationChannels/${channel_id}"] + } + } + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] +} +``` + +Notification channels can also reference notification channels created in the same module, just specify the channel name in the `notification_channels` attribute e.g. + +```hcl +notification_channels = ["channel-1"] +``` + +## Alerts factory + +Alerts can be also specified via a factory in a similar way to organization policies, policy constraints and custom roles by pointing to a directory containing YAML files where each file defines one or more Alerts. The structure of the YAML files is exactly the same as the `Alerts` variable. + +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + factories_config = { + alerts = "data/alerts" + } + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] +} +``` + +```yaml +alert-1: + display_name: alert-1 + enabled: true + conditions: + display_name: condition-1 + condition_threshold: + filter: metric.type="compute.googleapis.com/instance/disk/write_bytes_count" + comparison: COMPARISON_GT + threshold_value: 100 + duration: 60s + aggregations: + alignment_period: 60s + per_series_aligner: ALIGN_RATE + notification_channels: + - projects/${var.project_id}/notificationChannels/${channel_id} +``` +## Logging Metrics + +Project Logging Based Metrics can be managed via the `logging_metrics` variable +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + logging_metrics = { + metric-1 = { + name = "metric-1" + filter = "resource.type=\"gce_instance\"" + description = "This is a metric" + metric_descriptor = { + metric_kind = "GAUGE" + value_type = "DOUBLE" + unit = "ms" + } + } + } + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] +} +``` + +## Logging Metrics factory + +Logging Metrics can be also specified via a factory in a similar way to organization policies, policy constraints and custom roles by pointing to a directory containing YAML files where each file defines one or more Logging Metrics. The structure of the YAML files is exactly the same as the `logging_metrics` variable. + +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project" + billing_account = var.billing_account_id + parent = var.folder_id + prefix = var.prefix + factories_config = { + logging_metrics = "data/logging_metrics" + } + services = [ + "cloudquotas.googleapis.com", + "compute.googleapis.com" + ] +} +``` + +```yaml +metric-1: + name: metric-1 + filter: resource.type="gce_instance" + description: This is a metric + metric_descriptor: + metric_kind: GAUGE + value_type: DOUBLE + unit: ms +``` + ## Files @@ -1509,13 +1654,14 @@ channel-1: | [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | [labels](variables.tf#L101) | Resource labels. | map(string) | | {} | | [lien_reason](variables.tf#L108) | If non-empty, creates a project lien with this description. | string | | null | -| [log_scopes](variables-observability.tf#L262) | Log scopes under this project. | map(object({…})) | | {} | -| [logging_data_access](variables-observability.tf#L240) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | -| [logging_exclusions](variables-observability.tf#L255) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | +| [log_scopes](variables-observability.tf#L261) | Log scopes under this project. | map(object({…})) | | {} | +| [logging_data_access](variables-observability.tf#L239) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | +| [logging_exclusions](variables-observability.tf#L254) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | | [logging_metrics](variables-observability.tf#L147) | Logging metrics alerts configuration. | map(object({…})) | | {} | -| [logging_sinks](variables-observability.tf#L272) | Logging sinks to create for this project. | map(object({…})) | | {} | -| [metric_scopes](variables-observability.tf#L303) | List of projects that will act as metric scopes for this project. | list(string) | | [] | +| [logging_sinks](variables-observability.tf#L271) | Logging sinks to create for this project. | map(object({…})) | | {} | +| [metric_scopes](variables-observability.tf#L302) | List of projects that will act as metric scopes for this project. | list(string) | | [] | | [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | +| [notification_channels](variables-observability.tf#L220) | Logging metrics alerts configuration. | map(object({…})) | | {} | | [org_policies](variables.tf#L119) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | | [parent](variables.tf#L146) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | | [prefix](variables.tf#L156) | Optional prefix used to generate project id and name. | string | | null | From 1d4349904dcf9f7575031a1ae1cabb2a9492e6b3 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Tue, 31 Dec 2024 15:23:18 +0100 Subject: [PATCH 16/33] Revert change bootstrap outputs.tf --- fast/stages/0-bootstrap/outputs.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fast/stages/0-bootstrap/outputs.tf b/fast/stages/0-bootstrap/outputs.tf index 12818cb9ea..ca277a16e1 100644 --- a/fast/stages/0-bootstrap/outputs.tf +++ b/fast/stages/0-bootstrap/outputs.tf @@ -23,7 +23,7 @@ locals { # If users give a list of custom audiences we set by default the first element. # If no audiences are given, we set https://iam.googleapis.com/{PROVIDER_NAME} audiences = try( - local.cicd_providers[v["identity_provider"]].audiences, "e" + local.cicd_providers[v["identity_provider"]].audiences, "" ) identity_provider = try( local.cicd_providers[v["identity_provider"]].name, "" From 832bfee933b88bead9fa0676c33f50b35a239944 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Thu, 2 Jan 2025 22:16:57 +0100 Subject: [PATCH 17/33] Fix project notification channel vars and factories --- modules/project/notifications.tf | 47 +++++++++------------- modules/project/variables-observability.tf | 12 +++--- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/modules/project/notifications.tf b/modules/project/notifications.tf index 82c1ac40cb..7c4d714775 100644 --- a/modules/project/notifications.tf +++ b/modules/project/notifications.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,34 +19,27 @@ locals { for f in try(fileset(var.factories_config.channels, "*.yaml"), []) : yamldecode(file("${var.factories_config.channels}/${f}")) ]...) + # TODO: do we want to allow multiple channels in a single file? _channels_factory_data = { for k, v in local._channels_factory_data_raw : - k => merge({ - name = k - type = null - email_address = null - display_name = null - labels = {} - sensitive_labels = [] - user_labels = {} - enabled = null - description = null - }, v) + k => { + type = v.type + description = try(v.description, null) + display_name = try(v.display_name, null) + enabled = null + labels = try(v.labels, null) + sensitive_labels = !can(v.sensitive_labels) ? null : { + auth_token = try(v.sensitive_labels.auth_token, null) + password = try(v.sensitive_labels.password, null) + service_key = try(v.sensitive_labels.service_key, null) + } + user_labels = try(v.user_labels, null) + } } channels = merge(local._channels_factory_data, var.notification_channels) } -resource "google_monitoring_notification_channel" "default" { - count = var.default_alerts_email != null ? 1 : 0 - project = local.project.project_id - display_name = "Default Email Notification" - type = "email" - labels = { - email_address = var.default_alerts_email - } -} - -resource "google_monitoring_notification_channel" "this" { +resource "google_monitoring_notification_channel" "channels" { for_each = local.channels project = local.project.project_id enabled = each.value.enabled @@ -56,11 +49,11 @@ resource "google_monitoring_notification_channel" "this" { user_labels = each.value.user_labels description = each.value.description dynamic "sensitive_labels" { - for_each = lookup(each.value, "sensitive_labels", null)[*] + for_each = each.value.sensitive_labels[*] content { - auth_token = try(each.value.sensitive_labels.auth_token, null) - password = try(each.value.sensitive_labels.password, null) - service_key = try(each.value.sensitive_labels.service_key, null) + auth_token = sensitive_labels.value.auth_token + password = sensitive_labels.value.password + service_key = sensitive_labels.value.service_key } } } diff --git a/modules/project/variables-observability.tf b/modules/project/variables-observability.tf index f8df30dd93..cbba5c9899 100644 --- a/modules/project/variables-observability.tf +++ b/modules/project/variables-observability.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -220,17 +220,17 @@ variable "logging_metrics" { variable "notification_channels" { description = "Logging metrics alerts configuration." type = map(object({ + type = string description = optional(string) display_name = optional(string) - labels = optional(map(string)) - type = string - user_labels = optional(map(string)) enabled = optional(bool) - sensitive_labels = optional(list(object({ + labels = optional(map(string)) + sensitive_labels = optional(object({ auth_token = optional(string) password = optional(string) service_key = optional(string) - }))) + })) + user_labels = optional(map(string)) })) nullable = false default = {} From c253ef27db9b2d953f3f1f34ad8fa5282e93b595 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Thu, 2 Jan 2025 23:25:58 +0100 Subject: [PATCH 18/33] Fix vars and factory for logging alerts --- .../project/{notifications.tf => channels.tf} | 0 modules/project/logging-metrics.tf | 127 +++++++++++------- modules/project/variables-observability.tf | 80 ++++------- 3 files changed, 98 insertions(+), 109 deletions(-) rename modules/project/{notifications.tf => channels.tf} (100%) diff --git a/modules/project/notifications.tf b/modules/project/channels.tf similarity index 100% rename from modules/project/notifications.tf rename to modules/project/channels.tf diff --git a/modules/project/logging-metrics.tf b/modules/project/logging-metrics.tf index 56993f3fc2..eb9f93abed 100644 --- a/modules/project/logging-metrics.tf +++ b/modules/project/logging-metrics.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,77 +19,100 @@ locals { for f in try(fileset(var.factories_config.logging_metrics, "*.yaml"), []) : yamldecode(file("${var.factories_config.logging_metrics}/${f}")) ]...) + # TODO: do we want to allow multiple metrics in a single file? _logging_metrics_factory_data = { for k, v in local._logging_metrics_factory_data_raw : - k => merge({ - name = k - description = null - filter = null - value_extractor = null - label_extractors = null - bucket_name = null - disabled = null - metric_descriptor = { - metric_kind = null - value_type = null - labels = { - key = null - description = null - value_type = null + k => { + filter = v.filter + bucket_name = try(v.bucket_name, null) + bucket_options = !can(v.bucket_options) ? null : { + explicit_buckets = !can(v.bucket_options.explicit_buckets) ? null : { + bounds = v.bucket_options.explicit_buckets.bounds + } + exponential_buckets = !can(v.bucket_options.exponential_buckets) ? null : { + num_finite_buckets = v.bucket_options.exponential_buckets.num_finite_buckets + growth_factor = v.bucket_options.exponential_buckets.growth_factor + scale = v.bucket_options.exponential_buckets.scale + } + linear_buckets = !can(v.bucket_options.linear_buckets) ? null : { + num_finite_buckets = v.bucket_options.linear_buckets.num_finite_buckets + width = v.bucket_options.linear_buckets.width + offset = v.bucket_options.linear_buckets.offset } } - bucket_options = {} - }, v) + description = try(v.description, null) + disabled = try(v.disabled, null) + label_extractors = try(v.label_extractors, null) + metric_descriptor = !can(v.metric_descriptor) ? null : { + metric_kind = v.metric_descriptor.metric_kind + value_type = v.metric_descriptor.value_type + display_name = try(v.metric_descriptor.display_name, null) + labels = !can(v.metric_descriptor.labels) ? {} : { + for kk, vv in v.metric_descriptor.labels : + kk => { + description = try(vv.description, null) + value_type = try(vv.value_type, null) + } + } + unit = try(v.metric_descriptor.unit, null) + } + value_extractor = try(v.value_extractor, null) + } } metrics = merge(local._logging_metrics_factory_data, var.logging_metrics) } -resource "google_logging_metric" "default" { - for_each = local.metrics - project = local.project.project_id - filter = each.value.filter - name = each.value.name - disabled = each.value.disabled - description = each.value.description - bucket_name = each.value.bucket_name - metric_descriptor { - metric_kind = try(each.value.metric_descriptor.metric_kind, null) - value_type = try(each.value.metric_descriptor.value_type, null) - unit = try(each.value.metric_descriptor.unit, 1) - dynamic "labels" { - for_each = try(each.value.metric_descriptor.labels, {}) - content { - key = labels.value.key - description = labels.value.description - value_type = labels.value.value_type - } - } - } +resource "google_logging_metric" "metrics" { + for_each = local.metrics + project = local.project.project_id + name = each.key + filter = each.value.filter + description = each.value.description + disabled = each.value.disabled + bucket_name = each.value.bucket_name value_extractor = each.value.value_extractor label_extractors = each.value.label_extractors + dynamic "bucket_options" { - for_each = try(each.value.bucket_options, {}) + for_each = each.value.bucket_options[*] content { - dynamic "linear_buckets" { - for_each = lookup(each.value.bucket_options, "linear_buckets", null)[*] + dynamic "explicit_buckets" { + for_each = bucket_options.value.explicit_buckets[*] content { - num_finite_buckets = each.value.bucket_options.linear_buckets.num_finite_buckets - width = each.value.bucket_options.linear_buckets.width - offset = each.value.bucket_options.linear_buckets.offset + bounds = explicit_buckets.value.bounds } } dynamic "exponential_buckets" { - for_each = lookup(each.value.bucket_options, "exponential_buckets", null)[*] + for_each = bucket_options.value.exponential_buckets[*] content { - num_finite_buckets = each.value.bucket_options.exponential_buckets.num_finite_buckets - growth_factor = each.value.bucket_options.exponential_buckets.growth_factor - scale = each.value.bucket_options.exponential_buckets.scale + num_finite_buckets = exponential_buckets.value.num_finite_buckets + growth_factor = exponential_buckets.value.growth_factor + scale = exponential_buckets.value.scale } } - dynamic "explicit_buckets" { - for_each = lookup(each.value.bucket_options, "explicit_buckets", null)[*] + dynamic "linear_buckets" { + for_each = bucket_options.value.linear_buckets[*] + content { + num_finite_buckets = linear_buckets.value.num_finite_buckets + width = linear_buckets.value.width + offset = linear_buckets.value.offset + } + } + } + } + dynamic "metric_descriptor" { + for_each = each.value.metric_descriptor[*] + content { + metric_kind = metric_descriptor.value.metric_kind + value_type = metric_descriptor.value.value_type + display_name = metric_descriptor.value.display_name + unit = metric_descriptor.value.unit + dynamic "labels" { + for_each = metric_descriptor.value.labels content { - bounds = each.value.bucket_options.explicit_buckets.bounds + key = labels.key + description = labels.value.description + value_type = labels.value.value_type } } } diff --git a/modules/project/variables-observability.tf b/modules/project/variables-observability.tf index cbba5c9899..072ab395cf 100644 --- a/modules/project/variables-observability.tf +++ b/modules/project/variables-observability.tf @@ -147,74 +147,40 @@ variable "alerts" { variable "logging_metrics" { description = "Logging metrics alerts configuration." type = map(object({ - bucket_name = optional(string) - disabled = optional(bool) - description = optional(string) - filter = string - label_extractors = optional(map(string)) - labels = list(object({ - key = string - description = optional(string) - value_type = optional(string) - })) - metric_descriptor = optional(map(object({ - metric_kind = string - value_type = string - labels = list(object({ - key = string - description = optional(string) - value_type = optional(string) - })) - name = optional(string) - value_extractor = optional(string) - unit = optional(string) - }))) + filter = string + bucket_name = optional(string) bucket_options = optional(object({ - linear_buckets = optional(object({ - num_finite_buckets = number - width = number - offset = number + explicit_buckets = optional(object({ + bounds = list(number) })) exponential_buckets = optional(object({ num_finite_buckets = number growth_factor = number scale = number })) - explicit_buckets = object({ - bounds = list(number) - }) + linear_buckets = optional(object({ + num_finite_buckets = number + width = number + offset = number + })) + })) + description = optional(string) + disabled = optional(bool) + label_extractors = optional(map(string)) + metric_descriptor = optional(object({ + metric_kind = string + value_type = string + display_name = optional(string) + labels = optional(map(object({ + description = optional(string) + value_type = optional(string) + })), {}) + unit = optional(string) })) + value_extractor = optional(string) })) nullable = false default = {} - validation { - condition = alltrue([ - for k, v in var.logging_metrics : - contains(["INT64", "DOUBLE", "DISTRIBUTION"], v.metric_descriptor.unit) - ]) - error_message = "metric_descriptor.unit must be one of 'INT64', 'DOUBLE', 'DISTRIBUTION'." - } - validation { - condition = alltrue([ - for k, v in var.logging_metrics : - contains(["BOOL", "INT64", "DOUBLE", "STRING", "DISTRIBUTION", "MONEY"], v.metric_descriptor.value_type) - ]) - error_message = "metric_descriptor.unit must be one of 'BOOL', 'INT64', 'DOUBLE', 'STRING', 'DISTRIBUTION', 'MONEY'." - } - validation { - condition = alltrue([ - for k, v in var.logging_metrics : - contains(["DELTA", "GAUGE", "CUMULATIVE"], v.metric_descriptor.metric_kind) - ]) - error_message = "metric_descriptor.unit must be one of 'DELTA', 'GAUGE', 'CUMULATIVE'." - } - validation { - condition = alltrue([ - for k, v in var.logging_metrics : - contains(["BOOL", "INT64", "STRING"], v.labels.value_type) - ]) - error_message = "metric_descriptor.unit must be one of 'BOOL', 'INT64', 'STRING'." - } } variable "notification_channels" { From c88821d1af972c7be70b46f083b103ec2736e431 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 3 Jan 2025 13:22:19 +0100 Subject: [PATCH 19/33] Complete alert variable and factory --- modules/project/alerts.tf | 330 ++++++++++++++------- modules/project/variables-observability.tf | 112 +++---- 2 files changed, 261 insertions(+), 181 deletions(-) diff --git a/modules/project/alerts.tf b/modules/project/alerts.tf index ba23e9056b..86bed202ec 100644 --- a/modules/project/alerts.tf +++ b/modules/project/alerts.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,143 +21,253 @@ locals { ]...) _alerts_factory_data = { for k, v in local._alerts_factory_data_raw : - k => merge({ - name = k - filter = null - display_name = null - condition_threshold = {} - trigger_count = null - combiner = null - notification_channels = [] - conditions = { - display_name = null - condition_matched_log = null - condition_monitoring_query_language = null - condition_threshold = {} - condition_absent = null + k => { + combiner = v.combiner + display_name = try(v.display_name, null) + enabled = try(v.enabled, null) + notification_channels = try(v.notification_channels, null) + severity = try(v.severity, null) + user_labels = try(v.user_labels, null) + alert_strategy = !can(v.alert_strategy) ? null : { + auto_close = try(v.alert_strategy.auto_close, null) + notification_prompts = try(v.alert_strategy.notification_prompts, null) + notification_rate_limit = !can(v.alert_strategy.notification_rate_limit) ? null : { + period = try(v.alert_strategy.notification_rate_limit.period, null) + } + notification_channel_strategy = !can(v.alert_strategy.notification_channel_strategy) ? null : { + notification_channel_names = try(v.alert_strategy.notification_channel_strategy.notification_channel_names, null) + renotify_interval = try(v.alert_strategy.notification_channel_strategy.renotify_interval, null) + } + } + conditions = !can(v.conditions) ? null : [ + for c in v.conditions : { + display_name = c.display_name + condition_absent = !can(c.condition_absent) ? null : { + duration = c.condition_absent.duration + filter = try(c.condition_absent.filter, null) + aggregations = !can(c.condition_absent.aggregations) ? null : { + per_series_aligner = try(c.condition_absent.aggregations.per_series_aligner, null) + group_by_fields = try(c.condition_absent.aggregations.group_by_fields, null) + cross_series_reducer = try(c.condition_absent.aggregations.cross_series_reducer, null) + alignment_period = try(c.condition_absent.aggregations.alignment_period, null) + } + trigger = !can(c.condition_absent.trigger) ? null : { + count = try(c.condition_absent.trigger.count, null) + percent = try(c.condition_absent.trigger.percent, null) + } + } + condition_matched_log = !can(c.condition_matched_log) ? null : { + filter = c.condition_matched_log.filter + label_extractors = try(c.condition_matched_log.label_extractors, null) + } + condition_monitoring_query_language = !can(c.condition_monitoring_query_language) ? null : { + duration = c.condition_monitoring_query_language.duration + query = c.condition_monitoring_query_language.query + evaluation_missing_data = try(c.condition_monitoring_query_language.evaluation_missing_data, null) + trigger = !can(c.condition_monitoring_query_language.trigger) ? null : { + count = try(c.condition_monitoring_query_language.trigger.count, null) + percent = try(c.condition_monitoring_query_language.trigger.percent, null) + } + } + condition_prometheus_query_language = !can(c.condition_prometheus_query_language) ? null : { + query = c.condition_prometheus_query_language.query + alert_rule = try(c.condition_prometheus_query_language.alert_rule, null) + disable_metric_validation = try(c.condition_prometheus_query_language.disable_metric_validation, null) + duration = try(c.condition_prometheus_query_language.duration, null) + evaluation_interval = try(c.condition_prometheus_query_language.evaluation_interval, null) + labels = try(c.condition_prometheus_query_language.labels, null) + rule_group = try(c.condition_prometheus_query_language.rule_group, null) + } + condition_threshold = !can(c.condition_threshold) ? null : { + comparison = c.condition_threshold.comparison + duration = c.condition_threshold.duration + denominator_filter = try(c.condition_threshold.denominator_filter, null) + + + evaluation_missing_data = try(c.condition_threshold.evaluation_missing_data, null) + filter = try(c.condition_threshold.filter, null) + threshold_value = try(c.condition_threshold.threshold_value, null) + aggregations = !can(c.condition_threshold.aggregations) ? null : { + per_series_aligner = try(c.condition_threshold.aggregations.per_series_aligner, null) + group_by_fields = try(c.condition_threshold.aggregations.group_by_fields, null) + cross_series_reducer = try(c.condition_threshold.aggregations.cross_series_reducer, null) + alignment_period = try(c.condition_threshold.aggregations.alignment_period, null) + } + denominator_aggregations = !can(c.condition_threshold.denominator_aggregations) ? null : { + per_series_aligner = try(c.condition_threshold.denominator_aggregations.per_series_aligner, null) + group_by_fields = try(c.condition_threshold.denominator_aggregations.group_by_fields, null) + cross_series_reducer = try(c.condition_threshold.denominator_aggregations.cross_series_reducer, null) + alignment_period = try(c.condition_threshold.denominator_aggregations.alignment_period, null) + } + forecast_options = !can(c.condition_threshold.forecast_options) ? null : { + forecast_horizon = c.condition_threshold.forecast_options.forecast_horizon + } + trigger = !can(c.condition_threshold.trigger) ? null : { + count = try(c.condition_threshold.trigger.count, null) + percent = try(c.condition_threshold.trigger.percent, null) + } + } + } + ] + documentation = !can(v.documentation) ? null : { + content = try(v.documentation.content, null) + mime_type = try(v.documentation.mime_type, null) + subject = try(v.documentation.subject, null) + links = !can(v.documentation.links) ? null : [ + for l in v.documentation.link : { + display_name = try(l.display_name, null) + url = try(l.url, null) + }] } - alert_strategy = null - documentation = null - }, v) + } } alerts = merge(local._alerts_factory_data, var.alerts) } -resource "google_monitoring_alert_policy" "default" { - for_each = local.alerts - project = local.project.project_id - combiner = each.value.combiner - display_name = each.value.display_name - conditions { - display_name = each.value.display_name - dynamic "condition_matched_log" { - for_each = lookup(each.value.conditions, "condition_matched_log", null)[*] - content { - filter = each.value.conditions.condition_matched_log.filter - label_extractors = each.value.conditions.condition_matched_log.label_extractors - } - } - dynamic "condition_monitoring_query_language" { - for_each = lookup(each.value.conditions, "condition_monitoring_query_language", null)[*] - content { - query = each.value.conditions.condition_monitoring_query_language.query - duration = each.value.conditions.condition_monitoring_query_language.duration - trigger { - count = try(each.value.conditions.condition_monitoring_query_language.trigger.count, null) - percent = try(each.value.conditions.condition_monitoring_query_language.trigger.percent, null) +resource "google_monitoring_alert_policy" "alerts" { + for_each = local.alerts + project = local.project.project_id + + combiner = each.value.combiner + display_name = each.value.display_name + enabled = each.value.enabled + notification_channels = each.value.notification_channels + severity = each.value.severity + user_labels = each.value.user_labels + + dynamic "alert_strategy" { + for_each = each.value.alert_strategy[*] + content { + auto_close = alert_strategy.value.auto_close + notification_prompts = alert_strategy.value.notification_prompts + dynamic "notification_channel_strategy" { + for_each = alert_strategy.value.notification_channel_strategy[*] + content { + notification_channel_names = notification_channel_strategy.value.notification_channel_names + renotify_interval = notification_channel_strategy.value.renotify_interval } - evaluation_missing_data = each.value.conditions.condition_monitoring_query_language.evaluation_missing_data } - } - dynamic "condition_absent" { - for_each = lookup(each.value.conditions, "condition_absent", null)[*] - content { - filter = each.value.conditions.condition_absent.filter - trigger { - count = try(each.value.conditions.condition_absent.trigger.count, null) - percent = try(each.value.conditions.condition_absent.trigger.percent, null) - } - aggregations { - per_series_aligner = try(each.value.conditions.condition_absent.aggregations.per_series_aligner, null) - group_by_fields = try(each.value.conditions.condition_absent.aggregations.group_by_fields, []) - cross_series_reducer = try(each.value.conditions.condition_absent.aggregations.cross_series_reducer, null) - alignment_period = try(each.value.conditions.condition_absent.aggregations.alignment_period, null) + dynamic "notification_rate_limit" { + for_each = alert_strategy.value.notification_rate_limit[*] + content { + period = notification_rate_limit.value.period } - duration = each.value.conditions.condition_absent.duration } } - dynamic "condition_threshold" { - for_each = lookup(each.value.conditions, "condition_threshold", null)[*] - content { - threshold_value = try(each.value.condition_threshold.threshold_value, null) - denominator_filter = try(each.value.condition_threshold.denominator_filter, null) - dynamic "denominator_aggregations" { - for_each = try(each.value.condition_threshold.denominator_aggregations, null)[*] - content { - per_series_aligner = try(each.value.conditions.condition_threshold.denominator_aggregations.per_series_aligner, null) - group_by_fields = try(each.value.conditions.condition_threshold.denominator_aggregations.group_by_fields, []) - alignment_period = try(each.value.conditions.condition_threshold.denominator_aggregations.alignment_period, null) + } + dynamic "conditions" { + for_each = each.value.conditions + content { + display_name = conditions.value.display_name + dynamic "condition_absent" { + for_each = conditions.value.condition_absent[*] + content { + duration = condition_absent.value.duration + filter = condition_absent.value.filter + dynamic "aggregations" { + for_each = condition_absent.value.aggregations[*] + content { + alignment_period = aggregations.value.alignment_period + cross_series_reducer = aggregations.value.cross_series_reducer + group_by_fields = aggregations.value.group_by_fields + per_series_aligner = aggregations.value.per_series_aligner + } } - } - dynamic "forecast_options" { - for_each = try(each.value.conditions.condition_threshold.forecast_options, null)[*] - content { - forecast_horizon = try(each.value.conditions.condition_threshold.forecast_options.forecast_horizon, null) + dynamic "trigger" { + for_each = condition_absent.value.trigger[*] + content { + count = trigger.value.count + percent = trigger.value.percent + } } } - comparison = try(each.value.conditions.condition_threshold.comparison, null) - duration = try(each.value.conditions.condition_threshold.duration, null) - evaluation_missing_data = try(each.value.conditions.condition_threshold.evaluation_missing_data, null) - filter = try("resource.type = \"${each.value.conditions.condition_threshold.resource_type}\" AND metric.type = \"logging.googleapis.com/user/${google_logging_metric.default[each.value.name].name}\"", each.value.conditions.condition_threshold.filter) - trigger { - count = try(each.value.conditions.condition_threshold.trigger.count, null) - percent = try(each.value.conditions.condition_threshold.trigger.percent, null) + } + dynamic "condition_matched_log" { + for_each = conditions.value.condition_matched_log[*] + content { + filter = condition_matched_log.value.filter + label_extractors = condition_matched_log.value.label_extractors } - aggregations { - per_series_aligner = try(each.value.conditions.condition_threshold.aggregations.per_series_aligner, null) - group_by_fields = try(each.value.conditions.condition_threshold.aggregations.group_by_fields, []) - cross_series_reducer = try(each.value.conditions.condition_threshold.aggregations.cross_series_reducer, null) - alignment_period = try(each.value.conditions.condition_threshold.aggregations.alignment_period, null) + } + dynamic "condition_monitoring_query_language" { + for_each = conditions.value.condition_monitoring_query_language[*] + content { + duration = condition_monitoring_query_language.value.duration + query = condition_monitoring_query_language.value.query + evaluation_missing_data = condition_monitoring_query_language.value.evaluation_missing_data + trigger { + count = condition_monitoring_query_language.value.trigger.count + percent = condition_monitoring_query_language.value.trigger.percent + } } } - } - } - notification_channels = [ - try(google_monitoring_notification_channel.this[each.value.notification_channels].id, - each.value.notification_channels), - try(google_monitoring_notification_channel.default[0].id, "") - ] - dynamic "alert_strategy" { - for_each = lookup(each.value, "alert_strategy", null)[*] - content { - auto_close = try(each.value.alert_strategy.auto_close, "604800s") - notification_prompts = try(each.value.alert_strategy.notification_prompts, null) - dynamic "notification_rate_limit" { - for_each = try(each.value.alert_strategy.notification_rate_limit, null)[*] + + dynamic "condition_prometheus_query_language" { + for_each = conditions.value.condition_prometheus_query_language[*] content { - period = try(each.value.alert_strategy.notification_rate_limit.period, null) + query = condition_prometheus_query_language.value.query + disable_metric_validation = condition_prometheus_query_language.value.disable_metric_validation + duration = condition_prometheus_query_language.value.duration + evaluation_interval = condition_prometheus_query_language.value.evaluation_interval + labels = condition_prometheus_query_language.value.labels + rule_group = condition_prometheus_query_language.value.rule_group + alert_rule = condition_prometheus_query_language.value.alert_rule } } - dynamic "notification_channel_strategy" { - for_each = try(each.value.alert_strategy.notification_channel_strategy, null)[*] + dynamic "condition_threshold" { + for_each = conditions.value.condition_threshold[*] content { - notification_channel_names = try(each.value.alert_strategy.notification_channel_strategy.notification_channel_names, []) - renotify_interval = try(each.value.alert_strategy.notification_channel_strategy.renotify_interval, null) + comparison = condition_threshold.value.comparison + duration = condition_threshold.value.duration + denominator_filter = condition_threshold.value.denominator_filter + evaluation_missing_data = condition_threshold.value.evaluation_missing_data + filter = condition_threshold.value.filter + threshold_value = condition_threshold.value.threshold_value + dynamic "aggregations" { + for_each = condition_threshold.value.aggregations[*] + content { + alignment_period = aggregations.value.alignment_period + cross_series_reducer = aggregations.value.cross_series_reducer + group_by_fields = aggregations.value.group_by_fields + per_series_aligner = aggregations.value.per_series_aligner + } + } + dynamic "denominator_aggregations" { + for_each = condition_threshold.value.denominator_aggregations[*] + content { + alignment_period = denominator_aggregations.value.alignment_period + group_by_fields = denominator_aggregations.value.group_by_fields + per_series_aligner = denominator_aggregations.value.per_series_aligner + } + } + dynamic "forecast_options" { + for_each = condition_threshold.value.forecast_options[*] + content { + forecast_horizon = forecast_options.value.forecast_horizon + } + } + dynamic "trigger" { + for_each = condition_threshold.value.trigger[*] + content { + count = trigger.value.count + percent = trigger.value.percent + } + } } } } } dynamic "documentation" { - for_each = lookup(each.value, "documentation", null)[*] + for_each = each.value.documentation[*] content { - content = try(each.value.documentation.content, null) - mime_type = try(each.value.documentation.mime_type, null) - subject = try(each.value.documentation.subject, null) + content = documentation.value.content + mime_type = documentation.value.mime_type + subject = documentation.value.subject dynamic "links" { - for_each = try(each.value.documentation.links, []) + for_each = documentation.value.links[*] content { - display_name = try(links.value.display_name, null) - url = try(links.value.url, null) + display_name = links.value.display_name + url = links.value.url } } } diff --git a/modules/project/variables-observability.tf b/modules/project/variables-observability.tf index 072ab395cf..1b809e3336 100644 --- a/modules/project/variables-observability.tf +++ b/modules/project/variables-observability.tf @@ -17,6 +17,12 @@ variable "alerts" { description = "Logging metrics alerts configuration." type = map(object({ + combiner = string + display_name = optional(string) + enabled = optional(bool) + notification_channels = optional(list(string)) + severity = optional(string) + user_labels = optional(map(string)) alert_strategy = optional(object({ auto_close = optional(string) notification_prompts = optional(string) @@ -28,67 +34,72 @@ variable "alerts" { renotify_interval = optional(string) })) })) - combiner = string - conditions = object({ + conditions = optional(list(object({ + display_name = string + condition_absent = optional(object({ + duration = string + filter = optional(string) + aggregations = optional(object({ + per_series_aligner = optional(string) + group_by_fields = optional(list(string)) + cross_series_reducer = optional(string) + alignment_period = optional(string) + })) + trigger = optional(object({ + count = optional(number) + percent = optional(number) + })) + })) condition_matched_log = optional(object({ filter = string label_extractors = optional(map(string)) })) condition_monitoring_query_language = optional(object({ - query = string - duration = string + duration = string + query = string + evaluation_missing_data = optional(string) trigger = optional(object({ count = optional(number) percent = optional(number) })) - display_name = string - evaluation_missing_data = optional(string) + })) + condition_prometheus_query_language = optional(object({ + query = string + alert_rule = optional(string) + disable_metric_validation = optional(bool) + duration = optional(string) + evaluation_interval = optional(string) + labels = optional(map(string)) + rule_group = optional(string) })) condition_threshold = optional(object({ + comparison = string + duration = string + denominator_filter = optional(string) + evaluation_missing_data = optional(string) + filter = optional(string) + threshold_value = optional(number) aggregations = optional(object({ per_series_aligner = optional(string) group_by_fields = optional(list(string)) cross_series_reducer = optional(string) alignment_period = optional(string) })) - comparison = string - denominator_filter = optional(string) denominator_aggregations = optional(object({ per_series_aligner = optional(string) group_by_fields = optional(list(string)) cross_series_reducer = optional(string) alignment_period = optional(string) })) - duration = string - evaluation_missing_data = optional(string) forecast_options = optional(object({ forecast_horizon = string })) - filter = optional(string) - threshold_value = optional(number) - resource_type = optional(string) trigger = optional(object({ count = optional(number) percent = optional(number) })) })) - condition_absent = optional(object({ - aggregations = optional(object({ - per_series_aligner = optional(string) - group_by_fields = optional(list(string)) - cross_series_reducer = optional(string) - alignment_period = optional(string) - })) - duration = string - filter = optional(string) - trigger = optional(object({ - count = optional(number) - percent = optional(number) - })) - })) - }) - description = optional(string) - display_name = optional(string) + })), []) documentation = optional(object({ content = optional(string) mime_type = optional(string) @@ -98,50 +109,9 @@ variable "alerts" { url = optional(string) }))) })) - filter = string - name = string - notification_channels = optional(list(string)) - trigger_count = optional(number) })) nullable = false default = {} - validation { - condition = alltrue([ - for k, v in var.alerts : - contains(["AND", "OR", "AND_WITH_MATCHING_RESOURCE"], v.combiner) - ]) - error_message = "Combiner must be one of 'AND', 'OR', 'AND_WITH_MATCHING_RESOURCE'." - } - validation { - condition = alltrue([ - for k, v in var.alerts : - contains(["ALIGN_NONE", "ALIGN_DELTA", "ALIGN_RATE", "ALIGN_INTERPOLATE", "ALIGN_NEXT_OLDER", "ALIGN_MIN", "ALIGN_MAX", - "ALIGN_MEAN", "ALIGN_COUNT", "ALIGN_SUM", "ALIGN_STDDEV", "ALIGN_COUNT_TRUE", "ALIGN_COUNT_FALSE", "ALIGN_COUNT_FALSE", - "ALIGN_PERCENTILE_99", "ALIGN_PERCENTILE_95", "ALIGN_PERCENTILE_50", "ALIGN_PERCENTILE_05", "ALIGN_PERCENT_CHANGE"], v.aggregations.per_series_aligner) - ]) - error_message = "Aggregation: Per Series Aligner must be one of 'ALIGN_NONE', 'ALIGN_DELTA', 'ALIGN_RATE', 'ALIGN_INTERPOLATE', 'ALIGN_NEXT_OLDER', 'ALIGN_MIN', 'ALIGN_MAX','ALIGN_MEAN', 'ALIGN_COUNT', 'ALIGN_SUM', 'ALIGN_STDDEV', 'ALIGN_COUNT_TRUE', 'ALIGN_COUNT_FALSE', 'ALIGN_COUNT_FALSE', 'ALIGN_PERCENTILE_99', 'ALIGN_PERCENTILE_95', 'ALIGN_PERCENTILE_50', 'ALIGN_PERCENTILE_05', 'ALIGN_PERCENT_CHANGE'." - } - validation { - condition = alltrue([ - for k, v in var.alerts : - contains(["EVALUATION_MISSING_DATA_INACTIVE", "EVALUATION_MISSING_DATA_ACTIVE", "EVALUATION_MISSING_DATA_NO_OP"], v.conditions.condition_monitoring_query_language.evaluation_missing_data) - ]) - error_message = "conditions.condition_monitoring_query_language.evaluation_missing_data must be one of 'EVALUATION_MISSING_DATA_INACTIVE', 'EVALUATION_MISSING_DATA_ACTIVE', 'EVALUATION_MISSING_DATA_NO_OP'." - } - validation { - condition = alltrue([ - for k, v in var.alerts : - contains(["COMPARISON_GT", "COMPARISON_GE", "COMPARISON_LT", "COMPARISON_LE", "COMPARISON_EQ", "COMPARISON_NE"], v.conditions.condition_threshold.comparison) - ]) - error_message = "conditions.condition_threshold.comparison must be one of 'COMPARISON_GT', 'COMPARISON_GE', 'COMPARISON_LT', 'COMPARISON_LE', 'COMPARISON_EQ', 'COMPARISON_NE'." - } - validation { - condition = alltrue([ - for k, v in var.alerts : - contains(["EVALUATION_MISSING_DATA_INACTIVE", "EVALUATION_MISSING_DATA_ACTIVE", "EVALUATION_MISSING_DATA_NO_OP"], v.conditions.condition_threshold.evaluation_missing_data) - ]) - error_message = "conditions.condition_monitoring_query_language.evaluation_missing_data must be one of 'EVALUATION_MISSING_DATA_INACTIVE', 'EVALUATION_MISSING_DATA_ACTIVE', 'EVALUATION_MISSING_DATA_NO_OP'." - } } variable "logging_metrics" { From c4424959b9b53947dbfe83153e395c24dfafb617 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 3 Jan 2025 13:29:12 +0100 Subject: [PATCH 20/33] Reorder fields --- modules/project/channels.tf | 61 ---------------------- modules/project/logging-metrics.tf | 14 ++--- modules/project/variables-observability.tf | 16 +++--- 3 files changed, 15 insertions(+), 76 deletions(-) delete mode 100644 modules/project/channels.tf diff --git a/modules/project/channels.tf b/modules/project/channels.tf deleted file mode 100644 index 7c4d714775..0000000000 --- a/modules/project/channels.tf +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2025 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. - */ - -locals { - _channels_factory_data_raw = merge([ - for f in try(fileset(var.factories_config.channels, "*.yaml"), []) : - yamldecode(file("${var.factories_config.channels}/${f}")) - ]...) - # TODO: do we want to allow multiple channels in a single file? - _channels_factory_data = { - for k, v in local._channels_factory_data_raw : - k => { - type = v.type - description = try(v.description, null) - display_name = try(v.display_name, null) - enabled = null - labels = try(v.labels, null) - sensitive_labels = !can(v.sensitive_labels) ? null : { - auth_token = try(v.sensitive_labels.auth_token, null) - password = try(v.sensitive_labels.password, null) - service_key = try(v.sensitive_labels.service_key, null) - } - user_labels = try(v.user_labels, null) - } - } - channels = merge(local._channels_factory_data, var.notification_channels) -} - -resource "google_monitoring_notification_channel" "channels" { - for_each = local.channels - project = local.project.project_id - enabled = each.value.enabled - display_name = each.value.display_name - type = each.value.type - labels = each.value.labels - user_labels = each.value.user_labels - description = each.value.description - dynamic "sensitive_labels" { - for_each = each.value.sensitive_labels[*] - content { - auth_token = sensitive_labels.value.auth_token - password = sensitive_labels.value.password - service_key = sensitive_labels.value.service_key - } - } -} - - diff --git a/modules/project/logging-metrics.tf b/modules/project/logging-metrics.tf index eb9f93abed..f5e53b77d8 100644 --- a/modules/project/logging-metrics.tf +++ b/modules/project/logging-metrics.tf @@ -23,8 +23,12 @@ locals { _logging_metrics_factory_data = { for k, v in local._logging_metrics_factory_data_raw : k => { - filter = v.filter - bucket_name = try(v.bucket_name, null) + filter = v.filter + bucket_name = try(v.bucket_name, null) + description = try(v.description, null) + disabled = try(v.disabled, null) + label_extractors = try(v.label_extractors, null) + value_extractor = try(v.value_extractor, null) bucket_options = !can(v.bucket_options) ? null : { explicit_buckets = !can(v.bucket_options.explicit_buckets) ? null : { bounds = v.bucket_options.explicit_buckets.bounds @@ -40,13 +44,11 @@ locals { offset = v.bucket_options.linear_buckets.offset } } - description = try(v.description, null) - disabled = try(v.disabled, null) - label_extractors = try(v.label_extractors, null) metric_descriptor = !can(v.metric_descriptor) ? null : { metric_kind = v.metric_descriptor.metric_kind value_type = v.metric_descriptor.value_type display_name = try(v.metric_descriptor.display_name, null) + unit = try(v.metric_descriptor.unit, null) labels = !can(v.metric_descriptor.labels) ? {} : { for kk, vv in v.metric_descriptor.labels : kk => { @@ -54,9 +56,7 @@ locals { value_type = try(vv.value_type, null) } } - unit = try(v.metric_descriptor.unit, null) } - value_extractor = try(v.value_extractor, null) } } metrics = merge(local._logging_metrics_factory_data, var.logging_metrics) diff --git a/modules/project/variables-observability.tf b/modules/project/variables-observability.tf index 1b809e3336..5215db9892 100644 --- a/modules/project/variables-observability.tf +++ b/modules/project/variables-observability.tf @@ -117,8 +117,12 @@ variable "alerts" { variable "logging_metrics" { description = "Logging metrics alerts configuration." type = map(object({ - filter = string - bucket_name = optional(string) + filter = string + bucket_name = optional(string) + description = optional(string) + disabled = optional(bool) + label_extractors = optional(map(string)) + value_extractor = optional(string) bucket_options = optional(object({ explicit_buckets = optional(object({ bounds = list(number) @@ -134,20 +138,16 @@ variable "logging_metrics" { offset = number })) })) - description = optional(string) - disabled = optional(bool) - label_extractors = optional(map(string)) metric_descriptor = optional(object({ metric_kind = string value_type = string display_name = optional(string) + unit = optional(string) labels = optional(map(object({ description = optional(string) value_type = optional(string) })), {}) - unit = optional(string) })) - value_extractor = optional(string) })) nullable = false default = {} @@ -161,12 +161,12 @@ variable "notification_channels" { display_name = optional(string) enabled = optional(bool) labels = optional(map(string)) + user_labels = optional(map(string)) sensitive_labels = optional(object({ auth_token = optional(string) password = optional(string) service_key = optional(string) })) - user_labels = optional(map(string)) })) nullable = false default = {} From 05cecc3e38cf261754d3acfa9907e4b515ff2699 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 3 Jan 2025 13:29:49 +0100 Subject: [PATCH 21/33] Update readme --- modules/project/README.md | 57 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/modules/project/README.md b/modules/project/README.md index 2d54c631a3..dd40a7355f 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -1617,7 +1617,7 @@ metric-1: | [logging-metrics.tf](./logging-metrics.tf) | None | google_logging_metric | | [logging.tf](./logging.tf) | Log sinks and supporting resources. | google_bigquery_dataset_iam_member · google_logging_log_scope · google_logging_project_exclusion · google_logging_project_sink · google_project_iam_audit_config · google_project_iam_member · google_pubsub_topic_iam_member · google_storage_bucket_iam_member | | [main.tf](./main.tf) | Module-level locals and resources. | google_compute_project_metadata_item · google_essential_contacts_contact · google_monitoring_monitored_project · google_project · google_project_service · google_resource_manager_lien | -| [notifications.tf](./notifications.tf) | None | google_monitoring_notification_channel | +| [notification-channels.tf](./notification-channels.tf) | None | google_monitoring_notification_channel | | [organization-policies.tf](./organization-policies.tf) | Project-level organization policies. | google_org_policy_policy | | [outputs.tf](./outputs.tf) | Module outputs. | | | [quotas.tf](./quotas.tf) | None | google_cloud_quotas_quota_preference | @@ -1636,47 +1636,46 @@ metric-1: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L114) | Project name and id suffix. | string | ✓ | | -| [alerts](variables-observability.tf#L17) | Logging metrics alerts configuration. | map(object({…})) | | {} | +| [name](variables.tf#L108) | Project name and id suffix. | string | ✓ | | +| [alerts](variables-observability.tf#L17) | Logging metrics alerts configuration. | map(object({…})) | | {} | | [auto_create_network](variables.tf#L17) | Whether to create the default network for the project. | bool | | false | | [billing_account](variables.tf#L23) | Billing account id. | string | | null | | [compute_metadata](variables.tf#L29) | Optional compute metadata key/values. Only usable if compute API has been enabled. | map(string) | | {} | | [contacts](variables.tf#L36) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | map(list(string)) | | {} | | [custom_roles](variables.tf#L43) | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | -| [default_alerts_email](variables.tf#L50) | Default email address for alerting. | string | | null | -| [default_service_account](variables.tf#L56) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | string | | "keep" | -| [deletion_policy](variables.tf#L70) | Deletion policy setting for this project. | string | | "DELETE" | -| [descriptive_name](variables.tf#L81) | Name of the project name. Used for project name instead of `name` variable. | string | | null | -| [factories_config](variables.tf#L87) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | +| [default_service_account](variables.tf#L50) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | string | | "keep" | +| [deletion_policy](variables.tf#L64) | Deletion policy setting for this project. | string | | "DELETE" | +| [descriptive_name](variables.tf#L75) | Name of the project name. Used for project name instead of `name` variable. | string | | null | +| [factories_config](variables.tf#L81) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | | [iam](variables-iam.tf#L17) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | | [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | -| [labels](variables.tf#L101) | Resource labels. | map(string) | | {} | -| [lien_reason](variables.tf#L108) | If non-empty, creates a project lien with this description. | string | | null | -| [log_scopes](variables-observability.tf#L261) | Log scopes under this project. | map(object({…})) | | {} | -| [logging_data_access](variables-observability.tf#L239) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | -| [logging_exclusions](variables-observability.tf#L254) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | -| [logging_metrics](variables-observability.tf#L147) | Logging metrics alerts configuration. | map(object({…})) | | {} | -| [logging_sinks](variables-observability.tf#L271) | Logging sinks to create for this project. | map(object({…})) | | {} | -| [metric_scopes](variables-observability.tf#L302) | List of projects that will act as metric scopes for this project. | list(string) | | [] | +| [labels](variables.tf#L95) | Resource labels. | map(string) | | {} | +| [lien_reason](variables.tf#L102) | If non-empty, creates a project lien with this description. | string | | null | +| [log_scopes](variables-observability.tf#L197) | Log scopes under this project. | map(object({…})) | | {} | +| [logging_data_access](variables-observability.tf#L175) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | +| [logging_exclusions](variables-observability.tf#L190) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | +| [logging_metrics](variables-observability.tf#L117) | Logging metrics alerts configuration. | map(object({…})) | | {} | +| [logging_sinks](variables-observability.tf#L207) | Logging sinks to create for this project. | map(object({…})) | | {} | +| [metric_scopes](variables-observability.tf#L238) | List of projects that will act as metric scopes for this project. | list(string) | | [] | | [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [notification_channels](variables-observability.tf#L220) | Logging metrics alerts configuration. | map(object({…})) | | {} | -| [org_policies](variables.tf#L119) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | -| [parent](variables.tf#L146) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | -| [prefix](variables.tf#L156) | Optional prefix used to generate project id and name. | string | | null | -| [project_create](variables.tf#L166) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | +| [notification_channels](variables-observability.tf#L156) | Logging metrics alerts configuration. | map(object({…})) | | {} | +| [org_policies](variables.tf#L113) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | +| [parent](variables.tf#L140) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +| [prefix](variables.tf#L150) | Optional prefix used to generate project id and name. | string | | null | +| [project_create](variables.tf#L160) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | | [quotas](variables-quotas.tf#L17) | Service quota configuration. | map(object({…})) | | {} | -| [service_agents_config](variables.tf#L172) | Automatic service agent configuration options. | object({…}) | | {} | -| [service_config](variables.tf#L183) | Configure service API activation. | object({…}) | | {…} | -| [service_encryption_key_ids](variables.tf#L195) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | -| [services](variables.tf#L202) | Service APIs to enable. | list(string) | | [] | -| [shared_vpc_host_config](variables.tf#L208) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | -| [shared_vpc_service_config](variables.tf#L217) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | -| [skip_delete](variables.tf#L245) | Deprecated. Use deletion_policy. | bool | | null | +| [service_agents_config](variables.tf#L166) | Automatic service agent configuration options. | object({…}) | | {} | +| [service_config](variables.tf#L177) | Configure service API activation. | object({…}) | | {…} | +| [service_encryption_key_ids](variables.tf#L189) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | +| [services](variables.tf#L196) | Service APIs to enable. | list(string) | | [] | +| [shared_vpc_host_config](variables.tf#L202) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | +| [shared_vpc_service_config](variables.tf#L211) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | +| [skip_delete](variables.tf#L239) | Deprecated. Use deletion_policy. | bool | | null | | [tag_bindings](variables-tags.tf#L81) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | | [tags](variables-tags.tf#L88) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [vpc_sc](variables.tf#L257) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | +| [vpc_sc](variables.tf#L251) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | ## Outputs From 1f998b576cea45ba11be46221e8f49eb43bcd324 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 3 Jan 2025 13:34:43 +0100 Subject: [PATCH 22/33] Reorder variables --- modules/project/README.md | 14 +-- modules/project/variables-observability.tf | 102 ++++++++++----------- modules/project/variables.tf | 12 +-- 3 files changed, 64 insertions(+), 64 deletions(-) diff --git a/modules/project/README.md b/modules/project/README.md index dd40a7355f..7ec4b827eb 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -1653,14 +1653,14 @@ metric-1: | [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | [labels](variables.tf#L95) | Resource labels. | map(string) | | {} | | [lien_reason](variables.tf#L102) | If non-empty, creates a project lien with this description. | string | | null | -| [log_scopes](variables-observability.tf#L197) | Log scopes under this project. | map(object({…})) | | {} | -| [logging_data_access](variables-observability.tf#L175) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | -| [logging_exclusions](variables-observability.tf#L190) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | -| [logging_metrics](variables-observability.tf#L117) | Logging metrics alerts configuration. | map(object({…})) | | {} | -| [logging_sinks](variables-observability.tf#L207) | Logging sinks to create for this project. | map(object({…})) | | {} | -| [metric_scopes](variables-observability.tf#L238) | List of projects that will act as metric scopes for this project. | list(string) | | [] | +| [log_scopes](variables-observability.tf#L117) | Log scopes under this project. | map(object({…})) | | {} | +| [logging_data_access](variables-observability.tf#L127) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | +| [logging_exclusions](variables-observability.tf#L142) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | +| [logging_metrics](variables-observability.tf#L149) | Logging metrics alerts configuration. | map(object({…})) | | {} | +| [logging_sinks](variables-observability.tf#L188) | Logging sinks to create for this project. | map(object({…})) | | {} | +| [metric_scopes](variables-observability.tf#L219) | List of projects that will act as metric scopes for this project. | list(string) | | [] | | [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [notification_channels](variables-observability.tf#L156) | Logging metrics alerts configuration. | map(object({…})) | | {} | +| [notification_channels](variables-observability.tf#L226) | Logging metrics alerts configuration. | map(object({…})) | | {} | | [org_policies](variables.tf#L113) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | | [parent](variables.tf#L140) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | | [prefix](variables.tf#L150) | Optional prefix used to generate project id and name. | string | | null | diff --git a/modules/project/variables-observability.tf b/modules/project/variables-observability.tf index 5215db9892..93984447e3 100644 --- a/modules/project/variables-observability.tf +++ b/modules/project/variables-observability.tf @@ -114,6 +114,38 @@ variable "alerts" { default = {} } +variable "log_scopes" { + description = "Log scopes under this project." + type = map(object({ + description = optional(string) + resource_names = list(string) + })) + nullable = false + default = {} +} + +variable "logging_data_access" { + description = "Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services." + type = map(map(list(string))) + nullable = false + default = {} + validation { + condition = alltrue(flatten([ + for k, v in var.logging_data_access : [ + for kk, vv in v : contains(["DATA_READ", "DATA_WRITE", "ADMIN_READ"], kk) + ] + ])) + error_message = "Log type keys for each service can only be one of 'DATA_READ', 'DATA_WRITE', 'ADMIN_READ'." + } +} + +variable "logging_exclusions" { + description = "Logging exclusions for this project in the form {NAME -> FILTER}." + type = map(string) + default = {} + nullable = false +} + variable "logging_metrics" { description = "Logging metrics alerts configuration." type = map(object({ @@ -153,57 +185,6 @@ variable "logging_metrics" { default = {} } -variable "notification_channels" { - description = "Logging metrics alerts configuration." - type = map(object({ - type = string - description = optional(string) - display_name = optional(string) - enabled = optional(bool) - labels = optional(map(string)) - user_labels = optional(map(string)) - sensitive_labels = optional(object({ - auth_token = optional(string) - password = optional(string) - service_key = optional(string) - })) - })) - nullable = false - default = {} -} - -variable "logging_data_access" { - description = "Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services." - type = map(map(list(string))) - nullable = false - default = {} - validation { - condition = alltrue(flatten([ - for k, v in var.logging_data_access : [ - for kk, vv in v : contains(["DATA_READ", "DATA_WRITE", "ADMIN_READ"], kk) - ] - ])) - error_message = "Log type keys for each service can only be one of 'DATA_READ', 'DATA_WRITE', 'ADMIN_READ'." - } -} - -variable "logging_exclusions" { - description = "Logging exclusions for this project in the form {NAME -> FILTER}." - type = map(string) - default = {} - nullable = false -} - -variable "log_scopes" { - description = "Log scopes under this project." - type = map(object({ - description = optional(string) - resource_names = list(string) - })) - nullable = false - default = {} -} - variable "logging_sinks" { description = "Logging sinks to create for this project." type = map(object({ @@ -241,3 +222,22 @@ variable "metric_scopes" { default = [] nullable = false } + +variable "notification_channels" { + description = "Logging metrics alerts configuration." + type = map(object({ + type = string + description = optional(string) + display_name = optional(string) + enabled = optional(bool) + labels = optional(map(string)) + user_labels = optional(map(string)) + sensitive_labels = optional(object({ + auth_token = optional(string) + password = optional(string) + service_key = optional(string) + })) + })) + nullable = false + default = {} +} diff --git a/modules/project/variables.tf b/modules/project/variables.tf index fff9fbe4f6..eb3511aed3 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -87,12 +87,12 @@ variable "descriptive_name" { variable "factories_config" { description = "Paths to data files and folders that enable factory functionality." type = object({ - alerts = optional(string) - channels = optional(string) - custom_roles = optional(string) - logging_metrics = optional(string) - org_policies = optional(string) - quotas = optional(string) + alerts = optional(string) + notification_channels = optional(string) + custom_roles = optional(string) + logging_metrics = optional(string) + org_policies = optional(string) + quotas = optional(string) }) nullable = false default = {} From 6b30b0d46633659ab51dac16e941a8039fb79bb5 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 3 Jan 2025 17:22:30 +0100 Subject: [PATCH 23/33] Add schemas, update README, and fix some types --- modules/project/README.md | 238 ++++++------- modules/project/alerts.tf | 21 +- modules/project/logging-metrics.tf | 11 +- modules/project/notification-channels.tf | 61 ++++ modules/project/schemas/alerts.schema.json | 315 ++++++++++++++++++ .../schemas/logging-metrics.schema.json | 129 +++++++ .../schemas/notification-channels.schema.json | 56 ++++ modules/project/variables-observability.tf | 13 +- 8 files changed, 699 insertions(+), 145 deletions(-) create mode 100644 modules/project/notification-channels.tf create mode 100644 modules/project/schemas/alerts.schema.json create mode 100644 modules/project/schemas/logging-metrics.schema.json create mode 100644 modules/project/schemas/notification-channels.schema.json diff --git a/modules/project/README.md b/modules/project/README.md index 7ec4b827eb..b53d67c395 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -31,11 +31,8 @@ This module implements the creation and management of one GCP project including - [Project Related Outputs](#project-related-outputs) - [Managing project related configuration without creating it](#managing-project-related-configuration-without-creating-it) - [Notification Channels](#notification-channels) -- [Notification Channels factory](#notification-channels-factory) - [Alerts](#alerts) -- [Alerts factory](#alerts-factory) - [Logging Metrics](#logging-metrics) -- [Logging Metrics factory](#logging-metrics-factory) - [Files](#files) - [Variables](#variables) - [Outputs](#outputs) @@ -1409,7 +1406,8 @@ module "bucket" { ## Notification Channels -Project Notification Channels can be managed via the `notification_channels` variable +You can manage project notification channels using the `notification_channels` variable or define them using a factory with `factories_config.notification_channels`. Each YAML file should define one or more notification channels using the same structure as the `notification_channels` variable. + ```hcl module "project" { source = "./fabric/modules/project" @@ -1420,52 +1418,32 @@ module "project" { notification_channels = { channel-1 = { display_name = "channel-1" - enabled = true type = "email" labels = { - email_address = "hello@company.com" - services = [ - "cloudquotas.googleapis.com", - "compute.googleapis.com" - ] + email_address = "hello@example.com" } } } -} -``` - -## Notification Channels factory - -Notification Channels can be also specified via a factory in a similar way to organization policies, policy constraints and custom roles by pointing to a directory containing YAML files where each file defines one or more Notification Channels. The structure of the YAML files is exactly the same as the `notification_channels` variable. - -```hcl -module "project" { - source = "./fabric/modules/project" - name = "project" - billing_account = var.billing_account_id - parent = var.folder_id - prefix = var.prefix factories_config = { - notification_channels = "data/notification_channels" + notification_channels = "data/notification-channels" } - services = [ - "cloudquotas.googleapis.com", - "compute.googleapis.com" - ] } +# tftest modules=1 resources=3 files=channel-1 ``` ```yaml -channel-1: +# tftest-file id=channel-1 path=data/notification-channels/channel-1.yaml schema=notification-channels.schema.json +channel-1-from-yaml: display_name: channel-1 - enabled: true type: email labels: - email_address: hello@company.com + email_address: hello2@example.com ``` ## Alerts -Project alerts can be managed via the `alerts` variable +Project alerts can be managed via the `alerts` variable. Notice that alerts can reference notificatin either using thei fully qualified ID or channels using the channel key specified in the `notification_channels` variable (or the key in the YAML file if using a factory). +You can also define alerts using a factory with `factories_config.alerts`. Each YAML file should define one or more notification channels using the same structure as the `alerts` variable. + ```hcl module "project" { source = "./fabric/modules/project" @@ -1476,77 +1454,66 @@ module "project" { alerts = { alert-1 = { display_name = "alert-1" - enabled = true - conditions = { - display_name = "condition-1" + combiner = "OR" + notification_channels = [ + "my-channel", + "projects/other-project/notificationChannels/1234567890" + ] + conditions = [{ + display_name = "test condition" condition_threshold = { - filter = "metric.type=\"compute.googleapis.com/instance/disk/write_bytes_count\"" - comparison = "COMPARISON_GT" + filter = "metric.type=\"compute.googleapis.com/instance/disk/write_bytes_count\"" + comparison = "COMPARISON_GT" threshold_value = 100 - duration = "60s" + duration = "60s" aggregations = { - alignment_period = "60s" + alignment_period = "60s" per_series_aligner = "ALIGN_RATE" } } + }] + } + } + notification_channels = { + my-channel = { + display_name = "My Channel" + type = "email" + labels = { + email_address = "hello@example.com" } - notification_channels = ["projects/${var.project_id}/notificationChannels/${channel_id}"] } } - services = [ - "cloudquotas.googleapis.com", - "compute.googleapis.com" - ] -} -``` - -Notification channels can also reference notification channels created in the same module, just specify the channel name in the `notification_channels` attribute e.g. - -```hcl -notification_channels = ["channel-1"] -``` - -## Alerts factory - -Alerts can be also specified via a factory in a similar way to organization policies, policy constraints and custom roles by pointing to a directory containing YAML files where each file defines one or more Alerts. The structure of the YAML files is exactly the same as the `Alerts` variable. - -```hcl -module "project" { - source = "./fabric/modules/project" - name = "project" - billing_account = var.billing_account_id - parent = var.folder_id - prefix = var.prefix factories_config = { alerts = "data/alerts" } - services = [ - "cloudquotas.googleapis.com", - "compute.googleapis.com" - ] } +# tftest modules=1 resources=4 files=alerts-1 ``` ```yaml -alert-1: - display_name: alert-1 - enabled: true +# tftest-file id=alerts-1 path=data/alerts/channel-1.yaml schema=alerts.schema.json +alert-2: + display_name: My Alert Policy + combiner: OR conditions: - display_name: condition-1 - condition_threshold: - filter: metric.type="compute.googleapis.com/instance/disk/write_bytes_count" - comparison: COMPARISON_GT - threshold_value: 100 - duration: 60s - aggregations: - alignment_period: 60s - per_series_aligner: ALIGN_RATE - notification_channels: - - projects/${var.project_id}/notificationChannels/${channel_id} + - display_name: test condition + condition_threshold: + filter: | + metric.type="compute.googleapis.com/instance/disk/write_bytes_count" AND resource.type="gce_instance" + duration: 60s + comparison: COMPARISON_GT + aggregations: + alignment_period: 60s + per_series_aligner: ALIGN_RATE + user_labels: + foo: bar ``` + ## Logging Metrics -Project Logging Based Metrics can be managed via the `logging_metrics` variable +You can manage project log-based metrics using the `logging_metrics` variable or define them using a factory with `factories_config.logging_metrics`. Each YAML file should define one or more log-based metric using the same structure as the `logging_metrics` variable. + +Project log-based metrics can be managed via the `logging_metrics` variable ```hcl module "project" { source = "./fabric/modules/project" @@ -1555,56 +1522,73 @@ module "project" { parent = var.folder_id prefix = var.prefix logging_metrics = { - metric-1 = { - name = "metric-1" - filter = "resource.type=\"gce_instance\"" + metric-1 = { + name = "metric-1" + filter = "resource.type=\"gce_instance\"" description = "This is a metric" metric_descriptor = { - metric_kind = "GAUGE" - value_type = "DOUBLE" - unit = "ms" + metric_kind = "GAUGE" + value_type = "DOUBLE" + unit = "ms" } + } + metric2 = { + filter = "resource.type=gae_app AND severity>=ERROR" + metric_descriptor = { + metric_kind = "DELTA" + value_type = "INT64" + labels = [ + { + key = "mass" + value_type = "STRING" + description = "amount of matter" + } + ] } + label_extractors = { + "mass" = "EXTRACT(jsonPayload.request)" + } + } } - services = [ - "cloudquotas.googleapis.com", - "compute.googleapis.com" - ] -} -``` - -## Logging Metrics factory - -Logging Metrics can be also specified via a factory in a similar way to organization policies, policy constraints and custom roles by pointing to a directory containing YAML files where each file defines one or more Logging Metrics. The structure of the YAML files is exactly the same as the `logging_metrics` variable. - -```hcl -module "project" { - source = "./fabric/modules/project" - name = "project" - billing_account = var.billing_account_id - parent = var.folder_id - prefix = var.prefix - factories_config = { - logging_metrics = "data/logging_metrics" - } - services = [ - "cloudquotas.googleapis.com", - "compute.googleapis.com" - ] } +# tftest modules=1 resources=4 files=metrics-1 ``` ```yaml -metric-1: - name: metric-1 - filter: resource.type="gce_instance" - description: This is a metric +# tftest-file id=metrics-1 path=data/notification-channels/metrics-1.yaml schema=logging-metrics.schema.json +factory-metric-1: + filter: "resource.type=gae_app AND severity>=ERROR" metric_descriptor: - metric_kind: GAUGE - value_type: DOUBLE - unit: ms + metric_kind: DELTA + value_type: INT64 + disabled: true + +factory-metric-2: + filter: resource.type=gae_app AND severity>=ERROR + metric_descriptor: + metric_kind: DELTA + value_type: DISTRIBUTION + unit: "1" + labels: + - key: mass + value_type: STRING + description: amount of matter + - key: sku + value_type: INT64 + description: Identifying number for item + display_name: My metric + value_extractor: EXTRACT(jsonPayload.request) + label_extractors: + mass: EXTRACT(jsonPayload.request) + sku: EXTRACT(jsonPayload.id) + bucket_options: + linear_buckets: + num_finite_buckets: 3 + width: 1 + offset: 1 ``` + ## Files @@ -1637,7 +1621,7 @@ metric-1: | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [name](variables.tf#L108) | Project name and id suffix. | string | ✓ | | -| [alerts](variables-observability.tf#L17) | Logging metrics alerts configuration. | map(object({…})) | | {} | +| [alerts](variables-observability.tf#L17) | Monitoring alerts. | map(object({…})) | | {} | | [auto_create_network](variables.tf#L17) | Whether to create the default network for the project. | bool | | false | | [billing_account](variables.tf#L23) | Billing account id. | string | | null | | [compute_metadata](variables.tf#L29) | Optional compute metadata key/values. Only usable if compute API has been enabled. | map(string) | | {} | @@ -1656,11 +1640,11 @@ metric-1: | [log_scopes](variables-observability.tf#L117) | Log scopes under this project. | map(object({…})) | | {} | | [logging_data_access](variables-observability.tf#L127) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | | [logging_exclusions](variables-observability.tf#L142) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | -| [logging_metrics](variables-observability.tf#L149) | Logging metrics alerts configuration. | map(object({…})) | | {} | -| [logging_sinks](variables-observability.tf#L188) | Logging sinks to create for this project. | map(object({…})) | | {} | -| [metric_scopes](variables-observability.tf#L219) | List of projects that will act as metric scopes for this project. | list(string) | | [] | +| [logging_metrics](variables-observability.tf#L149) | Log-based metrics. | map(object({…})) | | {} | +| [logging_sinks](variables-observability.tf#L189) | Logging sinks to create for this project. | map(object({…})) | | {} | +| [metric_scopes](variables-observability.tf#L220) | List of projects that will act as metric scopes for this project. | list(string) | | [] | | [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [notification_channels](variables-observability.tf#L226) | Logging metrics alerts configuration. | map(object({…})) | | {} | +| [notification_channels](variables-observability.tf#L227) | Monitoring notification channels. | map(object({…})) | | {} | | [org_policies](variables.tf#L113) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | | [parent](variables.tf#L140) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | | [prefix](variables.tf#L150) | Optional prefix used to generate project id and name. | string | | null | diff --git a/modules/project/alerts.tf b/modules/project/alerts.tf index 86bed202ec..da02a0d712 100644 --- a/modules/project/alerts.tf +++ b/modules/project/alerts.tf @@ -25,7 +25,7 @@ locals { combiner = v.combiner display_name = try(v.display_name, null) enabled = try(v.enabled, null) - notification_channels = try(v.notification_channels, null) + notification_channels = try(v.notification_channels, []) severity = try(v.severity, null) user_labels = try(v.user_labels, null) alert_strategy = !can(v.alert_strategy) ? null : { @@ -121,6 +121,10 @@ locals { } } } + _notification_channel_names = { + for k, v in google_monitoring_notification_channel.channels : + k => v.name + } alerts = merge(local._alerts_factory_data, var.alerts) } @@ -128,12 +132,15 @@ resource "google_monitoring_alert_policy" "alerts" { for_each = local.alerts project = local.project.project_id - combiner = each.value.combiner - display_name = each.value.display_name - enabled = each.value.enabled - notification_channels = each.value.notification_channels - severity = each.value.severity - user_labels = each.value.user_labels + combiner = each.value.combiner + display_name = each.value.display_name + enabled = each.value.enabled + notification_channels = [ + for x in each.value.notification_channels : + lookup(local._notification_channel_names, x, x) + ] + severity = each.value.severity + user_labels = each.value.user_labels dynamic "alert_strategy" { for_each = each.value.alert_strategy[*] diff --git a/modules/project/logging-metrics.tf b/modules/project/logging-metrics.tf index f5e53b77d8..15bf2304f4 100644 --- a/modules/project/logging-metrics.tf +++ b/modules/project/logging-metrics.tf @@ -49,13 +49,14 @@ locals { value_type = v.metric_descriptor.value_type display_name = try(v.metric_descriptor.display_name, null) unit = try(v.metric_descriptor.unit, null) - labels = !can(v.metric_descriptor.labels) ? {} : { - for kk, vv in v.metric_descriptor.labels : - kk => { + labels = !can(v.metric_descriptor.labels) ? [] : [ + for vv in v.metric_descriptor.labels : + { + key = vv.key description = try(vv.description, null) value_type = try(vv.value_type, null) } - } + ] } } } @@ -110,7 +111,7 @@ resource "google_logging_metric" "metrics" { dynamic "labels" { for_each = metric_descriptor.value.labels content { - key = labels.key + key = labels.value.key description = labels.value.description value_type = labels.value.value_type } diff --git a/modules/project/notification-channels.tf b/modules/project/notification-channels.tf new file mode 100644 index 0000000000..59963eb6dd --- /dev/null +++ b/modules/project/notification-channels.tf @@ -0,0 +1,61 @@ +/** + * Copyright 2025 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. + */ + +locals { + _channels_factory_data_raw = merge([ + for f in try(fileset(var.factories_config.notification_channels, "*.yaml"), []) : + yamldecode(file("${var.factories_config.notification_channels}/${f}")) + ]...) + # TODO: do we want to allow multiple channels in a single file? + _channels_factory_data = { + for k, v in local._channels_factory_data_raw : + k => { + type = v.type + description = try(v.description, null) + display_name = try(v.display_name, null) + enabled = try(v.enabled, null) + labels = try(v.labels, null) + user_labels = try(v.user_labels, null) + sensitive_labels = !can(v.sensitive_labels) ? null : { + auth_token = try(v.sensitive_labels.auth_token, null) + password = try(v.sensitive_labels.password, null) + service_key = try(v.sensitive_labels.service_key, null) + } + } + } + channels = merge(local._channels_factory_data, var.notification_channels) +} + +resource "google_monitoring_notification_channel" "channels" { + for_each = local.channels + project = local.project.project_id + type = each.value.type + description = each.value.description + display_name = each.value.display_name + enabled = each.value.enabled + labels = each.value.labels + user_labels = each.value.user_labels + dynamic "sensitive_labels" { + for_each = each.value.sensitive_labels[*] + content { + auth_token = sensitive_labels.value.auth_token + password = sensitive_labels.value.password + service_key = sensitive_labels.value.service_key + } + } +} + + diff --git a/modules/project/schemas/alerts.schema.json b/modules/project/schemas/alerts.schema.json new file mode 100644 index 0000000000..2197bb81b8 --- /dev/null +++ b/modules/project/schemas/alerts.schema.json @@ -0,0 +1,315 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Alerts", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "combiner": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "notification_channels": { + "type": "array", + "items": { + "type": "string" + } + }, + "severity": { + "type": "string" + }, + "user_labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "alert_strategy": { + "type": "object", + "additionalProperties": false, + "properties": { + "auto_close": { + "type": "string" + }, + "notification_prompts": { + "type": "string" + }, + "notification_rate_limit": { + "type": "object", + "additionalProperties": false, + "properties": { + "period": { + "type": "string" + } + } + }, + "notification_channel_strategy": { + "type": "object", + "additionalProperties": false, + "properties": { + "notification_channel_names": { + "type": "array", + "items": { + "type": "string" + } + }, + "renotify_interval": { + "type": "string" + } + } + } + } + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/$defs/condition" + } + }, + "documentation": { + "type": "object", + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "mime_type": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "display_name": { + "type": "string" + }, + "url": { + "type": "string" + } + } + } + } + } + } + }, + "required": [ + "combiner" + ] + } + }, + "$defs": { + "condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "display_name": { + "type": "string" + }, + "condition_absent": { + "$ref": "#/$defs/absent_condition" + }, + "condition_matched_log": { + "$ref": "#/$defs/matched_log_condition" + }, + "condition_monitoring_query_language": { + "$ref": "#/$defs/monitoring_query_condition" + }, + "condition_prometheus_query_language": { + "$ref": "#/$defs/prometheus_query_condition" + }, + "condition_threshold": { + "$ref": "#/$defs/threshold_condition" + } + }, + "required": [ + "display_name" + ] + }, + "absent_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "duration": { + "type": "string" + }, + "filter": { + "type": "string" + }, + "aggregations": { + "$ref": "#/$defs/aggregations" + }, + "trigger": { + "$ref": "#/$defs/trigger" + } + }, + "required": [ + "duration" + ] + }, + "matched_log_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "filter": { + "type": "string" + }, + "label_extractors": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "filter" + ] + }, + "monitoring_query_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "duration": { + "type": "string" + }, + "query": { + "type": "string" + }, + "evaluation_missing_data": { + "type": "string" + }, + "trigger": { + "$ref": "#/$defs/trigger" + } + }, + "required": [ + "duration", + "query" + ] + }, + "prometheus_query_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + }, + "alert_rule": { + "type": "string" + }, + "disable_metric_validation": { + "type": "boolean" + }, + "duration": { + "type": "string" + }, + "evaluation_interval": { + "type": "string" + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "rule_group": { + "type": "string" + } + }, + "required": [ + "query" + ] + }, + "threshold_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "comparison": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "denominator_filter": { + "type": "string" + }, + "evaluation_missing_data": { + "type": "string" + }, + "filter": { + "type": "string" + }, + "threshold_value": { + "type": "number" + }, + "aggregations": { + "$ref": "#/$defs/aggregations" + }, + "denominator_aggregations": { + "$ref": "#/$defs/aggregations" + }, + "forecast_options": { + "type": "object", + "additionalProperties": false, + "properties": { + "forecast_horizon": { + "type": "string" + } + } + }, + "trigger": { + "$ref": "#/$defs/trigger" + } + }, + "required": [ + "comparison", + "duration" + ] + }, + "aggregations": { + "type": "object", + "additionalProperties": false, + "properties": { + "per_series_aligner": { + "type": "string" + }, + "group_by_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "cross_series_reducer": { + "type": "string" + }, + "alignment_period": { + "type": "string" + } + } + }, + "trigger": { + "type": "object", + "additionalProperties": false, + "properties": { + "count": { + "type": "number" + }, + "percent": { + "type": "number" + } + } + } + } +} diff --git a/modules/project/schemas/logging-metrics.schema.json b/modules/project/schemas/logging-metrics.schema.json new file mode 100644 index 0000000000..2dd1f516fa --- /dev/null +++ b/modules/project/schemas/logging-metrics.schema.json @@ -0,0 +1,129 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Logging Metrics", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "filter": { + "type": "string" + }, + "bucket_name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "label_extractors": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "value_extractor": { + "type": "string" + }, + "bucket_options": { + "type": "object", + "additionalProperties": false, + "properties": { + "explicit_buckets": { + "type": "object", + "additionalProperties": false, + "properties": { + "bounds": { + "type": "array", + "items": { + "type": "number" + } + } + } + }, + "exponential_buckets": { + "type": "object", + "additionalProperties": false, + "properties": { + "num_finite_buckets": { + "type": "number" + }, + "growth_factor": { + "type": "number" + }, + "scale": { + "type": "number" + } + } + }, + "linear_buckets": { + "type": "object", + "additionalProperties": false, + "properties": { + "num_finite_buckets": { + "type": "number" + }, + "width": { + "type": "number" + }, + "offset": { + "type": "number" + } + } + } + } + }, + "metric_descriptor": { + "type": "object", + "additionalProperties": false, + "properties": { + "metric_kind": { + "type": "string" + }, + "value_type": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value_type": { + "type": "string" + } + }, + "required": [ + "key" + ] + } + } + }, + "required": [ + "metric_kind", + "value_type" + ] + } + }, + "required": [ + "filter" + ] + } + } +} diff --git a/modules/project/schemas/notification-channels.schema.json b/modules/project/schemas/notification-channels.schema.json new file mode 100644 index 0000000000..7971639513 --- /dev/null +++ b/modules/project/schemas/notification-channels.schema.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Notification Channels", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "user_labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "sensitive_labels": { + "type": "object", + "additionalProperties": false, + "properties": { + "auth_token": { + "type": "string" + }, + "password": { + "type": "string" + }, + "service_key": { + "type": "string" + } + } + } + }, + "required": [ + "type" + ] + } + } +} diff --git a/modules/project/variables-observability.tf b/modules/project/variables-observability.tf index 93984447e3..56020d70ac 100644 --- a/modules/project/variables-observability.tf +++ b/modules/project/variables-observability.tf @@ -15,12 +15,12 @@ */ variable "alerts" { - description = "Logging metrics alerts configuration." + description = "Monitoring alerts." type = map(object({ combiner = string display_name = optional(string) enabled = optional(bool) - notification_channels = optional(list(string)) + notification_channels = optional(list(string), []) severity = optional(string) user_labels = optional(map(string)) alert_strategy = optional(object({ @@ -147,7 +147,7 @@ variable "logging_exclusions" { } variable "logging_metrics" { - description = "Logging metrics alerts configuration." + description = "Log-based metrics." type = map(object({ filter = string bucket_name = optional(string) @@ -175,10 +175,11 @@ variable "logging_metrics" { value_type = string display_name = optional(string) unit = optional(string) - labels = optional(map(object({ + labels = optional(list(object({ + key = string description = optional(string) value_type = optional(string) - })), {}) + })), []) })) })) nullable = false @@ -224,7 +225,7 @@ variable "metric_scopes" { } variable "notification_channels" { - description = "Logging metrics alerts configuration." + description = "Monitoring notification channels." type = map(object({ type = string description = optional(string) From b0eec9f25b0d5e628fe2fec0fd9cba482e924bdb Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 3 Jan 2025 17:29:08 +0100 Subject: [PATCH 24/33] Remove default alerts email from project and project-factory --- modules/project-factory/main.tf | 9 ++++----- modules/project-factory/variables.tf | 8 +------- modules/project/variables.tf | 8 +------- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/modules/project-factory/main.tf b/modules/project-factory/main.tf index 8f33016022..044ea81bb0 100644 --- a/modules/project-factory/main.tf +++ b/modules/project-factory/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,10 +40,9 @@ module "projects" { parent = lookup( local.context.folder_ids, each.value.parent, each.value.parent ) - default_alerts_email = "" - prefix = each.value.prefix - auto_create_network = try(each.value.auto_create_network, false) - compute_metadata = try(each.value.compute_metadata, {}) + prefix = each.value.prefix + auto_create_network = try(each.value.auto_create_network, false) + compute_metadata = try(each.value.compute_metadata, {}) # TODO: concat lists for each key contacts = merge( each.value.contacts, var.data_merges.contacts diff --git a/modules/project-factory/variables.tf b/modules/project-factory/variables.tf index 721bfc3e4a..9f45e04d79 100644 --- a/modules/project-factory/variables.tf +++ b/modules/project-factory/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,12 +97,6 @@ variable "data_overrides" { default = {} } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "factories_config" { description = "Path to folder with YAML resource description data files." type = object({ diff --git a/modules/project/variables.tf b/modules/project/variables.tf index eb3511aed3..d7ca7f87c8 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,12 +47,6 @@ variable "custom_roles" { nullable = false } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "default_service_account" { description = "Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`." default = "keep" From 2789313938b6c6768215d14ec3fe44d16349309f Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Sat, 4 Jan 2025 00:11:00 +0100 Subject: [PATCH 25/33] Move observability factory to a single file --- modules/project/README.md | 239 ++++---- modules/project/alerts.tf | 13 +- modules/project/logging-metrics.tf | 5 +- modules/project/main.tf | 6 +- modules/project/notification-channels.tf | 5 +- modules/project/schemas/alerts.schema.json | 315 ----------- .../schemas/logging-metrics.schema.json | 129 ----- .../schemas/notification-channels.schema.json | 56 -- .../project/schemas/observability.schema.json | 514 ++++++++++++++++++ modules/project/variables.tf | 13 +- 10 files changed, 639 insertions(+), 656 deletions(-) delete mode 100644 modules/project/schemas/alerts.schema.json delete mode 100644 modules/project/schemas/logging-metrics.schema.json delete mode 100644 modules/project/schemas/notification-channels.schema.json create mode 100644 modules/project/schemas/observability.schema.json diff --git a/modules/project/README.md b/modules/project/README.md index b53d67c395..535afbb2e3 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -30,9 +30,8 @@ This module implements the creation and management of one GCP project including - [VPC Service Controls](#vpc-service-controls) - [Project Related Outputs](#project-related-outputs) - [Managing project related configuration without creating it](#managing-project-related-configuration-without-creating-it) -- [Notification Channels](#notification-channels) -- [Alerts](#alerts) -- [Logging Metrics](#logging-metrics) +- [Observability](#observability) +- [Observability factory](#observability-factory) - [Files](#files) - [Variables](#variables) - [Outputs](#outputs) @@ -1404,45 +1403,9 @@ module "bucket" { # tftest inventory=data.yaml e2e ``` -## Notification Channels +## Observability -You can manage project notification channels using the `notification_channels` variable or define them using a factory with `factories_config.notification_channels`. Each YAML file should define one or more notification channels using the same structure as the `notification_channels` variable. - -```hcl -module "project" { - source = "./fabric/modules/project" - name = "project" - billing_account = var.billing_account_id - parent = var.folder_id - prefix = var.prefix - notification_channels = { - channel-1 = { - display_name = "channel-1" - type = "email" - labels = { - email_address = "hello@example.com" - } - } - } - factories_config = { - notification_channels = "data/notification-channels" - } -} -# tftest modules=1 resources=3 files=channel-1 -``` - -```yaml -# tftest-file id=channel-1 path=data/notification-channels/channel-1.yaml schema=notification-channels.schema.json -channel-1-from-yaml: - display_name: channel-1 - type: email - labels: - email_address: hello2@example.com -``` - -## Alerts -Project alerts can be managed via the `alerts` variable. Notice that alerts can reference notificatin either using thei fully qualified ID or channels using the channel key specified in the `notification_channels` variable (or the key in the YAML file if using a factory). -You can also define alerts using a factory with `factories_config.alerts`. Each YAML file should define one or more notification channels using the same structure as the `alerts` variable. +Alerting policies, log-based metrics, and notification channels are managed by the `alerts`, `logging_metrics`, and `notification_channels` variables, respectively. ```hcl module "project" { @@ -1474,6 +1437,18 @@ module "project" { }] } } + logging_metrics = { + metric-1 = { + name = "metric-1" + filter = "resource.type=\"gce_instance\"" + description = "This is a metric" + metric_descriptor = { + metric_kind = "GAUGE" + value_type = "DOUBLE" + unit = "ms" + } + } + } notification_channels = { my-channel = { display_name = "My Channel" @@ -1483,37 +1458,14 @@ module "project" { } } } - factories_config = { - alerts = "data/alerts" - } } -# tftest modules=1 resources=4 files=alerts-1 +# tftest modules=1 resources=4 ``` -```yaml -# tftest-file id=alerts-1 path=data/alerts/channel-1.yaml schema=alerts.schema.json -alert-2: - display_name: My Alert Policy - combiner: OR - conditions: - - display_name: test condition - condition_threshold: - filter: | - metric.type="compute.googleapis.com/instance/disk/write_bytes_count" AND resource.type="gce_instance" - duration: 60s - comparison: COMPARISON_GT - aggregations: - alignment_period: 60s - per_series_aligner: ALIGN_RATE - user_labels: - foo: bar -``` +# Observability factory -## Logging Metrics +Observability variables are exposed through a factory enabled by setting `var.factories_config.observability`. YAML files configure observability resources using top-level keys: `alerts`, `logging_metrics`, and `notification_channels`, which correspond to the respective variables. All top-level keys are optional, and their structure mirrors their corresponding variable's structure. -You can manage project log-based metrics using the `logging_metrics` variable or define them using a factory with `factories_config.logging_metrics`. Each YAML file should define one or more log-based metric using the same structure as the `logging_metrics` variable. - -Project log-based metrics can be managed via the `logging_metrics` variable ```hcl module "project" { source = "./fabric/modules/project" @@ -1521,71 +1473,78 @@ module "project" { billing_account = var.billing_account_id parent = var.folder_id prefix = var.prefix - logging_metrics = { - metric-1 = { - name = "metric-1" - filter = "resource.type=\"gce_instance\"" - description = "This is a metric" - metric_descriptor = { - metric_kind = "GAUGE" - value_type = "DOUBLE" - unit = "ms" - } - } - metric2 = { - filter = "resource.type=gae_app AND severity>=ERROR" - metric_descriptor = { - metric_kind = "DELTA" - value_type = "INT64" - labels = [ - { - key = "mass" - value_type = "STRING" - description = "amount of matter" - } - ] - } - label_extractors = { - "mass" = "EXTRACT(jsonPayload.request)" + factories_config = { + observability = "data/observability" + context = { + notification_channels = { + common-channel = "projects/other-project/notificationChannels/1234567890" } } } } -# tftest modules=1 resources=4 files=metrics-1 +# tftest modules=1 resources=5 files=observability ``` ```yaml -# tftest-file id=metrics-1 path=data/notification-channels/metrics-1.yaml schema=logging-metrics.schema.json -factory-metric-1: - filter: "resource.type=gae_app AND severity>=ERROR" - metric_descriptor: - metric_kind: DELTA - value_type: INT64 - disabled: true - -factory-metric-2: - filter: resource.type=gae_app AND severity>=ERROR - metric_descriptor: - metric_kind: DELTA - value_type: DISTRIBUTION - unit: "1" +# tftest-file id=observability path=data/observability/observability.yaml schema=observability.schema.json +logging_metrics: + factory-metric-1: + filter: "resource.type=gae_app AND severity>=ERROR" + metric_descriptor: + metric_kind: DELTA + value_type: INT64 + disabled: true + + factory-metric-2: + filter: resource.type=gae_app AND severity>=ERROR + metric_descriptor: + metric_kind: DELTA + value_type: DISTRIBUTION + unit: "1" + labels: + - key: mass + value_type: STRING + description: amount of matter + - key: sku + value_type: INT64 + description: Identifying number for item + display_name: My metric + value_extractor: EXTRACT(jsonPayload.request) + label_extractors: + mass: EXTRACT(jsonPayload.request) + sku: EXTRACT(jsonPayload.id) + bucket_options: + linear_buckets: + num_finite_buckets: 3 + width: 1 + offset: 1 + +notification_channels: + channel-1: + display_name: channel-1 + type: email labels: - - key: mass - value_type: STRING - description: amount of matter - - key: sku - value_type: INT64 - description: Identifying number for item - display_name: My metric - value_extractor: EXTRACT(jsonPayload.request) - label_extractors: - mass: EXTRACT(jsonPayload.request) - sku: EXTRACT(jsonPayload.id) - bucket_options: - linear_buckets: - num_finite_buckets: 3 - width: 1 - offset: 1 + email_address: hello2@example.com + +alerts: + alert-1: + display_name: My Alert Policy + combiner: OR + notification_channels: + - channel-1 + - common-channel + conditions: + - display_name: test condition + condition_threshold: + filter: | + metric.type="compute.googleapis.com/instance/disk/write_bytes_count" AND resource.type="gce_instance" + duration: 60s + comparison: COMPARISON_GT + aggregations: + alignment_period: 60s + per_series_aligner: ALIGN_RATE + user_labels: + foo: bar ``` @@ -1620,7 +1579,7 @@ factory-metric-2: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L108) | Project name and id suffix. | string | ✓ | | +| [name](variables.tf#L109) | Project name and id suffix. | string | ✓ | | | [alerts](variables-observability.tf#L17) | Monitoring alerts. | map(object({…})) | | {} | | [auto_create_network](variables.tf#L17) | Whether to create the default network for the project. | bool | | false | | [billing_account](variables.tf#L23) | Billing account id. | string | | null | @@ -1630,13 +1589,13 @@ factory-metric-2: | [default_service_account](variables.tf#L50) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | string | | "keep" | | [deletion_policy](variables.tf#L64) | Deletion policy setting for this project. | string | | "DELETE" | | [descriptive_name](variables.tf#L75) | Name of the project name. Used for project name instead of `name` variable. | string | | null | -| [factories_config](variables.tf#L81) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | +| [factories_config](variables.tf#L81) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | | [iam](variables-iam.tf#L17) | Authoritative IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | [iam_bindings](variables-iam.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…})) | | {} | | [iam_bindings_additive](variables-iam.tf#L39) | Individual additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | [iam_by_principals](variables-iam.tf#L54) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | -| [labels](variables.tf#L95) | Resource labels. | map(string) | | {} | -| [lien_reason](variables.tf#L102) | If non-empty, creates a project lien with this description. | string | | null | +| [labels](variables.tf#L96) | Resource labels. | map(string) | | {} | +| [lien_reason](variables.tf#L103) | If non-empty, creates a project lien with this description. | string | | null | | [log_scopes](variables-observability.tf#L117) | Log scopes under this project. | map(object({…})) | | {} | | [logging_data_access](variables-observability.tf#L127) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | map(map(list(string))) | | {} | | [logging_exclusions](variables-observability.tf#L142) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string) | | {} | @@ -1645,21 +1604,21 @@ factory-metric-2: | [metric_scopes](variables-observability.tf#L220) | List of projects that will act as metric scopes for this project. | list(string) | | [] | | [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | [notification_channels](variables-observability.tf#L227) | Monitoring notification channels. | map(object({…})) | | {} | -| [org_policies](variables.tf#L113) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | -| [parent](variables.tf#L140) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | -| [prefix](variables.tf#L150) | Optional prefix used to generate project id and name. | string | | null | -| [project_create](variables.tf#L160) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | +| [org_policies](variables.tf#L114) | Organization policies applied to this project keyed by policy name. | map(object({…})) | | {} | +| [parent](variables.tf#L141) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string | | null | +| [prefix](variables.tf#L151) | Optional prefix used to generate project id and name. | string | | null | +| [project_create](variables.tf#L161) | Create project. When set to false, uses a data source to reference existing project. | bool | | true | | [quotas](variables-quotas.tf#L17) | Service quota configuration. | map(object({…})) | | {} | -| [service_agents_config](variables.tf#L166) | Automatic service agent configuration options. | object({…}) | | {} | -| [service_config](variables.tf#L177) | Configure service API activation. | object({…}) | | {…} | -| [service_encryption_key_ids](variables.tf#L189) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | -| [services](variables.tf#L196) | Service APIs to enable. | list(string) | | [] | -| [shared_vpc_host_config](variables.tf#L202) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | -| [shared_vpc_service_config](variables.tf#L211) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | -| [skip_delete](variables.tf#L239) | Deprecated. Use deletion_policy. | bool | | null | +| [service_agents_config](variables.tf#L167) | Automatic service agent configuration options. | object({…}) | | {} | +| [service_config](variables.tf#L178) | Configure service API activation. | object({…}) | | {…} | +| [service_encryption_key_ids](variables.tf#L190) | Service Agents to be granted encryption/decryption permissions over Cloud KMS encryption keys. Format {SERVICE_AGENT => [KEY_ID]}. | map(list(string)) | | {} | +| [services](variables.tf#L197) | Service APIs to enable. | list(string) | | [] | +| [shared_vpc_host_config](variables.tf#L203) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | +| [shared_vpc_service_config](variables.tf#L212) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | {…} | +| [skip_delete](variables.tf#L240) | Deprecated. Use deletion_policy. | bool | | null | | [tag_bindings](variables-tags.tf#L81) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | | [tags](variables-tags.tf#L88) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | -| [vpc_sc](variables.tf#L251) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | +| [vpc_sc](variables.tf#L252) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…}) | | null | ## Outputs diff --git a/modules/project/alerts.tf b/modules/project/alerts.tf index da02a0d712..5ec6fa2a60 100644 --- a/modules/project/alerts.tf +++ b/modules/project/alerts.tf @@ -16,8 +16,8 @@ locals { _alerts_factory_data_raw = merge([ - for f in try(fileset(var.factories_config.alerts, "*.yaml"), []) : - yamldecode(file("${var.factories_config.alerts}/${f}")) + for k in local.observability_factory_data_raw : + lookup(k, "alerts", {}) ]...) _alerts_factory_data = { for k, v in local._alerts_factory_data_raw : @@ -137,7 +137,14 @@ resource "google_monitoring_alert_policy" "alerts" { enabled = each.value.enabled notification_channels = [ for x in each.value.notification_channels : - lookup(local._notification_channel_names, x, x) + try( + # first try to get a channel created by this module + google_monitoring_notification_channel.channels[x].name, + # otherwise check the context + var.factories_config.context.notification_channels[x], + # if nothing else, use the provided channel as is + x + ) ] severity = each.value.severity user_labels = each.value.user_labels diff --git a/modules/project/logging-metrics.tf b/modules/project/logging-metrics.tf index 15bf2304f4..f465586908 100644 --- a/modules/project/logging-metrics.tf +++ b/modules/project/logging-metrics.tf @@ -16,10 +16,9 @@ locals { _logging_metrics_factory_data_raw = merge([ - for f in try(fileset(var.factories_config.logging_metrics, "*.yaml"), []) : - yamldecode(file("${var.factories_config.logging_metrics}/${f}")) + for k in local.observability_factory_data_raw : + lookup(k, "logging_metrics", {}) ]...) - # TODO: do we want to allow multiple metrics in a single file? _logging_metrics_factory_data = { for k, v in local._logging_metrics_factory_data_raw : k => { diff --git a/modules/project/main.tf b/modules/project/main.tf index a90361d1ef..25d29fb0d6 100644 --- a/modules/project/main.tf +++ b/modules/project/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,10 @@ locals { descriptive_name = ( var.descriptive_name != null ? var.descriptive_name : "${local.prefix}${var.name}" ) + observability_factory_data_raw = [ + for f in try(fileset(var.factories_config.observability, "*.yaml"), []) : + yamldecode(file("${var.factories_config.observability}/${f}")) + ] parent_type = var.parent == null ? null : split("/", var.parent)[0] parent_id = var.parent == null ? null : split("/", var.parent)[1] prefix = var.prefix == null ? "" : "${var.prefix}-" diff --git a/modules/project/notification-channels.tf b/modules/project/notification-channels.tf index 59963eb6dd..45d1678c63 100644 --- a/modules/project/notification-channels.tf +++ b/modules/project/notification-channels.tf @@ -16,10 +16,9 @@ locals { _channels_factory_data_raw = merge([ - for f in try(fileset(var.factories_config.notification_channels, "*.yaml"), []) : - yamldecode(file("${var.factories_config.notification_channels}/${f}")) + for k in local.observability_factory_data_raw : + lookup(k, "notification_channels", {}) ]...) - # TODO: do we want to allow multiple channels in a single file? _channels_factory_data = { for k, v in local._channels_factory_data_raw : k => { diff --git a/modules/project/schemas/alerts.schema.json b/modules/project/schemas/alerts.schema.json deleted file mode 100644 index 2197bb81b8..0000000000 --- a/modules/project/schemas/alerts.schema.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Alerts", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9-]+$": { - "type": "object", - "additionalProperties": false, - "properties": { - "combiner": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "notification_channels": { - "type": "array", - "items": { - "type": "string" - } - }, - "severity": { - "type": "string" - }, - "user_labels": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "alert_strategy": { - "type": "object", - "additionalProperties": false, - "properties": { - "auto_close": { - "type": "string" - }, - "notification_prompts": { - "type": "string" - }, - "notification_rate_limit": { - "type": "object", - "additionalProperties": false, - "properties": { - "period": { - "type": "string" - } - } - }, - "notification_channel_strategy": { - "type": "object", - "additionalProperties": false, - "properties": { - "notification_channel_names": { - "type": "array", - "items": { - "type": "string" - } - }, - "renotify_interval": { - "type": "string" - } - } - } - } - }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/$defs/condition" - } - }, - "documentation": { - "type": "object", - "additionalProperties": false, - "properties": { - "content": { - "type": "string" - }, - "mime_type": { - "type": "string" - }, - "subject": { - "type": "string" - }, - "links": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "display_name": { - "type": "string" - }, - "url": { - "type": "string" - } - } - } - } - } - } - }, - "required": [ - "combiner" - ] - } - }, - "$defs": { - "condition": { - "type": "object", - "additionalProperties": false, - "properties": { - "display_name": { - "type": "string" - }, - "condition_absent": { - "$ref": "#/$defs/absent_condition" - }, - "condition_matched_log": { - "$ref": "#/$defs/matched_log_condition" - }, - "condition_monitoring_query_language": { - "$ref": "#/$defs/monitoring_query_condition" - }, - "condition_prometheus_query_language": { - "$ref": "#/$defs/prometheus_query_condition" - }, - "condition_threshold": { - "$ref": "#/$defs/threshold_condition" - } - }, - "required": [ - "display_name" - ] - }, - "absent_condition": { - "type": "object", - "additionalProperties": false, - "properties": { - "duration": { - "type": "string" - }, - "filter": { - "type": "string" - }, - "aggregations": { - "$ref": "#/$defs/aggregations" - }, - "trigger": { - "$ref": "#/$defs/trigger" - } - }, - "required": [ - "duration" - ] - }, - "matched_log_condition": { - "type": "object", - "additionalProperties": false, - "properties": { - "filter": { - "type": "string" - }, - "label_extractors": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "required": [ - "filter" - ] - }, - "monitoring_query_condition": { - "type": "object", - "additionalProperties": false, - "properties": { - "duration": { - "type": "string" - }, - "query": { - "type": "string" - }, - "evaluation_missing_data": { - "type": "string" - }, - "trigger": { - "$ref": "#/$defs/trigger" - } - }, - "required": [ - "duration", - "query" - ] - }, - "prometheus_query_condition": { - "type": "object", - "additionalProperties": false, - "properties": { - "query": { - "type": "string" - }, - "alert_rule": { - "type": "string" - }, - "disable_metric_validation": { - "type": "boolean" - }, - "duration": { - "type": "string" - }, - "evaluation_interval": { - "type": "string" - }, - "labels": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "rule_group": { - "type": "string" - } - }, - "required": [ - "query" - ] - }, - "threshold_condition": { - "type": "object", - "additionalProperties": false, - "properties": { - "comparison": { - "type": "string" - }, - "duration": { - "type": "string" - }, - "denominator_filter": { - "type": "string" - }, - "evaluation_missing_data": { - "type": "string" - }, - "filter": { - "type": "string" - }, - "threshold_value": { - "type": "number" - }, - "aggregations": { - "$ref": "#/$defs/aggregations" - }, - "denominator_aggregations": { - "$ref": "#/$defs/aggregations" - }, - "forecast_options": { - "type": "object", - "additionalProperties": false, - "properties": { - "forecast_horizon": { - "type": "string" - } - } - }, - "trigger": { - "$ref": "#/$defs/trigger" - } - }, - "required": [ - "comparison", - "duration" - ] - }, - "aggregations": { - "type": "object", - "additionalProperties": false, - "properties": { - "per_series_aligner": { - "type": "string" - }, - "group_by_fields": { - "type": "array", - "items": { - "type": "string" - } - }, - "cross_series_reducer": { - "type": "string" - }, - "alignment_period": { - "type": "string" - } - } - }, - "trigger": { - "type": "object", - "additionalProperties": false, - "properties": { - "count": { - "type": "number" - }, - "percent": { - "type": "number" - } - } - } - } -} diff --git a/modules/project/schemas/logging-metrics.schema.json b/modules/project/schemas/logging-metrics.schema.json deleted file mode 100644 index 2dd1f516fa..0000000000 --- a/modules/project/schemas/logging-metrics.schema.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Logging Metrics", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9-]+$": { - "type": "object", - "additionalProperties": false, - "properties": { - "filter": { - "type": "string" - }, - "bucket_name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "disabled": { - "type": "boolean" - }, - "label_extractors": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "value_extractor": { - "type": "string" - }, - "bucket_options": { - "type": "object", - "additionalProperties": false, - "properties": { - "explicit_buckets": { - "type": "object", - "additionalProperties": false, - "properties": { - "bounds": { - "type": "array", - "items": { - "type": "number" - } - } - } - }, - "exponential_buckets": { - "type": "object", - "additionalProperties": false, - "properties": { - "num_finite_buckets": { - "type": "number" - }, - "growth_factor": { - "type": "number" - }, - "scale": { - "type": "number" - } - } - }, - "linear_buckets": { - "type": "object", - "additionalProperties": false, - "properties": { - "num_finite_buckets": { - "type": "number" - }, - "width": { - "type": "number" - }, - "offset": { - "type": "number" - } - } - } - } - }, - "metric_descriptor": { - "type": "object", - "additionalProperties": false, - "properties": { - "metric_kind": { - "type": "string" - }, - "value_type": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "unit": { - "type": "string" - }, - "labels": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string" - }, - "description": { - "type": "string" - }, - "value_type": { - "type": "string" - } - }, - "required": [ - "key" - ] - } - } - }, - "required": [ - "metric_kind", - "value_type" - ] - } - }, - "required": [ - "filter" - ] - } - } -} diff --git a/modules/project/schemas/notification-channels.schema.json b/modules/project/schemas/notification-channels.schema.json deleted file mode 100644 index 7971639513..0000000000 --- a/modules/project/schemas/notification-channels.schema.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Notification Channels", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^[a-zA-Z0-9-]+$": { - "type": "object", - "additionalProperties": false, - "properties": { - "type": { - "type": "string" - }, - "description": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "labels": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "user_labels": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "sensitive_labels": { - "type": "object", - "additionalProperties": false, - "properties": { - "auth_token": { - "type": "string" - }, - "password": { - "type": "string" - }, - "service_key": { - "type": "string" - } - } - } - }, - "required": [ - "type" - ] - } - } -} diff --git a/modules/project/schemas/observability.schema.json b/modules/project/schemas/observability.schema.json new file mode 100644 index 0000000000..cf3eb2f0a7 --- /dev/null +++ b/modules/project/schemas/observability.schema.json @@ -0,0 +1,514 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Observability Schema", + "type": "object", + "additionalProperties": false, + "properties": { + "alerts": { + "$ref": "#/$defs/alerts" + }, + "logging_metrics": { + "$ref": "#/$defs/logging_metrics" + }, + "notification_channels": { + "$ref": "#/$defs/notification_channels" + } + }, + "$defs": { + "alerts": { + "title": "Alerts", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "combiner": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "notification_channels": { + "type": "array", + "items": { + "type": "string" + } + }, + "severity": { + "type": "string" + }, + "user_labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "alert_strategy": { + "type": "object", + "additionalProperties": false, + "properties": { + "auto_close": { + "type": "string" + }, + "notification_prompts": { + "type": "string" + }, + "notification_rate_limit": { + "type": "object", + "additionalProperties": false, + "properties": { + "period": { + "type": "string" + } + } + }, + "notification_channel_strategy": { + "type": "object", + "additionalProperties": false, + "properties": { + "notification_channel_names": { + "type": "array", + "items": { + "type": "string" + } + }, + "renotify_interval": { + "type": "string" + } + } + } + } + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/$defs/condition" + } + }, + "documentation": { + "type": "object", + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "mime_type": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "links": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "display_name": { + "type": "string" + }, + "url": { + "type": "string" + } + } + } + } + } + } + }, + "required": [ + "combiner" + ] + } + } + }, + "logging_metrics": { + "title": "Logging Metrics", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "filter": { + "type": "string" + }, + "bucket_name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "label_extractors": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "value_extractor": { + "type": "string" + }, + "bucket_options": { + "type": "object", + "additionalProperties": false, + "properties": { + "explicit_buckets": { + "type": "object", + "additionalProperties": false, + "properties": { + "bounds": { + "type": "array", + "items": { + "type": "number" + } + } + } + }, + "exponential_buckets": { + "type": "object", + "additionalProperties": false, + "properties": { + "num_finite_buckets": { + "type": "number" + }, + "growth_factor": { + "type": "number" + }, + "scale": { + "type": "number" + } + } + }, + "linear_buckets": { + "type": "object", + "additionalProperties": false, + "properties": { + "num_finite_buckets": { + "type": "number" + }, + "width": { + "type": "number" + }, + "offset": { + "type": "number" + } + } + } + } + }, + "metric_descriptor": { + "type": "object", + "additionalProperties": false, + "properties": { + "metric_kind": { + "type": "string" + }, + "value_type": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "labels": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value_type": { + "type": "string" + } + }, + "required": [ + "key" + ] + } + } + }, + "required": [ + "metric_kind", + "value_type" + ] + } + }, + "required": [ + "filter" + ] + } + } + }, + "notification_channels": { + "title": "Notification Channels", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "user_labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "sensitive_labels": { + "type": "object", + "additionalProperties": false, + "properties": { + "auth_token": { + "type": "string" + }, + "password": { + "type": "string" + }, + "service_key": { + "type": "string" + } + } + } + }, + "required": [ + "type" + ] + } + } + }, + "condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "display_name": { + "type": "string" + }, + "condition_absent": { + "$ref": "#/$defs/absent_condition" + }, + "condition_matched_log": { + "$ref": "#/$defs/matched_log_condition" + }, + "condition_monitoring_query_language": { + "$ref": "#/$defs/monitoring_query_condition" + }, + "condition_prometheus_query_language": { + "$ref": "#/$defs/prometheus_query_condition" + }, + "condition_threshold": { + "$ref": "#/$defs/threshold_condition" + } + }, + "required": [ + "display_name" + ] + }, + "absent_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "duration": { + "type": "string" + }, + "filter": { + "type": "string" + }, + "aggregations": { + "$ref": "#/$defs/aggregations" + }, + "trigger": { + "$ref": "#/$defs/trigger" + } + }, + "required": [ + "duration" + ] + }, + "matched_log_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "filter": { + "type": "string" + }, + "label_extractors": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "filter" + ] + }, + "monitoring_query_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "duration": { + "type": "string" + }, + "query": { + "type": "string" + }, + "evaluation_missing_data": { + "type": "string" + }, + "trigger": { + "$ref": "#/$defs/trigger" + } + }, + "required": [ + "duration", + "query" + ] + }, + "prometheus_query_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + }, + "alert_rule": { + "type": "string" + }, + "disable_metric_validation": { + "type": "boolean" + }, + "duration": { + "type": "string" + }, + "evaluation_interval": { + "type": "string" + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "rule_group": { + "type": "string" + } + }, + "required": [ + "query" + ] + }, + "threshold_condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "comparison": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "denominator_filter": { + "type": "string" + }, + "evaluation_missing_data": { + "type": "string" + }, + "filter": { + "type": "string" + }, + "threshold_value": { + "type": "number" + }, + "aggregations": { + "$ref": "#/$defs/aggregations" + }, + "denominator_aggregations": { + "$ref": "#/$defs/aggregations" + }, + "forecast_options": { + "type": "object", + "additionalProperties": false, + "properties": { + "forecast_horizon": { + "type": "string" + } + } + }, + "trigger": { + "$ref": "#/$defs/trigger" + } + }, + "required": [ + "comparison", + "duration" + ] + }, + "aggregations": { + "type": "object", + "additionalProperties": false, + "properties": { + "per_series_aligner": { + "type": "string" + }, + "group_by_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "cross_series_reducer": { + "type": "string" + }, + "alignment_period": { + "type": "string" + } + } + }, + "trigger": { + "type": "object", + "additionalProperties": false, + "properties": { + "count": { + "type": "number" + }, + "percent": { + "type": "number" + } + } + } + } +} diff --git a/modules/project/variables.tf b/modules/project/variables.tf index d7ca7f87c8..31ee76ff99 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -81,12 +81,13 @@ variable "descriptive_name" { variable "factories_config" { description = "Paths to data files and folders that enable factory functionality." type = object({ - alerts = optional(string) - notification_channels = optional(string) - custom_roles = optional(string) - logging_metrics = optional(string) - org_policies = optional(string) - quotas = optional(string) + custom_roles = optional(string) + observability = optional(string) + org_policies = optional(string) + quotas = optional(string) + context = optional(object({ + notification_channels = optional(map(string), {}) + }), {}) }) nullable = false default = {} From 2c3615fe7200f58577528f690b0de87af65e751c Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Sat, 4 Jan 2025 01:18:14 +0100 Subject: [PATCH 26/33] Add outputs to project module --- modules/project/README.md | 35 +++++++++++++++++++---------------- modules/project/outputs.tf | 24 ++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/modules/project/README.md b/modules/project/README.md index 535afbb2e3..2d8c9961ad 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -1624,20 +1624,23 @@ alerts: | name | description | sensitive | |---|---|:---:| -| [custom_role_id](outputs.tf#L17) | Map of custom role IDs created in the project. | | -| [custom_roles](outputs.tf#L27) | Map of custom roles resources created in the project. | | -| [default_service_accounts](outputs.tf#L33) | Emails of the default service accounts for this project. | | -| [id](outputs.tf#L41) | Project id. | | -| [name](outputs.tf#L59) | Project name. | | -| [network_tag_keys](outputs.tf#L71) | Tag key resources. | | -| [network_tag_values](outputs.tf#L80) | Tag value resources. | | -| [number](outputs.tf#L88) | Project number. | | -| [project_id](outputs.tf#L106) | Project id. | | -| [quota_configs](outputs.tf#L124) | Quota configurations. | | -| [quotas](outputs.tf#L135) | Quota resources. | | -| [service_agents](outputs.tf#L140) | List of all (active) service agents for this project. | | -| [services](outputs.tf#L149) | Service APIs to enabled in the project. | | -| [sink_writer_identities](outputs.tf#L158) | Writer identities created for each sink. | | -| [tag_keys](outputs.tf#L165) | Tag key resources. | | -| [tag_values](outputs.tf#L174) | Tag value resources. | | +| [alert_ids](outputs.tf#L17) | Monitoring alert IDs. | | +| [custom_role_id](outputs.tf#L25) | Map of custom role IDs created in the project. | | +| [custom_roles](outputs.tf#L35) | Map of custom roles resources created in the project. | | +| [default_service_accounts](outputs.tf#L40) | Emails of the default service accounts for this project. | | +| [id](outputs.tf#L48) | Project id. | | +| [name](outputs.tf#L66) | Project name. | | +| [network_tag_keys](outputs.tf#L78) | Tag key resources. | | +| [network_tag_values](outputs.tf#L87) | Tag value resources. | | +| [notification_channel_names](outputs.tf#L95) | Notification channel names. | | +| [notification_channels](outputs.tf#L103) | Full notification channel objects. | | +| [number](outputs.tf#L108) | Project number. | | +| [project_id](outputs.tf#L126) | Project id. | | +| [quota_configs](outputs.tf#L144) | Quota configurations. | | +| [quotas](outputs.tf#L155) | Quota resources. | | +| [service_agents](outputs.tf#L160) | List of all (active) service agents for this project. | | +| [services](outputs.tf#L169) | Service APIs to enabled in the project. | | +| [sink_writer_identities](outputs.tf#L178) | Writer identities created for each sink. | | +| [tag_keys](outputs.tf#L185) | Tag key resources. | | +| [tag_values](outputs.tf#L194) | Tag value resources. | | diff --git a/modules/project/outputs.tf b/modules/project/outputs.tf index 8a296ede51..60134a4468 100644 --- a/modules/project/outputs.tf +++ b/modules/project/outputs.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,14 @@ * limitations under the License. */ +output "alert_ids" { + description = "Monitoring alert IDs." + value = { + for k, v in google_monitoring_alert_policy.alerts : + k => v.id + } +} + output "custom_role_id" { description = "Map of custom role IDs created in the project." value = { @@ -29,7 +37,6 @@ output "custom_roles" { value = google_project_iam_custom_role.roles } - output "default_service_accounts" { description = "Emails of the default service accounts for this project." value = { @@ -85,6 +92,19 @@ output "network_tag_values" { } } +output "notification_channel_names" { + description = "Notification channel names." + value = { + for k, v in google_monitoring_notification_channel.channels : + k => v.name + } +} + +output "notification_channels" { + description = "Full notification channel objects." + value = google_monitoring_notification_channel.channels +} + output "number" { description = "Project number." value = local.project.number From 3049cc7d5f0360c8b7f5a6f231af03f92ab2338f Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Sat, 4 Jan 2025 01:22:09 +0100 Subject: [PATCH 27/33] Add factories_config to PF data_defaults and data_overrides --- modules/project-factory/README.md | 9 +++--- modules/project-factory/factory-projects.tf | 35 ++++++++++++++++++++- modules/project-factory/main.tf | 20 ++++++++---- modules/project-factory/variables.tf | 32 ++++++++++++------- 4 files changed, 73 insertions(+), 23 deletions(-) diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index 71547bef2d..a71bfc7b68 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -440,11 +440,10 @@ update_rules: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [factories_config](variables.tf#L106) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | -| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | object({…}) | | {} | -| [data_merges](variables.tf#L54) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | -| [data_overrides](variables.tf#L73) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | -| [default_alerts_email](variables.tf#L100) | Default email address for alerting. | string | | null | +| [factories_config](variables.tf#L112) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | +| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | object({…}) | | {} | +| [data_merges](variables.tf#L60) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | +| [data_overrides](variables.tf#L79) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | ## Outputs diff --git a/modules/project-factory/factory-projects.tf b/modules/project-factory/factory-projects.tf index c29be097df..77fe7432d7 100644 --- a/modules/project-factory/factory-projects.tf +++ b/modules/project-factory/factory-projects.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,39 @@ locals { } projects = { for k, v in local._projects : lookup(v, "name", k) => merge(v, { + factories_config = { + custom_roles = try( + coalesce( + var.data_overrides.factories_config.custom_roles, + try(v.factories_config.custom_roles, null), + var.data_defaults.factories_config.custom_roles + ), + null + ) + observability = try( + coalesce( + var.data_overrides.factories_config.observability, + try(v.factories_config.observability, null), + var.data_defaults.factories_config.observability + ), + null) + org_policies = try( + coalesce( + var.data_overrides.factories_config.org_policies, + try(v.factories_config.org_policies, null), + var.data_defaults.factories_config.org_policies + ), + null) + quotas = try( + coalesce( + var.data_overrides.factories_config.quotas, + try(v.factories_config.quotas, null), + var.data_defaults.factories_config.quotas + ), + null) + } + + billing_account = try(coalesce( var.data_overrides.billing_account, try(v.billing_account, null), diff --git a/modules/project-factory/main.tf b/modules/project-factory/main.tf index 044ea81bb0..f350503f37 100644 --- a/modules/project-factory/main.tf +++ b/modules/project-factory/main.tf @@ -41,19 +41,24 @@ module "projects" { local.context.folder_ids, each.value.parent, each.value.parent ) prefix = each.value.prefix + alerts = try(each.value.alerts, null) auto_create_network = try(each.value.auto_create_network, false) compute_metadata = try(each.value.compute_metadata, {}) # TODO: concat lists for each key contacts = merge( each.value.contacts, var.data_merges.contacts ) - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } default_service_account = try(each.value.default_service_account, "keep") descriptive_name = try(each.value.descriptive_name, null) + factories_config = { + custom_roles = each.value.factories_config.custom_roles + observability = each.value.factories_config.observability + org_policies = each.value.factories_config.org_policies + quotas = each.value.factories_config.quotas + context = { + notification_channels = var.factories_config.context.notification_channels + } + } iam = { for k, v in lookup(each.value, "iam", {}) : k => [ for vv in v : try( @@ -98,13 +103,16 @@ module "projects" { each.value.labels, var.data_merges.labels ) lien_reason = try(each.value.lien_reason, null) + log_scopes = try(each.value.log_scopes, null) logging_data_access = try(each.value.logging_data_access, {}) logging_exclusions = try(each.value.logging_exclusions, {}) + logging_metrics = try(each.value.logging_metrics, null) logging_sinks = try(each.value.logging_sinks, {}) metric_scopes = distinct(concat( each.value.metric_scopes, var.data_merges.metric_scopes )) - org_policies = each.value.org_policies + notification_channels = try(each.value.notification_channels, null) + org_policies = each.value.org_policies service_encryption_key_ids = merge( each.value.service_encryption_key_ids, var.data_merges.service_encryption_key_ids diff --git a/modules/project-factory/variables.tf b/modules/project-factory/variables.tf index 9f45e04d79..bf21ae2772 100644 --- a/modules/project-factory/variables.tf +++ b/modules/project-factory/variables.tf @@ -17,8 +17,14 @@ variable "data_defaults" { description = "Optional default values used when corresponding project data from files are missing." type = object({ - billing_account = optional(string) - contacts = optional(map(list(string)), {}) + billing_account = optional(string) + contacts = optional(map(list(string)), {}) + factories_config = optional(object({ + custom_roles = optional(string) + observability = optional(string) + org_policies = optional(string) + quotas = optional(string) + }), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) parent = optional(string) @@ -73,8 +79,14 @@ variable "data_merges" { variable "data_overrides" { description = "Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`." type = object({ - billing_account = optional(string) - contacts = optional(map(list(string))) + billing_account = optional(string) + contacts = optional(map(list(string))) + factories_config = optional(object({ + custom_roles = optional(string) + observability = optional(string) + org_policies = optional(string) + quotas = optional(string) + }), {}) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string))) @@ -100,24 +112,22 @@ variable "data_overrides" { variable "factories_config" { description = "Path to folder with YAML resource description data files." type = object({ - alerts = optional(string) budgets = optional(object({ billing_account = string budgets_data_path = string # TODO: allow defining notification channels via YAML files notification_channels = optional(map(any), {}) })) - channels = optional(string) context = optional(object({ # TODO: add KMS keys - folder_ids = optional(map(string), {}) - iam_principals = optional(map(string), {}) - tag_values = optional(map(string), {}) - vpc_host_projects = optional(map(string), {}) + folder_ids = optional(map(string), {}) + iam_principals = optional(map(string), {}) + tag_values = optional(map(string), {}) + vpc_host_projects = optional(map(string), {}) + notification_channels = optional(map(string), {}) }), {}) folders_data_path = optional(string) projects_data_path = optional(string) - logging_metrics = optional(string) }) nullable = false } From 1ca9b098a7d47e8bb0c949525f6b763411f91c01 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Sat, 4 Jan 2025 01:27:36 +0100 Subject: [PATCH 28/33] Reorder PF field processing --- modules/project-factory/factory-projects.tf | 22 ++++++++++----------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/modules/project-factory/factory-projects.tf b/modules/project-factory/factory-projects.tf index 77fe7432d7..e33a5193a1 100644 --- a/modules/project-factory/factory-projects.tf +++ b/modules/project-factory/factory-projects.tf @@ -48,6 +48,16 @@ locals { } projects = { for k, v in local._projects : lookup(v, "name", k) => merge(v, { + billing_account = try(coalesce( + var.data_overrides.billing_account, + try(v.billing_account, null), + var.data_defaults.billing_account + ), null) + contacts = coalesce( + var.data_overrides.contacts, + try(v.contacts, null), + var.data_defaults.contacts + ) factories_config = { custom_roles = try( coalesce( @@ -79,18 +89,6 @@ locals { ), null) } - - - billing_account = try(coalesce( - var.data_overrides.billing_account, - try(v.billing_account, null), - var.data_defaults.billing_account - ), null) - contacts = coalesce( - var.data_overrides.contacts, - try(v.contacts, null), - var.data_defaults.contacts - ) labels = coalesce( try(v.labels, null), var.data_defaults.labels From 6505c9b8c0549f4a43f71f18e1fd5b2e752fbf23 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Sun, 5 Jan 2025 12:51:04 +0100 Subject: [PATCH 29/33] Revert fast/ to master. We'll do observability stuff in a separate PR --- fast/stages/0-bootstrap/README.md | 59 +++++++++---------- fast/stages/0-bootstrap/automation.tf | 4 -- fast/stages/0-bootstrap/billing.tf | 8 +-- fast/stages/0-bootstrap/log-export.tf | 6 -- fast/stages/0-bootstrap/outputs.tf | 5 +- .../0-bootstrap/terraform.tfvars.sample | 2 - fast/stages/0-bootstrap/variables.tf | 10 +--- fast/stages/1-resman/README.md | 25 ++++---- fast/stages/1-resman/tenant-root.tf | 6 -- fast/stages/1-resman/variables-fast.tf | 6 -- fast/stages/1-resman/variables.tf | 3 - fast/stages/2-network-security/README.md | 19 +++--- fast/stages/2-network-security/main.tf | 8 +-- .../2-network-security/variables-fast.tf | 6 -- fast/stages/2-network-security/variables.tf | 5 +- fast/stages/2-networking-a-simple/README.md | 23 ++++---- fast/stages/2-networking-a-simple/net-dev.tf | 8 +-- .../2-networking-a-simple/net-landing.tf | 8 +-- fast/stages/2-networking-a-simple/net-prod.tf | 8 +-- .../2-networking-a-simple/variables-fast.tf | 6 -- .../stages/2-networking-a-simple/variables.tf | 3 - fast/stages/2-networking-b-nva/README.md | 27 ++++----- fast/stages/2-networking-b-nva/net-dev.tf | 8 +-- fast/stages/2-networking-b-nva/net-landing.tf | 8 +-- fast/stages/2-networking-b-nva/net-prod.tf | 8 +-- .../2-networking-b-nva/variables-fast.tf | 6 -- fast/stages/2-networking-b-nva/variables.tf | 3 - .../2-networking-c-separate-envs/README.md | 23 ++++---- .../2-networking-c-separate-envs/net-dev.tf | 8 +-- .../2-networking-c-separate-envs/net-prod.tf | 8 +-- .../variables-fast.tf | 6 -- .../2-networking-c-separate-envs/variables.tf | 3 - fast/stages/2-project-factory/README.md | 17 +++--- fast/stages/2-project-factory/main.tf | 6 +- .../2-project-factory/variables-fast.tf | 6 -- fast/stages/2-project-factory/variables.tf | 7 +-- fast/stages/2-security/README.md | 20 +++---- fast/stages/2-security/core-dev.tf | 14 ++--- fast/stages/2-security/core-prod.tf | 14 ++--- fast/stages/2-security/variables-fast.tf | 6 -- fast/stages/2-security/variables.tf | 11 ---- 41 files changed, 127 insertions(+), 310 deletions(-) diff --git a/fast/stages/0-bootstrap/README.md b/fast/stages/0-bootstrap/README.md index 3b123a8af9..83a15b1348 100644 --- a/fast/stages/0-bootstrap/README.md +++ b/fast/stages/0-bootstrap/README.md @@ -661,42 +661,41 @@ The remaining configuration is manual, as it regards the repositories themselves | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| | [billing_account](variables.tf#L17) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | | -| [organization](variables.tf#L298) | Organization details. | object({…}) | ✓ | | | -| [prefix](variables.tf#L313) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | +| [organization](variables.tf#L290) | Organization details. | object({…}) | ✓ | | | +| [prefix](variables.tf#L305) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | | [bootstrap_user](variables.tf#L38) | Email of the nominal user running this stage for the first time. | string | | null | | | [cicd_repositories](variables.tf#L44) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | null | | | [custom_roles](variables.tf#L98) | Map of role names => list of permissions to additionally create at the organization level. | map(list(string)) | | {} | | -| [default_alerts_email](variables.tf#L105) | Default email address for alerting. | string | | null | | -| [environments](variables.tf#L111) | Environment names. When not defined, short name is set to the key and tag name to lower(name). | map(object({…})) | | {…} | | -| [essential_contacts](variables.tf#L145) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L151) | Configuration for the resource factories or external data. | object({…}) | | {} | | -| [groups](variables.tf#L164) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | -| [iam](variables.tf#L180) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | -| [iam_bindings_additive](variables.tf#L187) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | -| [iam_by_principals](variables.tf#L202) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | -| [locations](variables.tf#L209) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | -| [log_sinks](variables.tf#L223) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | -| [org_policies_config](variables.tf#L279) | Organization policies customization. | object({…}) | | {} | | -| [outputs_location](variables.tf#L307) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [project_parent_ids](variables.tf#L322) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | -| [resource_names](variables.tf#L333) | Resource names overrides for specific resources. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | -| [workforce_identity_providers](variables.tf#L365) | Workforce Identity Federation pools. | map(object({…})) | | {} | | -| [workload_identity_providers](variables.tf#L381) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | +| [environments](variables.tf#L105) | Environment names. When not defined, short name is set to the key and tag name to lower(name). | map(object({…})) | | {…} | | +| [essential_contacts](variables.tf#L139) | Email used for essential contacts, unset if null. | string | | null | | +| [factories_config](variables.tf#L145) | Configuration for the resource factories or external data. | object({…}) | | {} | | +| [groups](variables.tf#L156) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | +| [iam](variables.tf#L172) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | +| [iam_bindings_additive](variables.tf#L179) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | +| [iam_by_principals](variables.tf#L194) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | +| [locations](variables.tf#L201) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | +| [log_sinks](variables.tf#L215) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | +| [org_policies_config](variables.tf#L271) | Organization policies customization. | object({…}) | | {} | | +| [outputs_location](variables.tf#L299) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [project_parent_ids](variables.tf#L314) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | +| [resource_names](variables.tf#L325) | Resource names overrides for specific resources. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | +| [workforce_identity_providers](variables.tf#L357) | Workforce Identity Federation pools. | map(object({…})) | | {} | | +| [workload_identity_providers](variables.tf#L373) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [automation](outputs.tf#L160) | Automation resources. | | | -| [billing_dataset](outputs.tf#L165) | BigQuery dataset prepared for billing export. | | | -| [cicd_repositories](outputs.tf#L170) | CI/CD repository configurations. | | | -| [custom_roles](outputs.tf#L182) | Organization-level custom roles. | | | -| [outputs_bucket](outputs.tf#L187) | GCS bucket where generated output files are stored. | | | -| [project_ids](outputs.tf#L192) | Projects created by this stage. | | | -| [providers](outputs.tf#L202) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | -| [service_accounts](outputs.tf#L209) | Automation service accounts created by this stage. | | | -| [tfvars](outputs.tf#L218) | Terraform variable files for the following stages. | ✓ | | -| [tfvars_globals](outputs.tf#L224) | Terraform Globals variable files for the following stages. | ✓ | | -| [workforce_identity_pool](outputs.tf#L230) | Workforce Identity Federation pool. | | | -| [workload_identity_pool](outputs.tf#L239) | Workload Identity Federation pool and providers. | | | +| [automation](outputs.tf#L159) | Automation resources. | | | +| [billing_dataset](outputs.tf#L164) | BigQuery dataset prepared for billing export. | | | +| [cicd_repositories](outputs.tf#L169) | CI/CD repository configurations. | | | +| [custom_roles](outputs.tf#L181) | Organization-level custom roles. | | | +| [outputs_bucket](outputs.tf#L186) | GCS bucket where generated output files are stored. | | | +| [project_ids](outputs.tf#L191) | Projects created by this stage. | | | +| [providers](outputs.tf#L201) | Terraform provider files for this stage and dependent stages. | ✓ | stage-01 | +| [service_accounts](outputs.tf#L208) | Automation service accounts created by this stage. | | | +| [tfvars](outputs.tf#L217) | Terraform variable files for the following stages. | ✓ | | +| [tfvars_globals](outputs.tf#L223) | Terraform Globals variable files for the following stages. | ✓ | | +| [workforce_identity_pool](outputs.tf#L229) | Workforce Identity Federation pool. | | | +| [workload_identity_pool](outputs.tf#L238) | Workload Identity Federation pool and providers. | | | diff --git a/fast/stages/0-bootstrap/automation.tf b/fast/stages/0-bootstrap/automation.tf index d9f1c2f61a..4dc44093d8 100644 --- a/fast/stages/0-bootstrap/automation.tf +++ b/fast/stages/0-bootstrap/automation.tf @@ -38,14 +38,10 @@ module "automation-project" { ? {} : { (var.essential_contacts) = ["ALL"] } ) - default_alerts_email = var.default_alerts_email factories_config = { org_policies = ( var.bootstrap_user != null ? null : var.factories_config.org_policies_iac ) - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts } # human (groups) IAM bindings iam_by_principals = { diff --git a/fast/stages/0-bootstrap/billing.tf b/fast/stages/0-bootstrap/billing.tf index b09ba3c93f..881968de0f 100644 --- a/fast/stages/0-bootstrap/billing.tf +++ b/fast/stages/0-bootstrap/billing.tf @@ -47,18 +47,12 @@ module "billing-export-project" { parent = coalesce( var.project_parent_ids.billing, "organizations/${var.organization.id}" ) - prefix = var.prefix - default_alerts_email = var.default_alerts_email + prefix = var.prefix contacts = ( var.bootstrap_user != null || var.essential_contacts == null ? {} : { (var.essential_contacts) = ["ALL"] } ) - factories_config = { - alerts = var.factories_config.alerts - channels = var.factories_config.channels - logging_metrics = var.factories_config.logging_metrics - } iam = { "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] "roles/viewer" = [module.automation-tf-bootstrap-r-sa.iam_email] diff --git a/fast/stages/0-bootstrap/log-export.tf b/fast/stages/0-bootstrap/log-export.tf index d57dabb19e..98061a7a20 100644 --- a/fast/stages/0-bootstrap/log-export.tf +++ b/fast/stages/0-bootstrap/log-export.tf @@ -49,12 +49,6 @@ module "log-export-project" { ? {} : { (var.essential_contacts) = ["ALL"] } ) - default_alerts_email = var.default_alerts_email - factories_config = { - alerts = var.factories_config.alerts - channels = var.factories_config.channels - logging_metrics = var.factories_config.logging_metrics - } iam = { "roles/owner" = [module.automation-tf-bootstrap-sa.iam_email] "roles/viewer" = [module.automation-tf-bootstrap-r-sa.iam_email] diff --git a/fast/stages/0-bootstrap/outputs.tf b/fast/stages/0-bootstrap/outputs.tf index ca277a16e1..876b269f07 100644 --- a/fast/stages/0-bootstrap/outputs.tf +++ b/fast/stages/0-bootstrap/outputs.tf @@ -109,7 +109,6 @@ locals { vpcsc = module.automation-tf-vpcsc-sa.email vpcsc-r = module.automation-tf-vpcsc-r-sa.email } - default_alerts_email = var.default_alerts_email } billing = { dataset = try(module.billing-export-dataset[0].id, null) @@ -223,8 +222,8 @@ output "tfvars" { output "tfvars_globals" { description = "Terraform Globals variable files for the following stages." - sensitive = false - value = jsonencode(local.tfvars_globals) + sensitive = true + value = local.tfvars_globals } output "workforce_identity_pool" { diff --git a/fast/stages/0-bootstrap/terraform.tfvars.sample b/fast/stages/0-bootstrap/terraform.tfvars.sample index a396c1a0d3..564e8113fb 100644 --- a/fast/stages/0-bootstrap/terraform.tfvars.sample +++ b/fast/stages/0-bootstrap/terraform.tfvars.sample @@ -23,5 +23,3 @@ outputs_location = "~/fast-config" # use something unique and no longer than 9 characters prefix = "abcd" - -default_alerts_email = "support@company.com" diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index fc7a667a4f..e1fd0bd9f1 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -102,12 +102,6 @@ variable "custom_roles" { default = {} } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "environments" { description = "Environment names. When not defined, short name is set to the key and tag name to lower(name)." type = map(object({ @@ -151,16 +145,14 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for the resource factories or external data." type = object({ - alerts = optional(string, "data/alerts") - channels = optional(string, "data/channels") custom_roles = optional(string, "data/custom-roles") - logging_metrics = optional(string, "data/logging-metrics") org_policies = optional(string, "data/org-policies") org_policies_iac = optional(string, "data/org-policies-iac") }) nullable = false default = {} } + variable "groups" { # https://cloud.google.com/docs/enterprise/setup-checklist description = "Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated." diff --git a/fast/stages/1-resman/README.md b/fast/stages/1-resman/README.md index b67fa2d85e..339b951700 100644 --- a/fast/stages/1-resman/README.md +++ b/fast/stages/1-resman/README.md @@ -267,22 +267,21 @@ terraform apply |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L42) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | 0-bootstrap | -| [environments](variables-fast.tf#L77) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [logging](variables-fast.tf#L122) | Logging configuration for tenants. | object({…}) | ✓ | | 1-tenant-factory | -| [organization](variables-fast.tf#L135) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables-fast.tf#L153) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [environments](variables-fast.tf#L71) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [logging](variables-fast.tf#L116) | Logging configuration for tenants. | object({…}) | ✓ | | 1-tenant-factory | +| [organization](variables-fast.tf#L129) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables-fast.tf#L147) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [custom_roles](variables-fast.tf#L53) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [default_alerts_email](variables-fast.tf#L71) | Default email address for alerting. | string | | null | | -| [factories_config](variables.tf#L20) | Configuration for the resource factories or external data. | object({…}) | | {} | | +| [factories_config](variables.tf#L20) | Configuration for the resource factories or external data. | object({…}) | | {} | | | [fast_stage_2](variables-stages.tf#L17) | FAST stages 2 configurations. | object({…}) | | {} | | | [fast_stage_3](variables-stages.tf#L95) | FAST stages 3 configurations. | map(object({…})) | | {} | | -| [groups](variables-fast.tf#L94) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | -| [locations](variables-fast.tf#L109) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | -| [outputs_location](variables.tf#L34) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [resource_names](variables.tf#L40) | Resource names overrides for specific resources. Stage names are interpolated via `$${name}`. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | -| [root_node](variables-fast.tf#L159) | Root node for the hierarchy, if running in tenant mode. | string | | null | 0-bootstrap | -| [tag_names](variables.tf#L65) | Customized names for resource management tags. | object({…}) | | {} | | -| [tags](variables.tf#L79) | Custom secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | +| [groups](variables-fast.tf#L88) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | +| [locations](variables-fast.tf#L103) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | +| [outputs_location](variables.tf#L31) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [resource_names](variables.tf#L37) | Resource names overrides for specific resources. Stage names are interpolated via `$${name}`. Prefix is always set via code, except where noted in the variable type. | object({…}) | | {} | | +| [root_node](variables-fast.tf#L153) | Root node for the hierarchy, if running in tenant mode. | string | | null | 0-bootstrap | +| [tag_names](variables.tf#L62) | Customized names for resource management tags. | object({…}) | | {} | | +| [tags](variables.tf#L76) | Custom secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | | [top_level_folders](variables-toplevel-folders.tf#L17) | Additional top-level folders. Keys are used for service account and bucket names, values implement the folders module interface with the addition of the 'automation' attribute. | map(object({…})) | | {} | | ## Outputs diff --git a/fast/stages/1-resman/tenant-root.tf b/fast/stages/1-resman/tenant-root.tf index 2a33baf875..e5d79c717c 100644 --- a/fast/stages/1-resman/tenant-root.tf +++ b/fast/stages/1-resman/tenant-root.tf @@ -38,12 +38,6 @@ module "automation-project" { project_create = false # do not assign tagViewer or tagUser roles here on tag keys and values as # they are managed authoritatively and will break multitenant stages - default_alerts_email = var.default_alerts_email - factories_config = { - alerts = var.factories_config.alerts - channels = var.factories_config.channels - logging_metrics = var.factories_config.logging_metrics - } tags = merge(local.tags, { (var.tag_names.context) = { description = "Resource management context." diff --git a/fast/stages/1-resman/variables-fast.tf b/fast/stages/1-resman/variables-fast.tf index f5ae8037b7..a35f5eca40 100644 --- a/fast/stages/1-resman/variables-fast.tf +++ b/fast/stages/1-resman/variables-fast.tf @@ -68,12 +68,6 @@ variable "custom_roles" { default = null } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/1-resman/variables.tf b/fast/stages/1-resman/variables.tf index 7a11ec1895..6c88f052ad 100644 --- a/fast/stages/1-resman/variables.tf +++ b/fast/stages/1-resman/variables.tf @@ -20,9 +20,6 @@ variable "factories_config" { description = "Configuration for the resource factories or external data." type = object({ - alerts = optional(string, "data/alerts") - channels = optional(string, "data/channels") - logging_metrics = optional(string, "data/logging-metrics") org_policies = optional(string, "data/org-policies") stage_3 = optional(string, "data/stage-3") top_level_folders = optional(string, "data/top-level-folders") diff --git a/fast/stages/2-network-security/README.md b/fast/stages/2-network-security/README.md index 1e205ec814..ebad676156 100644 --- a/fast/stages/2-network-security/README.md +++ b/fast/stages/2-network-security/README.md @@ -180,16 +180,15 @@ Make sure the CAs and the trusted configs created for NGFW Enterprise in the [2- |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L27) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [default_alerts_email](variables-fast.tf#L40) | Default email address for alerting. | string | ✓ | | | -| [folder_ids](variables-fast.tf#L46) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables-fast.tf#L88) | Organization details. | object({…}) | ✓ | | 00-globals | -| [prefix](variables-fast.tf#L98) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | -| [vpc_self_links](variables-fast.tf#L108) | Self link for the shared VPC. | object({…}) | ✓ | | 2-networking | -| [factories_config](variables.tf#L17) | Configuration for network resource factories. | object({…}) | | {…} | | -| [host_project_ids](variables-fast.tf#L57) | Host project for the shared VPC. | object({…}) | | {} | 2-networking | -| [ngfw_enterprise_config](variables.tf#L38) | NGFW Enterprise configuration. | object({…}) | | {…} | | -| [ngfw_tls_configs](variables-fast.tf#L68) | The NGFW Enterprise TLS configurations. | object({…}) | | {…} | 2-security | -| [outputs_location](variables.tf#L54) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [folder_ids](variables-fast.tf#L40) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables-fast.tf#L82) | Organization details. | object({…}) | ✓ | | 00-globals | +| [prefix](variables-fast.tf#L92) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [vpc_self_links](variables-fast.tf#L102) | Self link for the shared VPC. | object({…}) | ✓ | | 2-networking | +| [factories_config](variables.tf#L17) | Configuration for network resource factories. | object({…}) | | {…} | | +| [host_project_ids](variables-fast.tf#L51) | Host project for the shared VPC. | object({…}) | | {} | 2-networking | +| [ngfw_enterprise_config](variables.tf#L35) | NGFW Enterprise configuration. | object({…}) | | {…} | | +| [ngfw_tls_configs](variables-fast.tf#L62) | The NGFW Enterprise TLS configurations. | object({…}) | | {…} | 2-security | +| [outputs_location](variables.tf#L51) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | ## Outputs diff --git a/fast/stages/2-network-security/main.tf b/fast/stages/2-network-security/main.tf index c492e57c4c..946dba43f3 100644 --- a/fast/stages/2-network-security/main.tf +++ b/fast/stages/2-network-security/main.tf @@ -56,13 +56,7 @@ module "ngfw-quota-project" { ? true : false ) - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } - default_alerts_email = var.default_alerts_email - services = ["networksecurity.googleapis.com"] + services = ["networksecurity.googleapis.com"] } resource "google_network_security_firewall_endpoint" "firewall_endpoint" { diff --git a/fast/stages/2-network-security/variables-fast.tf b/fast/stages/2-network-security/variables-fast.tf index 5419e1a343..45fb1c0fbd 100644 --- a/fast/stages/2-network-security/variables-fast.tf +++ b/fast/stages/2-network-security/variables-fast.tf @@ -37,12 +37,6 @@ variable "billing_account" { } } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "folder_ids" { # tfdoc:variable:source 1-resman description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." diff --git a/fast/stages/2-network-security/variables.tf b/fast/stages/2-network-security/variables.tf index 353a3e2568..94f00b6fed 100644 --- a/fast/stages/2-network-security/variables.tf +++ b/fast/stages/2-network-security/variables.tf @@ -17,14 +17,11 @@ variable "factories_config" { description = "Configuration for network resource factories." type = object({ - alerts = optional(string, "data/alerts") - channels = optional(string, "data/channels") - cidrs = optional(string, "data/cidrs.yaml") + cidrs = optional(string, "data/cidrs.yaml") firewall_policy_rules = optional(object({ dev = string prod = string })) - logging_metrics = optional(string, "data/logging-metrics") }) nullable = false default = { diff --git a/fast/stages/2-networking-a-simple/README.md b/fast/stages/2-networking-a-simple/README.md index be27767b36..574689ef2d 100644 --- a/fast/stages/2-networking-a-simple/README.md +++ b/fast/stages/2-networking-a-simple/README.md @@ -501,24 +501,23 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L27) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [default_alerts_email](variables-fast.tf#L49) | Default email address for alerting. | string | ✓ | | | -| [environments](variables-fast.tf#L55) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [folder_ids](variables-fast.tf#L72) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | -| [prefix](variables-fast.tf#L82) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [environments](variables-fast.tf#L49) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [folder_ids](variables-fast.tf#L66) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | +| [prefix](variables-fast.tf#L76) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [create_test_instances](variables.tf#L42) | Enables the creation of test VMs in each VPC, useful to test and troubleshoot connectivity. | bool | | false | | | [custom_roles](variables-fast.tf#L40) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L48) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L58) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L65) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L71) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L95) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L101) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | {} | | -| [regions](variables.tf#L121) | Region definitions. | object({…}) | | {…} | | -| [spoke_configs](variables.tf#L133) | Spoke connectivity configurations. | object({…}) | | {…} | | -| [stage_config](variables-fast.tf#L92) | FAST stage configuration. | object({…}) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L106) | Root-level tag values. | map(string) | | {} | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L202) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [factories_config](variables.tf#L71) | Configuration for network resource factories. | object({…}) | | {…} | | +| [outputs_location](variables.tf#L92) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L98) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | {} | | +| [regions](variables.tf#L118) | Region definitions. | object({…}) | | {…} | | +| [spoke_configs](variables.tf#L130) | Spoke connectivity configurations. | object({…}) | | {…} | | +| [stage_config](variables-fast.tf#L86) | FAST stage configuration. | object({…}) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L100) | Root-level tag values. | map(string) | | {} | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L199) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-a-simple/net-dev.tf b/fast/stages/2-networking-a-simple/net-dev.tf index 89e89267f1..ad14b36224 100644 --- a/fast/stages/2-networking-a-simple/net-dev.tf +++ b/fast/stages/2-networking-a-simple/net-dev.tf @@ -24,13 +24,7 @@ module "dev-spoke-project" { var.folder_ids.networking-dev, var.folder_ids.networking ) - prefix = var.prefix - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } + prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-a-simple/net-landing.tf b/fast/stages/2-networking-a-simple/net-landing.tf index 2f64216de3..67e1c9d920 100644 --- a/fast/stages/2-networking-a-simple/net-landing.tf +++ b/fast/stages/2-networking-a-simple/net-landing.tf @@ -24,13 +24,7 @@ module "landing-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } + prefix = var.prefix services = [ "compute.googleapis.com", "dns.googleapis.com", diff --git a/fast/stages/2-networking-a-simple/net-prod.tf b/fast/stages/2-networking-a-simple/net-prod.tf index bed2f585a6..cfef3f425c 100644 --- a/fast/stages/2-networking-a-simple/net-prod.tf +++ b/fast/stages/2-networking-a-simple/net-prod.tf @@ -24,13 +24,7 @@ module "prod-spoke-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } + prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-a-simple/variables-fast.tf b/fast/stages/2-networking-a-simple/variables-fast.tf index 070c57a4af..dd7f96bf58 100644 --- a/fast/stages/2-networking-a-simple/variables-fast.tf +++ b/fast/stages/2-networking-a-simple/variables-fast.tf @@ -46,12 +46,6 @@ variable "custom_roles" { default = null } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/2-networking-a-simple/variables.tf b/fast/stages/2-networking-a-simple/variables.tf index dc6f754bc4..d04ad9a045 100644 --- a/fast/stages/2-networking-a-simple/variables.tf +++ b/fast/stages/2-networking-a-simple/variables.tf @@ -71,12 +71,9 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - alerts = optional(string, "data/alerts") - channels = optional(string, "data/channels") data_dir = optional(string, "data") dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") firewall_policy_name = optional(string, "net-default") - logging_metrics = optional(string, "data/logging-metrics") }) default = { data_dir = "data" diff --git a/fast/stages/2-networking-b-nva/README.md b/fast/stages/2-networking-b-nva/README.md index c64fa6d669..c8f9b78bb1 100644 --- a/fast/stages/2-networking-b-nva/README.md +++ b/fast/stages/2-networking-b-nva/README.md @@ -562,26 +562,25 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L27) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [default_alerts_email](variables-fast.tf#L49) | Default email address for alerting. | string | ✓ | | | -| [environments](variables-fast.tf#L55) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [folder_ids](variables-fast.tf#L72) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | -| [prefix](variables-fast.tf#L82) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [environments](variables-fast.tf#L49) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [folder_ids](variables-fast.tf#L66) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | +| [prefix](variables-fast.tf#L76) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [create_test_instances](variables.tf#L42) | Enables the creation of test VMs in each VPC, useful to test and troubleshoot connectivity. | bool | | false | | | [custom_roles](variables-fast.tf#L40) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L48) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L58) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L65) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L71) | Configuration for network resource factories. | object({…}) | | {…} | | -| [gcp_ranges](variables.tf#L95) | GCP address ranges in name => range format. | map(string) | | {…} | | -| [network_mode](variables.tf#L112) | Selection of the network design to deploy. | string | | "simple" | | -| [outputs_location](variables.tf#L123) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L129) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | {} | | -| [regions](variables.tf#L149) | Region definitions. | object({…}) | | {…} | | -| [stage_config](variables-fast.tf#L92) | FAST stage configuration. | object({…}) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L106) | Root-level tag values. | map(string) | | {} | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L161) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | -| [vpn_onprem_secondary_config](variables.tf#L204) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | +| [factories_config](variables.tf#L71) | Configuration for network resource factories. | object({…}) | | {…} | | +| [gcp_ranges](variables.tf#L92) | GCP address ranges in name => range format. | map(string) | | {…} | | +| [network_mode](variables.tf#L109) | Selection of the network design to deploy. | string | | "simple" | | +| [outputs_location](variables.tf#L120) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L126) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | {} | | +| [regions](variables.tf#L146) | Region definitions. | object({…}) | | {…} | | +| [stage_config](variables-fast.tf#L86) | FAST stage configuration. | object({…}) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L100) | Root-level tag values. | map(string) | | {} | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L158) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_secondary_config](variables.tf#L201) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-b-nva/net-dev.tf b/fast/stages/2-networking-b-nva/net-dev.tf index 4a72d6c581..eec1d55861 100644 --- a/fast/stages/2-networking-b-nva/net-dev.tf +++ b/fast/stages/2-networking-b-nva/net-dev.tf @@ -24,13 +24,7 @@ module "dev-spoke-project" { var.folder_ids.networking-dev, var.folder_ids.networking ) - prefix = var.prefix - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } + prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-b-nva/net-landing.tf b/fast/stages/2-networking-b-nva/net-landing.tf index 58c855b698..5bd65e0b8d 100644 --- a/fast/stages/2-networking-b-nva/net-landing.tf +++ b/fast/stages/2-networking-b-nva/net-landing.tf @@ -24,13 +24,7 @@ module "landing-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } + prefix = var.prefix services = [ "compute.googleapis.com", "dns.googleapis.com", diff --git a/fast/stages/2-networking-b-nva/net-prod.tf b/fast/stages/2-networking-b-nva/net-prod.tf index 1cbcf6f16a..bb59e58a4d 100644 --- a/fast/stages/2-networking-b-nva/net-prod.tf +++ b/fast/stages/2-networking-b-nva/net-prod.tf @@ -24,13 +24,7 @@ module "prod-spoke-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } + prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-b-nva/variables-fast.tf b/fast/stages/2-networking-b-nva/variables-fast.tf index 070c57a4af..dd7f96bf58 100644 --- a/fast/stages/2-networking-b-nva/variables-fast.tf +++ b/fast/stages/2-networking-b-nva/variables-fast.tf @@ -46,12 +46,6 @@ variable "custom_roles" { default = null } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/2-networking-b-nva/variables.tf b/fast/stages/2-networking-b-nva/variables.tf index 6bc49cfc4c..cc8e8065d9 100644 --- a/fast/stages/2-networking-b-nva/variables.tf +++ b/fast/stages/2-networking-b-nva/variables.tf @@ -71,12 +71,9 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - alerts = optional(string, "data/alerts") - channels = optional(string, "data/channels") data_dir = optional(string, "data") dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") firewall_policy_name = optional(string, "net-default") - logging_metrics = optional(string, "data/logging-metrics") }) default = { data_dir = "data" diff --git a/fast/stages/2-networking-c-separate-envs/README.md b/fast/stages/2-networking-c-separate-envs/README.md index 757977d87a..71d8487c66 100644 --- a/fast/stages/2-networking-c-separate-envs/README.md +++ b/fast/stages/2-networking-c-separate-envs/README.md @@ -360,23 +360,22 @@ Regions are defined via the `regions` variable which sets up a mapping between t |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L19) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L27) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [default_alerts_email](variables-fast.tf#L49) | Default email address for alerting. | string | ✓ | | | -| [environments](variables-fast.tf#L55) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [folder_ids](variables-fast.tf#L72) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | -| [prefix](variables-fast.tf#L82) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [environments](variables-fast.tf#L49) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [folder_ids](variables-fast.tf#L66) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. | object({…}) | ✓ | | 1-resman | +| [prefix](variables-fast.tf#L76) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [custom_roles](variables-fast.tf#L40) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L42) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L53) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L60) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L66) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L90) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L96) | IP ranges used for Private Service Access (e.g. CloudSQL). | object({…}) | | {} | | -| [regions](variables.tf#L116) | Region definitions. | object({…}) | | {…} | | -| [stage_config](variables-fast.tf#L92) | FAST stage configuration. | object({…}) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L106) | Root-level tag values. | map(string) | | {} | 1-resman | -| [vpn_onprem_dev_primary_config](variables.tf#L126) | VPN gateway configuration for onprem interconnection from dev in the primary region. | object({…}) | | null | | -| [vpn_onprem_prod_primary_config](variables.tf#L169) | VPN gateway configuration for onprem interconnection from prod in the primary region. | object({…}) | | null | | +| [factories_config](variables.tf#L66) | Configuration for network resource factories. | object({…}) | | {…} | | +| [outputs_location](variables.tf#L87) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L93) | IP ranges used for Private Service Access (e.g. CloudSQL). | object({…}) | | {} | | +| [regions](variables.tf#L113) | Region definitions. | object({…}) | | {…} | | +| [stage_config](variables-fast.tf#L86) | FAST stage configuration. | object({…}) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L100) | Root-level tag values. | map(string) | | {} | 1-resman | +| [vpn_onprem_dev_primary_config](variables.tf#L123) | VPN gateway configuration for onprem interconnection from dev in the primary region. | object({…}) | | null | | +| [vpn_onprem_prod_primary_config](variables.tf#L166) | VPN gateway configuration for onprem interconnection from prod in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-c-separate-envs/net-dev.tf b/fast/stages/2-networking-c-separate-envs/net-dev.tf index ba2fa30178..185db01ff3 100644 --- a/fast/stages/2-networking-c-separate-envs/net-dev.tf +++ b/fast/stages/2-networking-c-separate-envs/net-dev.tf @@ -24,13 +24,7 @@ module "dev-spoke-project" { var.folder_ids.networking-dev, var.folder_ids.networking ) - prefix = var.prefix - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } + prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-c-separate-envs/net-prod.tf b/fast/stages/2-networking-c-separate-envs/net-prod.tf index 8f19df75b3..f25c9f5560 100644 --- a/fast/stages/2-networking-c-separate-envs/net-prod.tf +++ b/fast/stages/2-networking-c-separate-envs/net-prod.tf @@ -24,13 +24,7 @@ module "prod-spoke-project" { var.folder_ids.networking-prod, var.folder_ids.networking ) - prefix = var.prefix - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } + prefix = var.prefix services = [ "container.googleapis.com", "compute.googleapis.com", diff --git a/fast/stages/2-networking-c-separate-envs/variables-fast.tf b/fast/stages/2-networking-c-separate-envs/variables-fast.tf index 070c57a4af..dd7f96bf58 100644 --- a/fast/stages/2-networking-c-separate-envs/variables-fast.tf +++ b/fast/stages/2-networking-c-separate-envs/variables-fast.tf @@ -46,12 +46,6 @@ variable "custom_roles" { default = null } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/2-networking-c-separate-envs/variables.tf b/fast/stages/2-networking-c-separate-envs/variables.tf index 29167801f7..aabaab2d98 100644 --- a/fast/stages/2-networking-c-separate-envs/variables.tf +++ b/fast/stages/2-networking-c-separate-envs/variables.tf @@ -66,12 +66,9 @@ variable "essential_contacts" { variable "factories_config" { description = "Configuration for network resource factories." type = object({ - alerts = optional(string, "data/alerts") - channels = optional(string, "data/channels") data_dir = optional(string, "data") dns_policy_rules_file = optional(string, "data/dns-policy-rules.yaml") firewall_policy_name = optional(string, "net-default") - logging_metrics = optional(string, "data/logging-metrics") }) default = { data_dir = "data" diff --git a/fast/stages/2-project-factory/README.md b/fast/stages/2-project-factory/README.md index 9edbbc1f66..159fa54fde 100644 --- a/fast/stages/2-project-factory/README.md +++ b/fast/stages/2-project-factory/README.md @@ -354,15 +354,14 @@ The approach is not shown here but reasonably easy to implement. The main projec | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| | [billing_account](variables-fast.tf#L17) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [default_alerts_email](variables-fast.tf#L30) | Default email address for alerting. | string | ✓ | | | -| [prefix](variables-fast.tf#L71) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | -| [factories_config](variables.tf#L17) | Configuration for YAML-based factories. | object({…}) | | {} | | -| [folder_ids](variables-fast.tf#L36) | Folders created in the resource management stage. | map(string) | | {} | 1-resman | -| [groups](variables-fast.tf#L44) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | map(string) | | {} | 0-bootstrap | -| [host_project_ids](variables-fast.tf#L53) | Host project for the shared VPC. | map(string) | | {} | 2-networking | -| [locations](variables-fast.tf#L61) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | -| [service_accounts](variables-fast.tf#L81) | Automation service accounts in name => email format. | map(string) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L89) | FAST-managed resource manager tag values. | map(string) | | {} | 1-resman | +| [prefix](variables-fast.tf#L65) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [factories_config](variables.tf#L17) | Configuration for YAML-based factories. | object({…}) | | {} | | +| [folder_ids](variables-fast.tf#L30) | Folders created in the resource management stage. | map(string) | | {} | 1-resman | +| [groups](variables-fast.tf#L38) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | map(string) | | {} | 0-bootstrap | +| [host_project_ids](variables-fast.tf#L47) | Host project for the shared VPC. | map(string) | | {} | 2-networking | +| [locations](variables-fast.tf#L55) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | +| [service_accounts](variables-fast.tf#L75) | Automation service accounts in name => email format. | map(string) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L83) | FAST-managed resource manager tag values. | map(string) | | {} | 1-resman | ## Outputs diff --git a/fast/stages/2-project-factory/main.tf b/fast/stages/2-project-factory/main.tf index 98fe3eb41f..12fd9f2bda 100644 --- a/fast/stages/2-project-factory/main.tf +++ b/fast/stages/2-project-factory/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * 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. @@ -31,11 +31,7 @@ module "projects" { data_overrides = { prefix = var.prefix } - default_alerts_email = var.default_alerts_email factories_config = merge(var.factories_config, { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts context = { folder_ids = merge( { for k, v in var.folder_ids : k => v if v != null }, diff --git a/fast/stages/2-project-factory/variables-fast.tf b/fast/stages/2-project-factory/variables-fast.tf index 304100e902..39aac8e468 100644 --- a/fast/stages/2-project-factory/variables-fast.tf +++ b/fast/stages/2-project-factory/variables-fast.tf @@ -27,12 +27,6 @@ variable "billing_account" { } } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "folder_ids" { # tfdoc:variable:source 1-resman description = "Folders created in the resource management stage." diff --git a/fast/stages/2-project-factory/variables.tf b/fast/stages/2-project-factory/variables.tf index a551c9c72b..a0bc5d7fdf 100644 --- a/fast/stages/2-project-factory/variables.tf +++ b/fast/stages/2-project-factory/variables.tf @@ -17,7 +17,8 @@ variable "factories_config" { description = "Configuration for YAML-based factories." type = object({ - alerts = optional(string, "data/alerts") + folders_data_path = optional(string, "data/hierarchy") + projects_data_path = optional(string, "data/projects") budgets = optional(object({ billing_account = string budgets_data_path = optional(string, "data/budgets") @@ -30,10 +31,6 @@ variable "factories_config" { tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) }), {}) - channels = optional(string, "data/channels") - folders_data_path = optional(string, "data/hierarchy") - logging_metrics = optional(string, "data/logging-metrics") - projects_data_path = optional(string, "data/projects") }) nullable = false default = {} diff --git a/fast/stages/2-security/README.md b/fast/stages/2-security/README.md index 9faa028b36..aa13a0a152 100644 --- a/fast/stages/2-security/README.md +++ b/fast/stages/2-security/README.md @@ -299,20 +299,18 @@ tls_inspection = { |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [default_alerts_email](variables-fast.tf#L47) | Default email address for alerting. | string | ✓ | | | -| [environments](variables-fast.tf#L53) | Environment names. | map(object({…})) | ✓ | | 0-globals | -| [factories_config](variables.tf#L184) | Configuration for network resource factories. | object({…}) | ✓ | | | -| [folder_ids](variables-fast.tf#L70) | Folder name => id mappings, the 'security' folder name must exist. | object({…}) | ✓ | | 1-resman | -| [prefix](variables-fast.tf#L80) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [environments](variables-fast.tf#L47) | Environment names. | map(object({…})) | ✓ | | 0-globals | +| [folder_ids](variables-fast.tf#L64) | Folder name => id mappings, the 'security' folder name must exist. | object({…}) | ✓ | | 1-resman | +| [prefix](variables-fast.tf#L74) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | | [cas_configs](variables.tf#L17) | The CAS CAs to add to each environment. | object({…}) | | {…} | | | [custom_roles](variables-fast.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [essential_contacts](variables.tf#L178) | Email used for essential contacts, unset if null. | string | | null | | -| [kms_keys](variables.tf#L194) | KMS keys to create, keyed by name. | map(object({…})) | | {} | | -| [ngfw_tls_configs](variables.tf#L233) | The CAS and trust configurations key names to be used for NGFW Enterprise. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L259) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | -| [stage_config](variables-fast.tf#L90) | FAST stage configuration. | object({…}) | | {} | 1-resman | -| [tag_values](variables-fast.tf#L104) | Root-level tag values. | map(string) | | {} | 1-resman | -| [trust_configs](variables.tf#L265) | The trust configs grouped by environment. | object({…}) | | {…} | | +| [kms_keys](variables.tf#L184) | KMS keys to create, keyed by name. | map(object({…})) | | {} | | +| [ngfw_tls_configs](variables.tf#L223) | The CAS and trust configurations key names to be used for NGFW Enterprise. | object({…}) | | {…} | | +| [outputs_location](variables.tf#L249) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | +| [stage_config](variables-fast.tf#L84) | FAST stage configuration. | object({…}) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L98) | Root-level tag values. | map(string) | | {} | 1-resman | +| [trust_configs](variables.tf#L255) | The trust configs grouped by environment. | object({…}) | | {…} | | ## Outputs diff --git a/fast/stages/2-security/core-dev.tf b/fast/stages/2-security/core-dev.tf index 4398732287..2c089db122 100644 --- a/fast/stages/2-security/core-dev.tf +++ b/fast/stages/2-security/core-dev.tf @@ -27,16 +27,10 @@ module "dev-sec-project" { parent = coalesce( var.folder_ids.security-dev, var.folder_ids.security ) - prefix = var.prefix - billing_account = var.billing_account.id - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } - labels = { environment = "dev" } - services = local.project_services + prefix = var.prefix + billing_account = var.billing_account.id + labels = { environment = "dev" } + services = local.project_services tag_bindings = local.has_env_folders ? {} : { environment = local.env_tag_values["dev"] } diff --git a/fast/stages/2-security/core-prod.tf b/fast/stages/2-security/core-prod.tf index e07837095c..b74a5376a2 100644 --- a/fast/stages/2-security/core-prod.tf +++ b/fast/stages/2-security/core-prod.tf @@ -27,16 +27,10 @@ module "prod-sec-project" { parent = coalesce( var.folder_ids.security-prod, var.folder_ids.security ) - prefix = var.prefix - billing_account = var.billing_account.id - default_alerts_email = var.default_alerts_email - factories_config = { - logging_metrics = var.factories_config.logging_metrics - channels = var.factories_config.channels - alerts = var.factories_config.alerts - } - labels = { environment = "prod" } - services = local.project_services + prefix = var.prefix + billing_account = var.billing_account.id + labels = { environment = "prod" } + services = local.project_services tag_bindings = local.has_env_folders ? {} : { environment = local.env_tag_values["prod"] } diff --git a/fast/stages/2-security/variables-fast.tf b/fast/stages/2-security/variables-fast.tf index 53590c4b81..b8a664e52d 100644 --- a/fast/stages/2-security/variables-fast.tf +++ b/fast/stages/2-security/variables-fast.tf @@ -44,12 +44,6 @@ variable "custom_roles" { default = null } -variable "default_alerts_email" { - description = "Default email address for alerting." - type = string - default = null -} - variable "environments" { # tfdoc:variable:source 0-globals description = "Environment names." diff --git a/fast/stages/2-security/variables.tf b/fast/stages/2-security/variables.tf index 45667e8f04..b13967c0dd 100644 --- a/fast/stages/2-security/variables.tf +++ b/fast/stages/2-security/variables.tf @@ -181,17 +181,6 @@ variable "essential_contacts" { default = null } -variable "factories_config" { - description = "Configuration for network resource factories." - type = object({ - alerts = optional(string, "data/alerts") - channels = optional(string, "data/channels") - logging_metrics = optional(string, "data/logging-metrics") - }) - nullable = false - default = {} -} - variable "kms_keys" { description = "KMS keys to create, keyed by name." type = map(object({ From c3c06d09adff57d7256d9b2719cde5fc8770d990 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Sun, 5 Jan 2025 19:38:14 +0100 Subject: [PATCH 30/33] Remove observability from FAST --- .../0-bootstrap/data/alerts/compliance.yaml | 165 ------------------ .../0-bootstrap/data/channels/compliance.yaml | 20 --- .../data/logging-metrics/compliance.yaml | 112 ------------ fast/stages/1-resman/data/alerts | 1 - fast/stages/1-resman/data/channels | 1 - fast/stages/1-resman/data/logging-metrics | 1 - fast/stages/2-network-security/data/alerts | 1 - fast/stages/2-network-security/data/channels | 1 - .../2-network-security/data/logging-metrics | 1 - fast/stages/2-networking-a-simple/data/alerts | 1 - .../2-networking-a-simple/data/channels | 1 - .../data/logging-metrics | 1 - fast/stages/2-networking-b-nva/data/alerts | 1 - fast/stages/2-networking-b-nva/data/channels | 1 - .../2-networking-b-nva/data/logging-metrics | 1 - .../2-networking-c-separate-envs/data/alerts | 1 - .../data/channels | 1 - .../data/logging-metrics | 1 - fast/stages/2-project-factory/data/alerts | 1 - fast/stages/2-project-factory/data/channels | 1 - .../2-project-factory/data/logging-metrics | 1 - fast/stages/2-security/data/alerts | 1 - fast/stages/2-security/data/channels | 1 - fast/stages/2-security/data/logging-metrics | 1 - 24 files changed, 318 deletions(-) delete mode 100644 fast/stages/0-bootstrap/data/alerts/compliance.yaml delete mode 100644 fast/stages/0-bootstrap/data/channels/compliance.yaml delete mode 100644 fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml delete mode 120000 fast/stages/1-resman/data/alerts delete mode 120000 fast/stages/1-resman/data/channels delete mode 120000 fast/stages/1-resman/data/logging-metrics delete mode 120000 fast/stages/2-network-security/data/alerts delete mode 120000 fast/stages/2-network-security/data/channels delete mode 120000 fast/stages/2-network-security/data/logging-metrics delete mode 120000 fast/stages/2-networking-a-simple/data/alerts delete mode 120000 fast/stages/2-networking-a-simple/data/channels delete mode 120000 fast/stages/2-networking-a-simple/data/logging-metrics delete mode 120000 fast/stages/2-networking-b-nva/data/alerts delete mode 120000 fast/stages/2-networking-b-nva/data/channels delete mode 120000 fast/stages/2-networking-b-nva/data/logging-metrics delete mode 120000 fast/stages/2-networking-c-separate-envs/data/alerts delete mode 120000 fast/stages/2-networking-c-separate-envs/data/channels delete mode 120000 fast/stages/2-networking-c-separate-envs/data/logging-metrics delete mode 120000 fast/stages/2-project-factory/data/alerts delete mode 120000 fast/stages/2-project-factory/data/channels delete mode 120000 fast/stages/2-project-factory/data/logging-metrics delete mode 120000 fast/stages/2-security/data/alerts delete mode 120000 fast/stages/2-security/data/channels delete mode 120000 fast/stages/2-security/data/logging-metrics diff --git a/fast/stages/0-bootstrap/data/alerts/compliance.yaml b/fast/stages/0-bootstrap/data/alerts/compliance.yaml deleted file mode 100644 index a9830b8ccc..0000000000 --- a/fast/stages/0-bootstrap/data/alerts/compliance.yaml +++ /dev/null @@ -1,165 +0,0 @@ -# Copyright 2024 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. - -route-changes: - display_name: "Network Route Changes" - combiner: OR - alert_strategy: - auto_close: 604800s - conditions: - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: global - trigger: - count: 1 - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - notification_channels: - compliance-default - -network-firewall-config-changes: - display_name: "VPC Network Firewall Changes" - combiner: OR - alert_strategy: - auto_close: 604800s - conditions: - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: global - trigger: - count: 1 - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - notification_channels: - compliance-default - -vpc-network-config-changes: - display_name: "VPC Network Changes" - combiner: OR - alert_strategy: - auto_close: 604800s - conditions: - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: global - trigger: - count: 1 - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - notification_channels: - compliance-default - -cloudsql-changes: - display_name: "CloudSQL Changes" - combiner: OR - alert_strategy: - auto_close: 604800s - conditions: - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: global - trigger: - count: 1 - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - notification_channels: - compliance-default - -cloudstorage-changes: - display_name: "Cloud Storage Changes" - combiner: OR - alert_strategy: - auto_close: 604800s - conditions: - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: "gcs_bucket" - trigger: - count: 1 - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - notification_channels: - compliance-default - -customrole-changes: - display_name: "IAM Custom Role Changes" - combiner: OR - alert_strategy: - auto_close: 604800s - conditions: - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: global - trigger: - count: 1 - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - notification_channels: - compliance-default - -audit-changes: - display_name: "Audit Configuration Changes" - combiner: OR - alert_strategy: - auto_close: 604800s - conditions: - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: global - trigger: - count: 1 - aggregations: - per_series_aligner: ALIGN_MEAN - cross_series_reducer: REDUCE_COUNT - alignment_period: "600s" - notification_channels: - compliance-default - -iam-owner-changes: - display_name: "Owner IAM Configuration Changes" - combiner: OR - alert_strategy: - auto_close: 604800s - conditions: - condition_threshold: - comparison: COMPARISON_GT - duration: "0s" - resource_type: global - trigger: - count: 1 - aggregations: - per_series_aligner: ALIGN_DELTA - cross_series_reducer: REDUCE_SUM - alignment_period: "600s" - notification_channels: - compliance-default diff --git a/fast/stages/0-bootstrap/data/channels/compliance.yaml b/fast/stages/0-bootstrap/data/channels/compliance.yaml deleted file mode 100644 index 8d3298a016..0000000000 --- a/fast/stages/0-bootstrap/data/channels/compliance.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2024 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. - -compliance-default: - type: email - display_name: "Default Email Notifications" - email_address: test@company.com - labels: - email_address: "fake_email@blahblah.com" diff --git a/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml b/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml deleted file mode 100644 index 316616e0af..0000000000 --- a/fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2024 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. - -route-changes: - description: "Monitor VPC network route configuration changes inside GCP projects" - filter: | - resource.type="gce_route" - AND ( - protoPayload.methodName="compute.routes.delete" - OR protoPayload.methodName="compute.routes.insert" - ) - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - -network-firewall-config-changes: - description: "Monitor VPC network firewall configuration changes inside GCP projects" - filter: | - resource.type="gce_firewall_rule" - AND ( - protoPayload.methodName="compute.firewalls.delete" - OR protoPayload.methodName="compute.firewalls.insert" - ) - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - -vpc-network-config-changes: - description: "Monitor VPC network configuration changes inside GCP projects" - filter: | - resource.type="gce_network" - AND ( - protoPayload.methodName="compute.networks.insert" - OR protoPayload.methodName="compute.networks.patch" - OR protoPayload.methodName="compute.networks.delete" - OR protoPayload.methodName="compute.networks.removePeering" - OR protoPayload.methodName="compute.networks.addPeering" - ) - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - -cloudsql-changes: - description: "Monitor CloudSQL configuration changes inside GCP projects" - filter: | - protoPayload.methodName="cloudsql.instances.update" - OR protoPayload.methodName="cloudsql.instances.create" - OR protoPayload.methodName="cloudsql.instances.delete" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - -cloudstorage-changes: - description: "Monitor Cloud Storage configuration changes inside GCP projects" - filter: | - resource.type="gcs_bucket" - AND protoPayload.methodName="storage.setIamPermissions" - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - -customrole-changes: - description: "Monitor IAM Custom Role configuration changes inside GCP projects" - filter: | - resource.type="iam_role" - AND ( - protoPayload.methodName="google.iam.admin.v1.CreateRole" - OR protoPayload.methodName="google.iam.admin.v1.DeleteRole" - OR protoPayload.methodName="google.iam.admin.v1.UpdateRole" - ) - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - -audit-changes: - description: "Monitor Audit configuration changes inside GCP projects" - filter: | - protoPayload.methodName="SetIamPolicy" - AND protoPayload.serviceData.policyDelta.auditConfigDeltas:* - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" - -iam-owner-changes: - description: "Monitor IAM Owner configuration changes inside GCP projects" - filter: | - ( - protoPayload.serviceName="cloudresourcemanager.googleapis.com" - AND (ProjectOwnership OR projectOwnerInvitee) - ) - OR ( - protoPayload.serviceData.policyDelta.bindingDeltas.action="REMOVE" - AND protoPayload.serviceData.policyDelta.bindingDeltas.role="roles/owner" - ) - OR ( - protoPayload.serviceData.policyDelta.bindingDeltas.action="ADD" - AND protoPayload.serviceData.policyDelta.bindingDeltas.role="roles/owner" - ) - metric_descriptor: - metric_kind: DELTA - value_type: "INT64" diff --git a/fast/stages/1-resman/data/alerts b/fast/stages/1-resman/data/alerts deleted file mode 120000 index 03f9d9702f..0000000000 --- a/fast/stages/1-resman/data/alerts +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/1-resman/data/channels b/fast/stages/1-resman/data/channels deleted file mode 120000 index 652bcb00ad..0000000000 --- a/fast/stages/1-resman/data/channels +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/1-resman/data/logging-metrics b/fast/stages/1-resman/data/logging-metrics deleted file mode 120000 index 24c8aad4b7..0000000000 --- a/fast/stages/1-resman/data/logging-metrics +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-network-security/data/alerts b/fast/stages/2-network-security/data/alerts deleted file mode 120000 index 03f9d9702f..0000000000 --- a/fast/stages/2-network-security/data/alerts +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-network-security/data/channels b/fast/stages/2-network-security/data/channels deleted file mode 120000 index 652bcb00ad..0000000000 --- a/fast/stages/2-network-security/data/channels +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-network-security/data/logging-metrics b/fast/stages/2-network-security/data/logging-metrics deleted file mode 120000 index 24c8aad4b7..0000000000 --- a/fast/stages/2-network-security/data/logging-metrics +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-networking-a-simple/data/alerts b/fast/stages/2-networking-a-simple/data/alerts deleted file mode 120000 index 03f9d9702f..0000000000 --- a/fast/stages/2-networking-a-simple/data/alerts +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-networking-a-simple/data/channels b/fast/stages/2-networking-a-simple/data/channels deleted file mode 120000 index 652bcb00ad..0000000000 --- a/fast/stages/2-networking-a-simple/data/channels +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-networking-a-simple/data/logging-metrics b/fast/stages/2-networking-a-simple/data/logging-metrics deleted file mode 120000 index 24c8aad4b7..0000000000 --- a/fast/stages/2-networking-a-simple/data/logging-metrics +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-networking-b-nva/data/alerts b/fast/stages/2-networking-b-nva/data/alerts deleted file mode 120000 index 03f9d9702f..0000000000 --- a/fast/stages/2-networking-b-nva/data/alerts +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-networking-b-nva/data/channels b/fast/stages/2-networking-b-nva/data/channels deleted file mode 120000 index 652bcb00ad..0000000000 --- a/fast/stages/2-networking-b-nva/data/channels +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-networking-b-nva/data/logging-metrics b/fast/stages/2-networking-b-nva/data/logging-metrics deleted file mode 120000 index 24c8aad4b7..0000000000 --- a/fast/stages/2-networking-b-nva/data/logging-metrics +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-networking-c-separate-envs/data/alerts b/fast/stages/2-networking-c-separate-envs/data/alerts deleted file mode 120000 index 03f9d9702f..0000000000 --- a/fast/stages/2-networking-c-separate-envs/data/alerts +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-networking-c-separate-envs/data/channels b/fast/stages/2-networking-c-separate-envs/data/channels deleted file mode 120000 index 652bcb00ad..0000000000 --- a/fast/stages/2-networking-c-separate-envs/data/channels +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-networking-c-separate-envs/data/logging-metrics b/fast/stages/2-networking-c-separate-envs/data/logging-metrics deleted file mode 120000 index 24c8aad4b7..0000000000 --- a/fast/stages/2-networking-c-separate-envs/data/logging-metrics +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-project-factory/data/alerts b/fast/stages/2-project-factory/data/alerts deleted file mode 120000 index 03f9d9702f..0000000000 --- a/fast/stages/2-project-factory/data/alerts +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-project-factory/data/channels b/fast/stages/2-project-factory/data/channels deleted file mode 120000 index 652bcb00ad..0000000000 --- a/fast/stages/2-project-factory/data/channels +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-project-factory/data/logging-metrics b/fast/stages/2-project-factory/data/logging-metrics deleted file mode 120000 index 24c8aad4b7..0000000000 --- a/fast/stages/2-project-factory/data/logging-metrics +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/logging-metrics \ No newline at end of file diff --git a/fast/stages/2-security/data/alerts b/fast/stages/2-security/data/alerts deleted file mode 120000 index 03f9d9702f..0000000000 --- a/fast/stages/2-security/data/alerts +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/alerts \ No newline at end of file diff --git a/fast/stages/2-security/data/channels b/fast/stages/2-security/data/channels deleted file mode 120000 index 652bcb00ad..0000000000 --- a/fast/stages/2-security/data/channels +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/channels \ No newline at end of file diff --git a/fast/stages/2-security/data/logging-metrics b/fast/stages/2-security/data/logging-metrics deleted file mode 120000 index 24c8aad4b7..0000000000 --- a/fast/stages/2-security/data/logging-metrics +++ /dev/null @@ -1 +0,0 @@ -../../0-bootstrap/data/logging-metrics \ No newline at end of file From c4008be7e597fa2ade041850d299e3976dec608d Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Sun, 5 Jan 2025 19:40:16 +0100 Subject: [PATCH 31/33] Remove new FAST tests --- tests/fast/stages/s0_bootstrap/simple.yaml | 5 +---- tests/fast/stages/s2_network_security/simple.yaml | 5 +---- tests/fast/stages/s2_network_security/tls.yaml | 5 +---- tests/fast/stages/s2_networking_a_simple/ncc.yaml | 6 ++---- tests/fast/stages/s2_networking_a_simple/simple.yaml | 6 ++---- tests/fast/stages/s2_networking_a_simple/vpn.yaml | 6 ++---- tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml | 6 ++---- tests/fast/stages/s2_networking_b_nva/regional.yaml | 6 ++---- tests/fast/stages/s2_networking_b_nva/simple.yaml | 6 ++---- tests/fast/stages/s2_networking_c_separate_envs/simple.yaml | 6 ++---- tests/fast/stages/s2_project_factory/simple.yaml | 5 +---- tests/fast/stages/s2_security/simple.tfvars | 1 - tests/fast/stages/s2_security/simple.yaml | 5 +---- 13 files changed, 19 insertions(+), 49 deletions(-) diff --git a/tests/fast/stages/s0_bootstrap/simple.yaml b/tests/fast/stages/s0_bootstrap/simple.yaml index edf4e89d2a..259d2e38b2 100644 --- a/tests/fast/stages/s0_bootstrap/simple.yaml +++ b/tests/fast/stages/s0_bootstrap/simple.yaml @@ -16,12 +16,9 @@ counts: google_bigquery_dataset: 1 google_bigquery_default_service_account: 3 google_essential_contacts_contact: 3 - google_logging_metric: 24 google_logging_organization_settings: 1 google_logging_organization_sink: 4 google_logging_project_bucket_config: 4 - google_monitoring_alert_policy: 24 - google_monitoring_notification_channel: 3 google_org_policy_policy: 24 google_organization_iam_binding: 27 google_organization_iam_custom_role: 13 @@ -44,7 +41,7 @@ counts: google_tags_tag_value: 2 local_file: 10 modules: 20 - resources: 286 + resources: 235 outputs: automation: __missing__ diff --git a/tests/fast/stages/s2_network_security/simple.yaml b/tests/fast/stages/s2_network_security/simple.yaml index e438f04680..c0ff76a2d5 100644 --- a/tests/fast/stages/s2_network_security/simple.yaml +++ b/tests/fast/stages/s2_network_security/simple.yaml @@ -16,9 +16,6 @@ counts: google_compute_network_firewall_policy: 2 google_compute_network_firewall_policy_association: 2 google_compute_network_firewall_policy_rule: 4 - google_logging_metric: 8 - google_monitoring_alert_policy: 8 - google_monitoring_notification_channel: 1 google_network_security_firewall_endpoint: 3 google_network_security_firewall_endpoint_association: 6 google_network_security_security_profile: 2 @@ -28,4 +25,4 @@ counts: google_project_service_identity: 1 google_storage_bucket_object: 1 modules: 3 - resources: 42 + resources: 25 diff --git a/tests/fast/stages/s2_network_security/tls.yaml b/tests/fast/stages/s2_network_security/tls.yaml index e1c9994d2a..8c6e0a93dc 100644 --- a/tests/fast/stages/s2_network_security/tls.yaml +++ b/tests/fast/stages/s2_network_security/tls.yaml @@ -308,9 +308,6 @@ counts: google_compute_network_firewall_policy: 2 google_compute_network_firewall_policy_association: 2 google_compute_network_firewall_policy_rule: 4 - google_logging_metric: 8 - google_monitoring_alert_policy: 8 - google_monitoring_notification_channel: 1 google_network_security_firewall_endpoint: 3 google_network_security_firewall_endpoint_association: 6 google_network_security_security_profile: 2 @@ -320,7 +317,7 @@ counts: google_project_service_identity: 1 google_storage_bucket_object: 1 modules: 3 - resources: 42 + resources: 25 outputs: ngfw_enterprise_endpoint_ids: __missing__ diff --git a/tests/fast/stages/s2_networking_a_simple/ncc.yaml b/tests/fast/stages/s2_networking_a_simple/ncc.yaml index 5261c3f2e7..65630a089d 100644 --- a/tests/fast/stages/s2_networking_a_simple/ncc.yaml +++ b/tests/fast/stages/s2_networking_a_simple/ncc.yaml @@ -29,9 +29,7 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_monitoring_alert_policy: 26 - google_logging_metric: 24 - google_monitoring_notification_channel: 3 + google_monitoring_alert_policy: 2 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 google_network_connectivity_hub: 1 @@ -45,4 +43,4 @@ counts: google_tags_tag_binding: 3 google_vpc_access_connector: 2 modules: 24 - resources: 231 + resources: 180 diff --git a/tests/fast/stages/s2_networking_a_simple/simple.yaml b/tests/fast/stages/s2_networking_a_simple/simple.yaml index 75414a35ed..eaf9c6690a 100644 --- a/tests/fast/stages/s2_networking_a_simple/simple.yaml +++ b/tests/fast/stages/s2_networking_a_simple/simple.yaml @@ -35,9 +35,7 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_monitoring_alert_policy: 26 - google_logging_metric: 24 - google_monitoring_notification_channel: 3 + google_monitoring_alert_policy: 2 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 google_project: 3 @@ -50,4 +48,4 @@ counts: google_vpc_access_connector: 2 modules: 29 random_id: 3 - resources: 250 + resources: 199 diff --git a/tests/fast/stages/s2_networking_a_simple/vpn.yaml b/tests/fast/stages/s2_networking_a_simple/vpn.yaml index efb6369d40..d948bcb932 100644 --- a/tests/fast/stages/s2_networking_a_simple/vpn.yaml +++ b/tests/fast/stages/s2_networking_a_simple/vpn.yaml @@ -33,11 +33,9 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_logging_metric: 24 - google_monitoring_alert_policy: 26 + google_monitoring_alert_policy: 2 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 - google_monitoring_notification_channel: 3 google_project: 3 google_project_iam_binding: 2 google_project_iam_member: 20 @@ -48,4 +46,4 @@ counts: google_vpc_access_connector: 2 modules: 31 random_id: 17 - resources: 295 + resources: 244 diff --git a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml index bd38d2f6b5..bb1a447703 100644 --- a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml +++ b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml @@ -36,11 +36,9 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_logging_metric: 24 - google_monitoring_alert_policy: 26 + google_monitoring_alert_policy: 2 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 - google_monitoring_notification_channel: 3 google_network_connectivity_hub: 2 google_network_connectivity_spoke: 4 google_project: 3 @@ -53,4 +51,4 @@ counts: google_vpc_access_connector: 2 modules: 39 random_id: 6 - resources: 312 + resources: 261 diff --git a/tests/fast/stages/s2_networking_b_nva/regional.yaml b/tests/fast/stages/s2_networking_b_nva/regional.yaml index ea2e711dda..bcd5a85555 100644 --- a/tests/fast/stages/s2_networking_b_nva/regional.yaml +++ b/tests/fast/stages/s2_networking_b_nva/regional.yaml @@ -40,11 +40,9 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_logging_metric: 24 - google_monitoring_alert_policy: 26 + google_monitoring_alert_policy: 2 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 - google_monitoring_notification_channel: 3 google_project: 3 google_project_iam_binding: 2 google_project_iam_member: 19 @@ -55,4 +53,4 @@ counts: google_vpc_access_connector: 2 modules: 47 random_id: 6 - resources: 320 + resources: 269 diff --git a/tests/fast/stages/s2_networking_b_nva/simple.yaml b/tests/fast/stages/s2_networking_b_nva/simple.yaml index 0fc2668590..51c09deb68 100644 --- a/tests/fast/stages/s2_networking_b_nva/simple.yaml +++ b/tests/fast/stages/s2_networking_b_nva/simple.yaml @@ -40,11 +40,9 @@ counts: google_dns_response_policy: 1 google_dns_response_policy_rule: 39 google_essential_contacts_contact: 1 - google_logging_metric: 24 - google_monitoring_alert_policy: 26 + google_monitoring_alert_policy: 2 google_monitoring_dashboard: 3 google_monitoring_monitored_project: 2 - google_monitoring_notification_channel: 3 google_project: 3 google_project_iam_binding: 2 google_project_iam_member: 19 @@ -55,4 +53,4 @@ counts: google_vpc_access_connector: 2 modules: 43 random_id: 6 - resources: 298 + resources: 247 diff --git a/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml b/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml index 99d05b5c89..1f684b5980 100644 --- a/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml +++ b/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml @@ -34,10 +34,8 @@ counts: google_dns_response_policy: 2 google_dns_response_policy_rule: 78 google_essential_contacts_contact: 1 - google_logging_metric: 16 - google_monitoring_alert_policy: 20 + google_monitoring_alert_policy: 4 google_monitoring_dashboard: 6 - google_monitoring_notification_channel: 2 google_project: 2 google_project_iam_binding: 2 google_project_iam_member: 16 @@ -48,4 +46,4 @@ counts: google_vpc_access_connector: 2 modules: 22 random_id: 6 - resources: 249 + resources: 215 diff --git a/tests/fast/stages/s2_project_factory/simple.yaml b/tests/fast/stages/s2_project_factory/simple.yaml index c7def2e106..ac5f5dccc1 100644 --- a/tests/fast/stages/s2_project_factory/simple.yaml +++ b/tests/fast/stages/s2_project_factory/simple.yaml @@ -145,12 +145,9 @@ tests/fast/stages/s2_project_factory/tftest.yaml values: counts: google_compute_shared_vpc_service_project: 4 google_folder: 6 - google_logging_metric: 32 - google_monitoring_alert_policy: 32 - google_monitoring_notification_channel: 8 google_project: 4 google_project_iam_member: 4 google_project_service: 4 google_tags_tag_binding: 4 modules: 11 - resources: 98 + resources: 26 diff --git a/tests/fast/stages/s2_security/simple.tfvars b/tests/fast/stages/s2_security/simple.tfvars index feb2a5d427..8713d2693d 100644 --- a/tests/fast/stages/s2_security/simple.tfvars +++ b/tests/fast/stages/s2_security/simple.tfvars @@ -40,7 +40,6 @@ kms_keys = { rotation_period = null } } -factories_config = {} service_accounts = { security = "foobar@iam.gserviceaccount.com" data-platform-dev = "foobar@iam.gserviceaccount.com" diff --git a/tests/fast/stages/s2_security/simple.yaml b/tests/fast/stages/s2_security/simple.yaml index d3581d98a7..5fbb91f0e2 100644 --- a/tests/fast/stages/s2_security/simple.yaml +++ b/tests/fast/stages/s2_security/simple.yaml @@ -429,9 +429,6 @@ counts: google_kms_crypto_key: 8 google_kms_crypto_key_iam_binding: 8 google_kms_key_ring: 8 - google_logging_metric: 16 - google_monitoring_alert_policy: 16 - google_monitoring_notification_channel: 2 google_project: 2 google_project_iam_binding: 4 google_project_iam_member: 6 @@ -440,4 +437,4 @@ counts: google_storage_bucket_object: 1 google_tags_tag_binding: 2 modules: 11 - resources: 100 + resources: 66 From bfeb2b66dbb30f68782980da2ba893a2c17abc83 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Sun, 5 Jan 2025 19:42:23 +0100 Subject: [PATCH 32/33] Remove unused local --- modules/project/alerts.tf | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/project/alerts.tf b/modules/project/alerts.tf index 5ec6fa2a60..625cb189e7 100644 --- a/modules/project/alerts.tf +++ b/modules/project/alerts.tf @@ -121,10 +121,6 @@ locals { } } } - _notification_channel_names = { - for k, v in google_monitoring_notification_channel.channels : - k => v.name - } alerts = merge(local._alerts_factory_data, var.alerts) } From 7100d9bd2f9478d3893c69abcd49492fde994cde Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Sun, 5 Jan 2025 19:55:03 +0100 Subject: [PATCH 33/33] Fix tests --- modules/project-factory/README.md | 4 ++-- tests/modules/project_factory/examples/example.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index a71bfc7b68..7b705612c2 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -266,7 +266,7 @@ module "project-factory" { } } } -# tftest modules=15 resources=59 files=0,1,2,3,4,5,6,7,8 inventory=example.yaml +# tftest modules=15 resources=56 files=0,1,2,3,4,5,6,7,8 inventory=example.yaml ``` A simple hierarchy of folders: @@ -478,7 +478,7 @@ module "project-factory" { projects_data_path = "data/projects" } } -# tftest modules=4 resources=25 files=test-0,test-1,test-2 +# tftest modules=4 resources=22 files=test-0,test-1,test-2 ``` ```yaml diff --git a/tests/modules/project_factory/examples/example.yaml b/tests/modules/project_factory/examples/example.yaml index 9f28a3e08f..a7154ae6f3 100644 --- a/tests/modules/project_factory/examples/example.yaml +++ b/tests/modules/project_factory/examples/example.yaml @@ -377,7 +377,7 @@ counts: google_folder: 5 google_folder_iam_binding: 1 google_kms_crypto_key_iam_member: 1 - google_monitoring_notification_channel: 4 + google_monitoring_notification_channel: 1 google_project: 3 google_project_iam_binding: 2 google_project_iam_member: 14 @@ -389,4 +389,4 @@ counts: google_storage_project_service_account: 3 google_tags_tag_binding: 1 modules: 15 - resources: 59 + resources: 56