Skip to content

CloudWatch alarms should have an action configured

A CloudWatch alarm that fires but triggers no action is operationally invisible. Without configured actions, threshold breaches go unnoticed, incidents escalate silently, and the alarm exists only as a dashboard decoration. At that point it tells you nothing you'll ever act on.

Configuring SNS topics, Auto Scaling policies, or Lambda functions as alarm actions gets the right people or systems notified when metrics deviate from expected baselines. For regulated workloads, actionless alarms create a gap between detection and response that auditors will flag immediately.

Retrofit consideration

Existing alarms may have been created without actions, or with actions_enabled set to false. Each alarm needs review to identify the correct notification target (SNS topic, Auto Scaling policy, etc.), and in larger environments that coordination spans multiple teams.

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  = "pcidss.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  = "hipaa.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  = "nist80053.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  = "fedrampmoderate.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  = "nist800171.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  = "awswellarchitected.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  = "ffiec.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  = "cccsmedium.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  = "acscessentialeight.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  = "eugmpannex11.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  = "fedramplow.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  = "cisv120.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  = "hipaasecurity2003.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  = "iso270012013.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  = "nist80053rev4.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

For aws_cloudwatch_metric_alarm, the control validates two things: actions_enabled must be true (the default, but can be set to false explicitly), and at least one of alarm_actions, insufficient_data_actions, or ok_actions must contain a valid ARN. Setting actions_enabled = false fails the control even if action ARNs are present. Leaving all three action lists empty or omitted also fails.

The ARNs typically reference aws_sns_topic resources, but can also point to Auto Scaling policies, SSM OpsItems, or Lambda functions. When the optional named ARN parameter is used by the underlying AWS Config rule, that ARN must appear in one of the action lists.

Common pitfalls

  • actions_enabled defaults to true but can be silently overridden

    The actions_enabled argument defaults to true, so most alarm configurations never set it explicitly. That makes it easy to miss when a module or variable override flips it to false during testing to suppress notifications. The alarm passes Terraform validation but fails this control. Pin actions_enabled = true explicitly in production modules.

  • Empty action lists are valid Terraform but fail the control

    Terraform accepts alarm_actions = [] without error, which makes this easy to miss. If conditional logic that builds the list resolves to empty in certain environments, the alarm has no actions and will be flagged as non-compliant. Check your variable resolution, not just your resource definition.

  • SNS topic exists but has no subscriptions

    This control checks that an action ARN is present on the alarm, not that the target actually works. You can reference an aws_sns_topic in alarm_actions with zero aws_sns_topic_subscription resources and pass the control fine. Alarms will fire into the void. Pair this with subscription verification.

  • Composite alarms require separate action configuration

    The aws_cloudwatch_composite_alarm resource has its own alarm_actions and ok_actions arguments, separate from aws_cloudwatch_metric_alarm, and it doesn't support insufficient_data_actions. If you're using composite alarms alongside metric alarms, configure their actions separately. This control evaluates metric alarms only.

Audit evidence

Auditors expect AWS Config rule evaluation results showing each AWS::CloudWatch::Alarm resource as COMPLIANT. Supporting evidence includes the CloudWatch console alarm list filtered to show the "Actions" column populated for every alarm, or aws cloudwatch describe-alarms output showing non-empty AlarmActions, InsufficientDataActions, or OKActions arrays alongside ActionsEnabled: true.

For deeper validation, auditors may request SNS subscription confirmations proving alarm action targets actually deliver to recipients, along with CloudTrail events showing SetAlarmState or PutMetricAlarm API calls that confirm alarms were configured with actions at creation.

Framework-specific interpretation

SOC 2: CC7.2 and CC7.3 call for monitoring system components and evaluating detected events. SOC 2 Trust Services Criteria also require that identified events reach responsible personnel. An alarm with no action fails that communication requirement outright.

PCI DSS v4.0: Requirement 10.7 wants failures of critical security controls detected, reported, and responded to promptly. Requirement 12.10.1 ties to incident response planning. PCI DSS v4.0 expects monitoring mechanisms to generate actionable alerts, not passive records.

HIPAA Omnibus Rule 2013: 45 CFR 164.312(b) requires audit controls that record and examine activity in information systems. Alarms with configured actions support the timely notification and response workflows that the HIPAA Security Rule expects for incident detection and handling.

NIST SP 800-53 Rev 5: SI-4, AU-6, and IR-4 all depend on alerts reaching someone who can act. NIST 800-53 Rev 5 expects automated mechanisms to integrate monitoring with alerting. An actionless alarm collects data but satisfies none of that automation intent.

NIST Cybersecurity Framework v2.0: DE.CM and RS.AN both come into play here. CloudWatch alarm actions close the loop between detection and automated response, which is core to the Detect and Respond functions in CSF v2.

FedRAMP Moderate Baseline Rev 4: SI-4(5) requires automated mechanisms that alert personnel to unusual or inappropriate activity. At the Moderate baseline, alarms without configured actions fail this requirement directly, and IR-4 incident handling depends on those alerts actually reaching someone.

Tool mappings

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

  • Compliance.tf Control: cloudwatch_alarm_action_enabled

  • AWS Config Managed Rule: CLOUDWATCH_ALARM_ACTION_CHECK

  • Checkov Check: CKV_AWS_319

  • Powerpipe Control: aws_compliance.control.cloudwatch_alarm_action_enabled

  • Prowler Checks: cloudwatch_alarm_actions_alarm_state_configured, cloudwatch_alarm_actions_enabled

  • AWS Security Hub Control: CloudWatch.15

Last reviewed: 2026-03-09