Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e3956e5
initial implementation
cx-andre-pereira Nov 4, 2025
d1abf4c
improvements
cx-andre-pereira Nov 5, 2025
2986333
expected results fix
cx-andre-pereira Nov 5, 2025
0f31d91
improved logic part 1
cx-andre-pereira Nov 5, 2025
f88251c
logic improvements
cx-andre-pereira Nov 5, 2025
d3ae9e2
minor cleanup
cx-andre-pereira Nov 5, 2025
d40830e
logic improvement
cx-andre-pereira Nov 5, 2025
452e8a7
Merge branch 'master' into AST-116647_40_6.1.2.11_ensure_activity_log…
cx-andre-pereira Nov 5, 2025
6b822ad
query renaming
cx-andre-pereira Nov 6, 2025
9f5389d
Merge branch 'AST-116647_40_6.1.2.11_ensure_activity_log_alert_for_se…
cx-andre-pereira Nov 6, 2025
0c9bbfd
Added tests for complete QA Accuracy Validation for this query (#7897)
cx-vasco-oliveira Dec 4, 2025
0f646cf
Merge branch 'master' into AST-116647_40_6.1.2.11_ensure_activity_log…
cx-vasco-oliveira Dec 4, 2025
57dc0ad
Fix test for complete QA Accuracy Validation for this query (#7898)
cx-vasco-oliveira Dec 4, 2025
1c6fea4
Merge branch 'master' into AST-116647_40_6.1.2.11_ensure_activity_log…
cx-vasco-oliveira Dec 4, 2025
9d787b3
Fixed test for complete QA Accuracy Validation for this query (#7900)
cx-vasco-oliveira Dec 4, 2025
862810e
Fixed test for complete QA Accuracy Validation for this query (#7902)
cx-vasco-oliveira Dec 5, 2025
da05fe5
Merge branch 'master' into AST-116647_40_6.1.2.11_ensure_activity_log…
cx-vasco-oliveira Dec 5, 2025
6416c58
Merge branch 'master' into AST-116647_40_6.1.2.11_ensure_activity_log…
cx-andre-pereira Dec 9, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"id": "f677bd92-3922-4e75-8f0c-2c0f8fbc9609",
"queryName": "Beta - Activity Log Alert For Service Health Not Configured",
"severity": "MEDIUM",
"category": "Observability",
"descriptionText": "There should be a 'azurerm_monitor_activity_log_alert' resource configured to capture service health events",
"descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_activity_log_alert",
"platform": "Terraform",
"descriptionID": "f677bd92",
"cloudProvider": "azure",
"cwe": "778",
"riskScore": "3.0",
"experimental": "true"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package Cx

import data.generic.common as common_lib
import data.generic.terraform as tf_lib

CxPolicy[result] {
resources := {input.document[index].id : log_alerts | log_alerts := input.document[index].resource.azurerm_monitor_activity_log_alert}
subscriptions := {input.document[index].id : subs | subs := input.document[index].data.azurerm_subscription}

subscriptions[doc_id][name]
value := at_least_one_valid_log_alert(resources, name, doc_id)
value.result != "has_valid_log"

results := get_results(value)[_]
dynamic_values := get_values(results)

result := {
"documentId": results.doc_id,
"resourceType": "azurerm_monitor_activity_log_alert",
"resourceName": dynamic_values.resourceName,
"searchKey": dynamic_values.searchKey,
"issueType": results.issueType,
"keyExpectedValue": "A 'azurerm_monitor_activity_log_alert' resource that monitors 'ServiceHealth' events should be defined for each subscription",
"keyActualValue": results.keyActualValue,
"searchLine": dynamic_values.searchLine
}
}

get_values(results) = dynamic_values {
results.no_log == true
dynamic_values := {
"resourceName": "",
"searchKey": sprintf("azurerm_subscription[%s]", [results.name]),
"searchLine": common_lib.build_search_line(["data", "azurerm_subscription", results.name], [])
}
} else = {
"resourceName": tf_lib.get_resource_name(results.resource, results.name),
"searchKey": sprintf("azurerm_monitor_activity_log_alert[%s].criteria", [results.name]),
"searchLine": common_lib.build_search_line(["resource", "azurerm_monitor_activity_log_alert", results.name, "criteria"], [])
}

at_least_one_valid_log_alert(resources, subscription_name, doc_id_subs) = {"result" : "has_valid_log"} {
resources[doc_index][x].scopes[_] == sprintf("${data.azurerm_subscription.%s.id}",[subscription_name])
resources[doc_index][x].criteria.category == "ServiceHealth"
resources[doc_index][x].criteria.service_health.events[_] == "Incident"
common_lib.valid_key(resources[doc_index][x].action, "action_group_id")

} else = {"result" : "has_log_without_action", "logs": logs} {
logs := {doc_index: filtered |
resources[doc_index]
filtered := {key: resource |
resource := resources[doc_index][key]
resource.scopes[_] == sprintf("${data.azurerm_subscription.%s.id}",[subscription_name])
resource.criteria.category == "ServiceHealth"
resource.criteria.service_health.events[_] == "Incident"}
}
logs[_] != {}

} else = {"result" : "has_log_without_incident_event", "logs": logs} {
logs := {doc_index: filtered |
resources[doc_index]
filtered := {key: resource |
resource := resources[doc_index][key]
resource.scopes[_] == sprintf("${data.azurerm_subscription.%s.id}",[subscription_name])
resource.criteria.category == "ServiceHealth"}
}
logs[_] != {}

} else = {"result" : "has_invalid_logs_only", "logs": logs} {
logs := {doc_index: filtered |
resources[doc_index]
filtered := {key: resource |
resource := resources[doc_index][key]
resource.scopes[_] == sprintf("${data.azurerm_subscription.%s.id}",[subscription_name])}
}
logs[_] != {}
} else = {"result" : "no_logs", "subscription" : subscription_name, "doc_id": doc_id_subs}

get_results(value) = results { # Case of one or more resources failing due to not setting an "action.action_group_id" field
value.result == "has_log_without_action"

results := [z |
log := value.logs[doc_id][name]
z := {
"doc_id" : doc_id,
"resource" : log,
"name" : name,
"issueType": "MissingAttribute",
"keyActualValue" : sprintf("The 'azurerm_monitor_activity_log_alert[%s]' resource monitors 'ServiceHealth' events but is missing an 'action.action_group_id' field", [name])
}]

} else = results { # Case of one or more resources failing due to not including "Incident" in events array
value.result == "has_log_without_incident_event"

results := [z |
log := value.logs[doc_id][name]
z := {
"doc_id" : doc_id,
"resource" : log,
"name" : name,
"issueType": check_service_health_block_issue_type(log) ,
"keyActualValue" : sprintf("The 'azurerm_monitor_activity_log_alert[%s]' resource monitors 'ServiceHealth' events but does not include 'Incident' in its 'criteria.service_health.events' array", [name])
}]

} else = results { # Case of all resources failing due to invalid category and/or operation_name
value.result == "has_invalid_logs_only"

results := [z |
log := value.logs[doc_id][name]
z := {
"doc_id" : doc_id,
"resource" : log,
"name" : name,
"issueType": "IncorrectValue",
"keyActualValue" : "None of the 'azurerm_monitor_activity_log_alert' resources monitor 'ServiceHealth' events"
}]
} else = results { # Case of "subscription" defined without a single alert log associated with it
name := value.subscription
results := [{
"doc_id" : value.doc_id,
"name" : name,
"issueType": "MissingAttribute",
"keyActualValue" : sprintf("There is not a single 'azurerm_monitor_activity_log_alert' resource associated with the '%s' subscription", [name]),
"no_log": true
}]
}


check_service_health_block_issue_type(log) = "IncorrectValue"{ # If events array is set but does not include "Incident"
common_lib.valid_key(log.criteria.service_health, "events")
} else = "MissingAttribute" # If "service_health" or "events" is undefined or null
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
resource "azurerm_monitor_activity_log_alert" "negative1" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.current.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "ServiceHealth"

service_health {
events = ["Incident"]
}
}

action {
action_group_id = azurerm_monitor_action_group.notify_team.id
}
}

resource "azurerm_monitor_activity_log_alert" "negative2" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.current.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "ServiceHealth"

service_health {
events = [
"Incident",
"Maintenance",
"Security",
"Informational"
]
}
}

action {
action_group_id = azurerm_monitor_action_group.notify_team.id
}
}

data "azurerm_subscription" "current" {}

resource "azurerm_monitor_activity_log_alert" "negative3" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.secondary.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "ServiceHealth"

service_health {
events = ["Incident"]
}
}

action {
action_group_id = azurerm_monitor_action_group.notify_team.id
}
}

data "azurerm_subscription" "secondary" {
subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
resource "azurerm_monitor_activity_log_alert" "positive1_1" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.current.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "Security" # Wrong category

service_health {
events = ["Incident"]
}
}

action {
action_group_id = azurerm_monitor_action_group.notify_team.id
}
}

resource "azurerm_monitor_activity_log_alert" "positive1_2" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.current.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "Recommendation" # Wrong category

service_health {
events = ["Incident", "Informational"]
}
}

action {
action_group_id = azurerm_monitor_action_group.notify_team.id
}
}

data "azurerm_subscription" "current" {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
resource "azurerm_monitor_activity_log_alert" "positive2_1" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.current.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "ServiceHealth"

service_health {
events = ["Maintenance"] # Missing 'Incident'
}
}

action {
action_group_id = azurerm_monitor_action_group.notify_team.id
}
}

resource "azurerm_monitor_activity_log_alert" "positive2_2" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.current.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "ServiceHealth"

service_health {
# Missing 'events'
}
}

action {
action_group_id = azurerm_monitor_action_group.notify_team.id
}
}

data "azurerm_subscription" "current" {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
resource "azurerm_monitor_activity_log_alert" "positive2_3" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.current.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "ServiceHealth"

# Missing 'service_health'
}

action {
action_group_id = azurerm_monitor_action_group.notify_team.id
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"queryName": "Beta - Activity Log Alert For Service Health Not Configured",
"severity": "MEDIUM",
"line": 8,
"fileName": "positive2_1.tf"
},
{
"queryName": "Beta - Activity Log Alert For Service Health Not Configured",
"severity": "MEDIUM",
"line": 28,
"fileName": "positive2_1.tf"
},
{
"queryName": "Beta - Activity Log Alert For Service Health Not Configured",
"severity": "MEDIUM",
"line": 8,
"fileName": "positive2_2.tf"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Case of correct "service_health.events" and "category" but the "action.action_group_id" field is missing
resource "azurerm_monitor_activity_log_alert" "positive3_1" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.current.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "ServiceHealth"

service_health {
events = ["Incident"]
}
}

# Missing action
}

data "azurerm_subscription" "current" {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Case of correct "service_health.events" and "category" but the "action.action_group_id" field is missing
resource "azurerm_monitor_activity_log_alert" "positive3_2" {
name = "ServiceHealthActivityLogAlert"
resource_group_name = var.resource_group_name
scopes = [data.azurerm_subscription.current.id]
description = "Alert for Azure Service Health events"
enabled = true

criteria {
category = "ServiceHealth"

service_health {
events = ["Incident"]
}
}

action {
# Missing action_group_id
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"queryName": "Beta - Activity Log Alert For Service Health Not Configured",
"severity": "MEDIUM",
"line": 9,
"fileName": "positive3_1.tf"
},
{
"queryName": "Beta - Activity Log Alert For Service Health Not Configured",
"severity": "MEDIUM",
"line": 9,
"fileName": "positive3_2.tf"
}
]
Loading
Loading