How to Create CloudWatch Alarms for Every EC2 Instance (Without a Manual CLI Loop)
`aws cloudwatch put-metric-alarm` takes exactly one `--dimensions Name=InstanceId,Value=<id>` at a time. There's no `--all-instances` flag, no wildcard dimension value, no account-wide mode. If you want CPUUtilization and StatusCheckFailed alarms on every EC2 instance you're running, the CLI's answer is: write a loop.
- What does a real "alarm for all instances" script actually look like?
- Which CloudWatch alarms does every EC2 instance actually need?
- Why does this script quietly stop covering new instances?
- How does Nuberio Audit find EC2 instances without alarms?
What does a real "alarm for all instances" script actually look like?
It's a two-step shape: list every instance ID in the region with `describe-instances`, then loop `put-metric-alarm` once per instance per metric. For three alarms per instance — CPU, and the two AWS/EC2 status checks — that's three CLI calls per instance, every time.
#!/usr/bin/env bash
set -euo pipefail
REGION="us-east-1"
SNS_TOPIC_ARN="arn:aws:sns:us-east-1:111111111111:alerts"
INSTANCE_IDS=$(aws ec2 describe-instances \
--region "$REGION" \
--filters "Name=instance-state-name,Values=pending,running,stopping,stopped" \
--query "Reservations[].Instances[].InstanceId" \
--output text)
for ID in $INSTANCE_IDS; do
aws cloudwatch put-metric-alarm \
--region "$REGION" \
--alarm-name "${ID}-cpu-high" \
--metric-name CPUUtilization --namespace AWS/EC2 \
--statistic Average --period 300 --threshold 80 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 3 --datapoints-to-alarm 2 \
--treat-missing-data breaching \
--dimensions Name=InstanceId,Value="$ID" \
--alarm-actions "$SNS_TOPIC_ARN"
aws cloudwatch put-metric-alarm \
--region "$REGION" \
--alarm-name "${ID}-status-failed" \
--metric-name StatusCheckFailed --namespace AWS/EC2 \
--statistic Maximum --period 60 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 2 --datapoints-to-alarm 1 \
--treat-missing-data breaching \
--dimensions Name=InstanceId,Value="$ID" \
--alarm-actions "$SNS_TOPIC_ARN"
aws cloudwatch put-metric-alarm \
--region "$REGION" \
--alarm-name "${ID}-ebs-failed" \
--metric-name StatusCheckFailed_AttachedEBS --namespace AWS/EC2 \
--statistic Maximum --period 60 --threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 2 --datapoints-to-alarm 1 \
--treat-missing-data breaching \
--dimensions Name=InstanceId,Value="$ID" \
--alarm-actions "$SNS_TOPIC_ARN"
doneThat's one region. If instances run across multiple regions — most accounts past the first few months do — the whole block runs again per region, with `REGION` swapped each time. There's no cross-region `describe-instances` call; AWS APIs are region-scoped, so the loop-of-loops is the honest shape of a full-account version.
Which CloudWatch alarms does every EC2 instance actually need?
Three, at minimum: CPUUtilization above 80% sustained, StatusCheckFailed (the instance-level check — hardware or hypervisor), and StatusCheckFailed_AttachedEBS (the volume-level check, catching an unreachable EBS volume that a plain instance-status alarm won't catch on its own).
| Metric | Statistic / period | Threshold | What it actually catches |
|---|---|---|---|
| CPUUtilization | Average, 300s, 3 periods (2 to alarm) | > 80% | Sustained CPU pressure — not a single spike, since it needs 2 of 3 five-minute periods over threshold |
| StatusCheckFailed | Maximum, 60s, 2 periods (1 to alarm) | ≥ 1 | Instance-level failure — underlying hardware or hypervisor problem, usually needs a stop/start to recover from |
| StatusCheckFailed_AttachedEBS | Maximum, 60s, 2 periods (1 to alarm) | ≥ 1 | The attached EBS volume specifically has failed its reachability check — a CPU or plain status-check alarm won't fire for this |
Why does this script quietly stop covering new instances?
Because it's a snapshot, not a subscription. The `describe-instances` call at the top only sees instances that exist at the moment the script runs. Launch a new instance an hour later — a manual EC2 launch, an Auto Scaling replacement, a new box from a Terraform apply that didn't also wire up alarms — and it has zero CloudWatch alarms until someone re-runs the script or remembers to add it by hand.
That's the actual failure mode behind "create a CloudWatch alarm for all instances" as a search query: it's rarely a one-time setup problem. It's the ongoing problem of a fleet that changes shape faster than anyone re-runs the loop.
How does Nuberio Audit find EC2 instances without alarms?
It re-runs the discovery step for you, on demand: a paginated `DescribeInstancesCommand` call per configured region, excluding only terminated instances, merged into one account-wide list. Every instance in that list is checked against the same three metrics above — not a sample, not the ones that happen to be tagged, all of them.
One honest detail worth stating plainly rather than glossing over: Nuberio Audit doesn't try to infer production vs. non-production status for EC2 instances from tags or naming the way it does for some other resource types. Every EC2 instance is treated as non-prod for severity purposes, which downgrades what would otherwise be a critical missing-alarm finding to high. It still shows up, with the exact `put-metric-alarm` command to fix it — it's just not flagged at the top severity tier by default.
What it doesn't do: create the alarm for you. The audit is read-only — it produces the same `put-metric-alarm` commands shown above, pre-filled with the real instance ID and name, for whichever of the three metrics are actually missing on each instance. You still run the command (or hand it to Terraform/CloudFormation); the audit's job is finding every gap across the account in one pass instead of one `describe-instances` call and a loop you have to remember to re-run.
Frequently asked questions
How do I create a CloudWatch alarm for all EC2 instances in my account?
There's no built-in "all instances" option in the CloudWatch API — `put-metric-alarm` takes one InstanceId dimension at a time. The standard approach is a script that lists instance IDs with `describe-instances`, then loops `put-metric-alarm` once per instance per metric, per region. Nuberio Audit runs the equivalent discovery automatically and generates the missing commands for you, without creating the alarms itself.
Does AWS have a built-in way to alarm on all EC2 instances automatically?
Not directly through CloudWatch alarms. The closest native options are CloudWatch Agent default dashboards (visibility, not alarms) or writing the alarms into your launch template/Auto Scaling group lifecycle hook so every new instance gets alarmed at launch time — that covers instances going forward but doesn't retroactively cover ones that already exist without alarms.
What CloudWatch alarms does every EC2 instance need at minimum?
Three: CPUUtilization (sustained, not spike-sensitive — 2 of 3 five-minute periods over 80%), StatusCheckFailed (instance-level hardware/hypervisor check), and StatusCheckFailed_AttachedEBS (the volume-reachability check, which a plain status-check alarm won't catch). These are also the three Nuberio Audit checks for every discovered instance.
Will a newly launched EC2 instance automatically get alarms?
No, unless you've explicitly wired alarm creation into the launch path — a launch template's user data, an Auto Scaling lifecycle hook, or IaC that provisions the instance and its alarms together. A one-off script run today has no visibility into an instance launched tomorrow, which is the most common way accounts end up with unmonitored EC2 instances.
Does Nuberio Audit create the missing alarms for me?
No — it's read-only. It discovers every EC2 instance account-wide, checks each against the same three-metric baseline above, and returns the exact `put-metric-alarm` command for whatever's missing on each instance. You (or your IaC) still run it.
Related reading
- → The Complete AWS CloudWatch Alarm Setup Guide
- → EC2 has no memory or disk alarms until you install the CloudWatch Agent
- → The 12 CloudWatch alarms every small AWS team should have
- → EC2 StatusCheckFailed — threshold & debugging guide
- → EC2 CPUUtilization — threshold & debugging guide
- → Run a free Nuberio Audit — find every EC2 instance missing alarms
Not sure your alarm coverage is actually solid?
Run a free Nuberio Audit — hygiene score, missing alarms, and security findings in about 5 minutes.