Skip to content

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 show actions_enabled = false until you apply configuration. If actions_enabled is 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 in alarm_actions, ok_actions, or insufficient_data_actions still 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-actions during 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 with actions_enabled = true declared.

  • Composite alarms have separate actions_enabled

    The aws_cloudwatch_composite_alarm resource also has an actions_enabled argument. 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.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: cloudwatch_alarm_action_enabled_check

  • AWS Config Managed Rule: CLOUDWATCH_ALARM_ACTION_ENABLED_CHECK

  • Checkov Check: CKV_AWS_319

  • Powerpipe Control: aws_compliance.control.cloudwatch_alarm_action_enabled_check

  • Prowler Check: cloudwatch_alarm_actions_enabled

  • AWS Security Hub Control: CloudWatch.17

Last reviewed: 2026-03-09