CloudWatch alarms should have action enabled
A CloudWatch alarm with disabled actions is functionally decorative. It will change state internally but never notify an SNS topic, trigger an Auto Scaling policy, or stop a runaway EC2 instance. Teams often disable actions during maintenance windows and forget to re-enable them, creating blind spots that persist for weeks or months.
The failure mode is insidious because dashboards may still show alarm states correctly, giving operators a false sense of coverage. Actual incident response depends on actions firing, not on the alarm existing.
Retrofit consideration
Some alarms may have been intentionally disabled during troubleshooting or migration. Bulk re-enabling without review can trigger a flood of stale notifications if underlying conditions are still in alarm state.
Implementation
Choose the approach that matches how you manage Terraform.
Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.
module "cloudwatch" {
source = "soc2.compliance.tf/terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = ">=5.0.0"
actions_enabled = true
alarm_actions = ["arn:aws:sns:us-east-1:123456789012:example-topic"]
alarm_name = "pofix-abc123"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
}
module "cloudwatch" {
source = "nistcsf.compliance.tf/terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = ">=5.0.0"
actions_enabled = true
alarm_actions = ["arn:aws:sns:us-east-1:123456789012:example-topic"]
alarm_name = "pofix-abc123"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
}
module "cloudwatch" {
source = "nydfs23.compliance.tf/terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = ">=5.0.0"
actions_enabled = true
alarm_actions = ["arn:aws:sns:us-east-1:123456789012:example-topic"]
alarm_name = "pofix-abc123"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
}
module "cloudwatch" {
source = "nistcsfv11.compliance.tf/terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = ">=5.0.0"
actions_enabled = true
alarm_actions = ["arn:aws:sns:us-east-1:123456789012:example-topic"]
alarm_name = "pofix-abc123"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
}
module "cloudwatch" {
source = "pcidssv321.compliance.tf/terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = ">=5.0.0"
actions_enabled = true
alarm_actions = ["arn:aws:sns:us-east-1:123456789012:example-topic"]
alarm_name = "pofix-abc123"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
}
If you use terraform-aws-modules/cloudwatch/aws//modules/metric-alarm, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.
module "cloudwatch" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = ">=5.0.0"
alarm_actions = ["arn:aws:sns:us-east-1:123456789012:example-topic"]
alarm_name = "pofix-abc123"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
actions_enabled = true
}
Use AWS provider resources directly. See docs for the resources involved: aws_cloudwatch_metric_alarm.
resource "aws_cloudwatch_metric_alarm" "this" {
alarm_actions = ["arn:aws:sns:us-east-1:123456789012:example-topic"]
alarm_name = "pofix-abc123"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = 300
statistic = "Average"
threshold = 80
actions_enabled = true
}
What this control checks
The aws_cloudwatch_metric_alarm resource controls this via the actions_enabled argument. Set to true (or omitted, since true is the provider default), the alarm executes its configured alarm_actions, ok_actions, and insufficient_data_actions on state transitions. Setting actions_enabled = false fails the control. To pass, either set it explicitly or rely on the default. Note that the alarm also needs at least one ARN in alarm_actions for the flag to be meaningful, but this control only checks the flag itself.
Common pitfalls
Default is true but explicit false persists after import
Import an alarm that had actions disabled via the console or
aws cloudwatch disable-alarm-actions, and the imported state may showactions_enabled = falseuntil you apply configuration. Ifactions_enabledis omitted from the resource block, Terraform uses the provider default (true) and a plan will show an update to re-enable actions. Make the value explicit rather than relying on the default.Actions enabled but action list is empty
This control passes when
actions_enabled = true, but an alarm with no ARNs inalarm_actions,ok_actions, orinsufficient_data_actionsstill does nothing on state change. Passing this check alone does not guarantee operational alerting.Temporary disable during deployments not reverted
Teams sometimes call
aws cloudwatch disable-alarm-actionsduring blue/green deployments to suppress noise. If the re-enable step is skipped because a CI/CD pipeline fails or a runbook step gets missed, alarms stay silenced. Terraform will not correct this unless the alarm is fully managed in state withactions_enabled = truedeclared.Composite alarms have separate actions_enabled
The
aws_cloudwatch_composite_alarmresource also has anactions_enabledargument. If you use composite alarms to aggregate metric alarms, both layers need actions enabled. This control evaluates metric alarms, but composite alarms with disabled actions create the same blind spot.
Audit evidence
Config rule evaluation results for CLOUDWATCH_ALARM_ACTION_CHECK showing all alarms as compliant is the primary artifact. Supporting evidence includes aws cloudwatch describe-alarms output filtered to show ActionsEnabled: true for every alarm, or console screenshots of the alarm list with the "Actions enabled" column visible.
For ongoing assurance, CloudTrail logs showing DisableAlarmActions and EnableAlarmActions API calls help auditors verify that any temporary disabling was reversed promptly.
Framework-specific interpretation
SOC 2: CC7.2 covers monitoring for anomalies; CC7.3 covers evaluation and response to identified issues. Disabled alarm actions sever the connection between those two requirements: the system detects, but never acts on what it finds. Type II examiners evaluate operating effectiveness, and an alarm that never pages anyone or triggers automation does not demonstrate effective monitoring controls.
NIST Cybersecurity Framework v2.0: DE.CM and DE.AE require that monitoring signals actually reach someone or trigger a response. An alarm that transitions state but never fires its actions satisfies neither. Detection capability that exists only in configuration is not detection capability in practice.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
cloudwatch_alarm_action_enabled_checkAWS Config Managed Rule:
CLOUDWATCH_ALARM_ACTION_ENABLED_CHECKCheckov Check:
CKV_AWS_319Powerpipe Control:
aws_compliance.control.cloudwatch_alarm_action_enabled_checkProwler Check:
cloudwatch_alarm_actions_enabledAWS Security Hub Control:
CloudWatch.17
Last reviewed: 2026-03-09