# Creating Composite and Anomaly Detection Alarms in CloudWatch: A Complete Guide

> Master CloudWatch composite and anomaly detection alarms. Learn to aggregate metrics and use ML for dynamic thresholds to proactively monitor your AWS environment. Get the complete guide.

- Repository: [Amazon Web Services/agent-toolkit-for-aws](https://github.com/aws/agent-toolkit-for-aws)
- Tags: how-to-guide
- Published: 2026-07-02

---

**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`](https://github.com/aws/agent-toolkit-for-aws/blob/main/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`](https://github.com/aws/agent-toolkit-for-aws/blob/main/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`](https://github.com/aws/agent-toolkit-for-aws/blob/main/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()`.

```typescript
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.

```json
{
  "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`](https://github.com/aws/agent-toolkit-for-aws/blob/main/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`](https://github.com/aws/agent-toolkit-for-aws/blob/main/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.

```bash
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 = 3` and `DatapointsToAlarm = 2` to prevent flapping while maintaining responsiveness (see lines 18-20 of [`alarm-template.ts`](https://github.com/aws/agent-toolkit-for-aws/blob/main/alarm-template.ts)).
- **Missing Data Handling**: Configure `TreatMissingData = NOT_BREACHING` so 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 `CompositeAlarm` and `AlarmRule.anyOf()`.
- **Anomaly detection alarms** replace static thresholds with dynamic ML-generated bands using `ANOMALY_DETECTION_BAND` and `LessThanLowerOrGreaterThanUpperThreshold` operators.
- The **[`alarm-template.ts`](https://github.com/aws/agent-toolkit-for-aws/blob/main/alarm-template.ts)** file provides the CDK reference implementation for composite alarms (lines 76-84).
- The **[`alarms.md`](https://github.com/aws/agent-toolkit-for-aws/blob/main/alarms.md)** reference file documents anomaly detection configuration and band width selection.
- The **[`generate_dashboards.py`](https://github.com/aws/agent-toolkit-for-aws/blob/main/generate_dashboards.py)** script automates CloudFormation generation for both alarm types, including the `serverless_alarms` function (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.