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:
File diff suppressed because one or more lines are too long
285
modules/project/alerts.tf
Normal file
285
modules/project/alerts.tf
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
120
modules/project/logging-metrics.tf
Normal file
120
modules/project/logging-metrics.tf
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}-"
|
||||
|
||||
60
modules/project/notification-channels.tf
Normal file
60
modules/project/notification-channels.tf
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
514
modules/project/schemas/observability.schema.json
Normal file
514
modules/project/schemas/observability.schema.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 = {}
|
||||
}
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
Reference in New Issue
Block a user