Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions modules/cis-log-alerts/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ resource "google_monitoring_alert_policy" "alert_policy" {
project = var.project

# notification_channels = var.notification_channels
notification_channels = [google_monitoring_notification_channel.email.name]
notification_channels = concat([google_monitoring_notification_channel.email.name], var.notification_channels)
display_name = each.key
combiner = var.combiner
conditions {
display_name = "each.key"
display_name = each.key
condition_threshold {
filter = each.value
duration = var.duration
Expand Down
6 changes: 6 additions & 0 deletions modules/cis-log-alerts/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ variable "duration" {
type = string
}

variable "notification_channels" {
description = "Additional notification channels for alert policies."
type = list(string)
default = []
}

variable "per_series_aligner" {
description = "Per series aligner."
type = string
Expand Down
4 changes: 2 additions & 2 deletions modules/cis-log-metrics/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ locals {
log_filter = {
project-owner-log = <<EOH
(protoPayload.serviceName="cloudresourcemanager.googleapis.com")
AND (ProjectOwnership OR projectOwnerInvitee)
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")
AND protoPayload.serviceData.policyDelta.bindingDeltas.role="roles/owner"))
EOH

audit-config-change = <<EOH
Expand Down
2 changes: 1 addition & 1 deletion modules/intrusion-detection-system/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ resource "google_compute_global_address" "ids_private_ip" {

# Create Private Connection: ####
resource "google_service_networking_connection" "private_vpc_connection" {
count = var.create_service_networking_connection ? 0 : 1
count = var.create_service_networking_connection ? 1 : 0

network = data.google_compute_network.vpc_network.id
service = "servicenetworking.googleapis.com"
Expand Down
8 changes: 4 additions & 4 deletions modules/organization-se/iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,17 @@ locals {
)
}
iam_bindings_additive = merge(
var.iam_bindings_additive,
[
merge([
for principal, roles in var.iam_by_principals_additive : {
for role in roles :
"iam-bpa:${principal}-${role}" => {
"iam-bpa:${principal}//${role}" => {
member = principal
role = role
condition = null
}
}
]...
]...),
var.iam_bindings_additive
)
}

Expand Down
2 changes: 1 addition & 1 deletion modules/organization-se/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ variable "organization_id" {
description = "Organization id in organizations/nnnnnn format."
type = string
validation {
condition = can(regex("^organizations/[0-9]+", var.organization_id))
condition = can(regex("^organizations/[0-9]+$", var.organization_id))
error_message = "The organization_id must in the form organizations/nnn."
}
}
Expand Down
8 changes: 8 additions & 0 deletions modules/spanner-instance-se/iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ resource "google_spanner_instance_iam_binding" "bindings" {
instance = local.spanner_instance.id
role = each.value.role
members = each.value.members
dynamic "condition" {
for_each = try(each.value.condition, null) == null ? [] : [""]
content {
expression = each.value.condition.expression
title = each.value.condition.title
description = each.value.condition.description
}
}
}

resource "google_spanner_instance_iam_member" "bindings" {
Expand Down
5 changes: 5 additions & 0 deletions modules/spanner-instance-se/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ variable "iam_bindings" {
type = map(object({
members = list(string)
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
}))
nullable = false
default = {}
Expand Down
28 changes: 28 additions & 0 deletions tests/tools/test_blueprint_and_module_validations.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,33 @@ def test_modules_enforce_30_char_id_preconditions(self):
)



def test_custom_stellar_engine_modules_validations(self):
"""Verify hardening and logic fixes across custom Stellar Engine modules."""
alerts_main = (REPO_ROOT / "modules/cis-log-alerts/main.tf").read_text(encoding="utf-8")
self.assertIn("concat([google_monitoring_notification_channel.email.name], var.notification_channels)", alerts_main)
self.assertIn("display_name = each.key", alerts_main)
self.assertNotIn('display_name = "each.key"', alerts_main)

alerts_vars = (REPO_ROOT / "modules/cis-log-alerts/variables.tf").read_text(encoding="utf-8")
self.assertIn('variable "notification_channels"', alerts_vars)

metrics_main = (REPO_ROOT / "modules/cis-log-metrics/main.tf").read_text(encoding="utf-8")
self.assertIn("AND ((ProjectOwnership OR projectOwnerInvitee)", metrics_main)

ids_main = (REPO_ROOT / "modules/intrusion-detection-system/main.tf").read_text(encoding="utf-8")
self.assertIn("count = var.create_service_networking_connection ? 1 : 0", ids_main)

org_se_iam = (REPO_ROOT / "modules/organization-se/iam.tf").read_text(encoding="utf-8")
self.assertIn('"iam-bpa:${principal}//${role}"', org_se_iam)
org_se_vars = (REPO_ROOT / "modules/organization-se/variables.tf").read_text(encoding="utf-8")
self.assertIn('can(regex("^organizations/[0-9]+$", var.organization_id))', org_se_vars)

spanner_se_iam = (REPO_ROOT / "modules/spanner-instance-se/iam.tf").read_text(encoding="utf-8")
self.assertIn('dynamic "condition"', spanner_se_iam)
spanner_se_vars = (REPO_ROOT / "modules/spanner-instance-se/variables.tf").read_text(encoding="utf-8")
self.assertIn("condition = optional(object({", spanner_se_vars)


if __name__ == "__main__":
unittest.main()
Loading