Add Alerts, Logging, Channels Factories (#2758)

* WIP: Logging Alerts Factory

* Implement Logging Alerts on Remaining Modules

* Documentation & FMT

* Convert To Multiple Factories

* Correct Project

* Update Documentation

* Update modules/project/alerts-factory.tf

Co-authored-by: Julio Castillo <jccb@google.com>

* Update fast/stages/0-bootstrap/data/logging-metrics/compliance.yaml

Co-authored-by: Julio Castillo <jccb@google.com>

* Update Tests, Resources

* tests

* Fix Tests

* Fix formatting

* Reformat metric filters

* Formatting, reordering, and small fixes

* Bring back alerts and metrics documentation

* Revert change bootstrap outputs.tf

* Fix project notification channel vars and factories

* Fix vars and factory for logging alerts

* Complete alert variable and factory

* Reorder fields

* Update readme

* Reorder variables

* Add schemas, update README, and fix some types

* Remove default alerts email from project and project-factory

* Move observability factory to a single file

* Add outputs to project module

* Add factories_config to PF data_defaults and data_overrides

* Reorder PF field processing

* Revert fast/ to master.

We'll do observability stuff in a separate PR

* Remove observability from FAST

* Remove new FAST tests

* Remove unused local

* Fix tests

---------

Co-authored-by: Julio Castillo <jccb@google.com>
Co-authored-by: Ludovico Magnocavallo <ludomagno@google.com>
This commit is contained in:
Joshua Wright
2025-01-05 19:49:20 +00:00
committed by GitHub
parent 2e86b09d0b
commit 325a997d79
13 changed files with 1446 additions and 67 deletions

File diff suppressed because one or more lines are too long

285
modules/project/alerts.tf Normal file
View File

@@ -0,0 +1,285 @@
/**
* 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 {
_alerts_factory_data_raw = merge([
for k in local.observability_factory_data_raw :
lookup(k, "alerts", {})
]...)
_alerts_factory_data = {
for k, v in local._alerts_factory_data_raw :
k => {
combiner = v.combiner
display_name = try(v.display_name, null)
enabled = try(v.enabled, 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 : {
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)
}]
}
}
}
alerts = merge(local._alerts_factory_data, var.alerts)
}
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 = [
for x in each.value.notification_channels :
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
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
}
}
dynamic "notification_rate_limit" {
for_each = alert_strategy.value.notification_rate_limit[*]
content {
period = notification_rate_limit.value.period
}
}
}
}
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 "trigger" {
for_each = condition_absent.value.trigger[*]
content {
count = trigger.value.count
percent = trigger.value.percent
}
}
}
}
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
}
}
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
}
}
}
dynamic "condition_prometheus_query_language" {
for_each = conditions.value.condition_prometheus_query_language[*]
content {
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 "condition_threshold" {
for_each = conditions.value.condition_threshold[*]
content {
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 = each.value.documentation[*]
content {
content = documentation.value.content
mime_type = documentation.value.mime_type
subject = documentation.value.subject
dynamic "links" {
for_each = documentation.value.links[*]
content {
display_name = links.value.display_name
url = links.value.url
}
}
}
}
}

View File

@@ -0,0 +1,120 @@
/**
* 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 {
_logging_metrics_factory_data_raw = merge([
for k in local.observability_factory_data_raw :
lookup(k, "logging_metrics", {})
]...)
_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)
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
}
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
}
}
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 vv in v.metric_descriptor.labels :
{
key = vv.key
description = try(vv.description, null)
value_type = try(vv.value_type, null)
}
]
}
}
}
metrics = merge(local._logging_metrics_factory_data, var.logging_metrics)
}
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 = each.value.bucket_options[*]
content {
dynamic "explicit_buckets" {
for_each = bucket_options.value.explicit_buckets[*]
content {
bounds = explicit_buckets.value.bounds
}
}
dynamic "exponential_buckets" {
for_each = bucket_options.value.exponential_buckets[*]
content {
num_finite_buckets = exponential_buckets.value.num_finite_buckets
growth_factor = exponential_buckets.value.growth_factor
scale = exponential_buckets.value.scale
}
}
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 {
key = labels.value.key
description = labels.value.description
value_type = labels.value.value_type
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,60 @@
/**
* 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 k in local.observability_factory_data_raw :
lookup(k, "notification_channels", {})
]...)
_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
}
}
}

View File

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

View File

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

View File

@@ -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,116 @@
* limitations under the License.
*/
variable "alerts" {
description = "Monitoring alerts."
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)
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(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({
duration = string
query = string
evaluation_missing_data = optional(string)
trigger = optional(object({
count = optional(number)
percent = optional(number)
}))
}))
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)
}))
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 = string
}))
trigger = optional(object({
count = optional(number)
percent = optional(number)
}))
}))
})), [])
documentation = optional(object({
content = optional(string)
mime_type = optional(string)
subject = optional(string)
links = optional(list(object({
display_name = optional(string)
url = optional(string)
})))
}))
}))
nullable = false
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)))
@@ -36,11 +146,41 @@ variable "logging_exclusions" {
nullable = false
}
variable "log_scopes" {
description = "Log scopes under this project."
variable "logging_metrics" {
description = "Log-based metrics."
type = map(object({
description = optional(string)
resource_names = list(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)
}))
exponential_buckets = optional(object({
num_finite_buckets = number
growth_factor = number
scale = number
}))
linear_buckets = optional(object({
num_finite_buckets = number
width = number
offset = number
}))
}))
metric_descriptor = optional(object({
metric_kind = string
value_type = string
display_name = optional(string)
unit = optional(string)
labels = optional(list(object({
key = string
description = optional(string)
value_type = optional(string)
})), [])
}))
}))
nullable = false
default = {}
@@ -83,3 +223,22 @@ variable "metric_scopes" {
default = []
nullable = false
}
variable "notification_channels" {
description = "Monitoring notification channels."
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 = {}
}

View File

@@ -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.
@@ -81,9 +81,13 @@ 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)
observability = optional(string)
org_policies = optional(string)
quotas = optional(string)
context = optional(object({
notification_channels = optional(map(string), {})
}), {})
})
nullable = false
default = {}