Creating Composite and Anomaly Detection Alarms in CloudWatch: A Complete Guide
Composite alarms aggregate multiple metric alarms into a single notification using Boolean logic, while anomaly detection alarms use CloudWatch's ML models to create dynamic thresholds that adapt to metric patterns.
The AWS Agent Toolkit for AWS provides production-ready patterns for implementing both alarm types in Amazon CloudWatch. Whether you need to reduce pager noise by grouping related alerts or eliminate static threshold maintenance with machine learning, the Toolkit contains reference implementations in TypeScript CDK and CloudFormation generation scripts. This guide walks through the specific source files and code patterns you need to deploy these advanced alarm configurations.
What Are Composite Alarms in CloudWatch?
A Composite Alarm is a higher-level CloudWatch alarm that transitions to the ALARM state only when any or all of its underlying metric alarms meet specified conditions. Unlike standard metric alarms, composite alarms do not monitor metrics directly—they evaluate Boolean expressions composed of other alarms.
According to the AWS Agent Toolkit source code, all child alarms must reside in the same AWS account and Region as the composite alarm itself. This restriction applies even when using Cross-Account Observability Access Manager (OAM) to aggregate data.
The Toolkit implements this pattern in plugins/aws-core/skills/aws-observability/assets/alarm-template.ts (lines 76-84) using the CDK CompositeAlarm class together with AlarmRule.anyOf(...) for OR logic or AlarmRule.allOf(...) for AND logic.
What Are Anomaly Detection Alarms?
An Anomaly Detection Alarm leverages CloudWatch's built-in machine learning models to learn the normal behavior of a metric over time. Instead of static thresholds, these alarms use a dynamic ANOMALY_DETECTION_BAND that adapts to seasonality, trends, and spikes.
When the metric falls outside the learned band, the alarm transitions to ALARM. The alarm definition resembles a standard MetricAlarm, but uses anomaly-specific comparison operators like LessThanLowerOrGreaterThanUpperThreshold.
The Toolkit documents these requirements in skills/core-skills/aws-observability/references/alarms.md, explaining that the band width typically defaults to 2-sigma (standard deviations) and that the Threshold field encodes the band expression rather than a static value.
How to Create Composite Alarms with the AWS Agent Toolkit
CDK Implementation
The file alarm-template.ts demonstrates the complete pattern for wrapping multiple metric alarms into a single composite alarm. The implementation creates three child alarms—error rate, P99 duration, and throttles—then aggregates them using AlarmRule.anyOf().
import {
Alarm, CompositeAlarm, AlarmRule, AlarmState,
ComparisonOperator, TreatMissingData,
} from 'aws-cdk-lib/aws-cloudwatch';
import { SnsAction } from 'aws-cdk-lib/aws-cloudwatch-actions';
import { IFunction } from 'aws-cdk-lib/aws-lambda';
import { ITopic } from 'aws-cdk-lib/aws-sns';
import { Construct } from 'constructs';
/* Create three child alarms (error-rate, p99 duration, throttles) … */
const errorRateAlarm = new Alarm(this, 'ErrorRateAlarm', {
metric: /* MathExpression that calculates error % */,
threshold: 5,
evaluationPeriods: 3,
datapointsToAlarm: 2,
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
treatMissingData: TreatMissingData.NOT_BREACHING,
});
const durationAlarm = new Alarm(this, 'DurationP99Alarm', {
metric: fn.metricDuration({ statistic: 'p99', period: Duration.minutes(1) }),
threshold: 3000,
evaluationPeriods: 3,
datapointsToAlarm: 2,
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
treatMissingData: TreatMissingData.NOT_BREACHING,
});
const throttleAlarm = new Alarm(this, 'ThrottleAlarm', {
metric: fn.metricThrottles({ period: Duration.minutes(1) }),
threshold: 1,
evaluationPeriods: 3,
datapointsToAlarm: 2,
comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
treatMissingData: TreatMissingData.NOT_BREACHING,
});
/* Composite alarm – pages only when any child alarm is ALARM */
const serviceHealthAlarm = new CompositeAlarm(this, 'ServiceHealthAlarm', {
alarmRule: AlarmRule.anyOf(
AlarmRule.fromAlarm(errorRateAlarm, AlarmState.ALARM),
AlarmRule.fromAlarm(durationAlarm, AlarmState.ALARM),
AlarmRule.fromAlarm(throttleAlarm, AlarmState.ALARM),
),
});
serviceHealthAlarm.addAlarmAction(new SnsAction(snsTopic));
This pattern reduces operational noise by sending a single notification through the SNS topic rather than individual pages for each underlying metric.
Implementing Anomaly Detection Alarms
CloudFormation Structure
To convert a standard alarm to anomaly detection, change the ComparisonOperator to LessThanLowerOrGreaterThanUpperThreshold and reference the ANOMALY_DETECTION_BAND in the Threshold field.
{
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmName": "CPUUtilization-Anomaly",
"AlarmDescription": "CPU usage deviates from learned pattern",
"Namespace": "AWS/EC2",
"MetricName": "CPUUtilization",
"Dimensions": [{ "Name": "InstanceId", "Value": "i-0123456789abcdef0" }],
"ComparisonOperator": "LessThanLowerOrGreaterThanUpperThreshold",
"EvaluationPeriods": 3,
"DatapointsToAlarm": 2,
"TreatMissingData": "notBreaching",
"Statistics": "Average",
"Period": 300,
"Threshold": { "Fn::GetAtt": ["ANOMALY_DETECTION_BAND", "Upper"] }
}
}
The ComparisonOperator tells CloudWatch to compare the metric against both the lower and upper bounds of the learned band. The Toolkit's alarms.md reference file recommends starting with a 2-sigma band width for most workloads.
Automated Generation with Python
For infrastructure-as-code workflows, the Toolkit provides skills/specialized-skills/database-skills/amazon-elasticache/scripts/generate_dashboards.py. This script generates CloudFormation templates containing both dashboards and alarms, including support for anomaly detection thresholds.
python generate_dashboards.py \
--serverless my-cache \
--region us-east-1 \
--sns-topic arn:aws:sns:us-east-1:123456789012:alerts \
--throttle-threshold "ANOMALY_DETECTION_BAND(60,2)" \
--output elasticache-observability.json
The serverless_alarms function (lines 54-84) and generate_template logic (lines 80-118) handle the ComparisonOperator assignment and SNS wiring automatically.
Best Practices for CloudWatch Alarm Configuration
The AWS Agent Toolkit enforces several best practices in its reference implementations:
- Evaluation Periods: Set
EvaluationPeriods = 3andDatapointsToAlarm = 2to prevent flapping while maintaining responsiveness (see lines 18-20 ofalarm-template.ts). - Missing Data Handling: Configure
TreatMissingData = NOT_BREACHINGso that missing data points are interpreted as healthy rather than triggering false alarms. - Regional Co-location: Ensure all child alarms and the composite alarm exist in the same AWS Region and account.
- Anomaly Band Width: Start with the default 2-sigma band unless your metric exhibits high volatility.
- SNS Consolidation: Attach a single SNS topic to the composite alarm rather than distributing actions across individual metric alarms.
Summary
- Composite alarms use Boolean logic to combine multiple metric alarms into a single notification endpoint, implemented in the Toolkit using
CompositeAlarmandAlarmRule.anyOf(). - Anomaly detection alarms replace static thresholds with dynamic ML-generated bands using
ANOMALY_DETECTION_BANDandLessThanLowerOrGreaterThanUpperThresholdoperators. - The
alarm-template.tsfile provides the CDK reference implementation for composite alarms (lines 76-84). - The
alarms.mdreference file documents anomaly detection configuration and band width selection. - The
generate_dashboards.pyscript automates CloudFormation generation for both alarm types, including theserverless_alarmsfunction (lines 54-84).
Frequently Asked Questions
What is the difference between a composite alarm and a metric alarm?
A metric alarm monitors a specific CloudWatch metric against a static or anomaly detection threshold, while a composite alarm evaluates the state of other alarms using Boolean logic. Composite alarms do not directly monitor metrics—they only reference the ALARM/OK state of their child alarms, allowing you to receive a single notification when any of multiple failure conditions occur.
Can I use anomaly detection with composite alarms?
Yes. You can include anomaly detection alarms as child alarms within a composite alarm structure. The composite alarm evaluates the state of the anomaly detection alarm just as it would any other metric alarm, transitioning to ALARM when the metric deviates from the learned band pattern.
Why must all child alarms reside in the same region as the composite alarm?
CloudWatch composite alarms require regional co-location because they evaluate alarm states stored in the CloudWatch regional endpoint. The AWS Agent Toolkit enforces this constraint in its examples, and attempting to reference alarms across regions results in validation errors during deployment, even when using Cross-Account Observability Access Manager (OAM).
How do I prevent false positives with anomaly detection alarms?
Configure the band width appropriately for your metric's volatility—the Toolkit recommends starting with a 2-sigma band (default). Additionally, set TreatMissingData to NOT_BREACHING and use multiple evaluation periods (EvaluationPeriods = 3 with DatapointsToAlarm = 2) to ensure temporary spikes don't trigger immediate notifications.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →