I recently needed to create CPU alarms (and other metrics), for a bunch of EC2 instances. The instances in question weren’t all declared in terraform yet, so I needed a way to create them using a data source. This solution worked nicely.
First, I created a data source for every running instance. If an instance is stopped or removed, this allows us to automatically remove its alarms by running terraform apply
again.
# All AWS Instances
data "aws_instances" "MyInstances" {
instance_state_names = ["running"]
}
(if needed) Next, for each instance, fetch the full information with tags aws_instances
only gives basic info. This is needed if we want to add instance name tags to alarms for example.
# Get full info about each instance
data "aws_instance" "MyInstance" {
for_each = toset(data.aws_instances.MyInstances.ids)
instance_id = each.value
}
Now, you can use those instances as you need. The key here is to use for_each = toset(data.aws_instances.MyInstances.ids)
to loop over each of the instances in the account. Then we can use that instance id to index into the rest of the instance information provided by each aws_instance
. For example, data.aws_instance.MyInstance[each.value].tags.Name
will fetch the Name
tag of the instance:
# Add alarms for each instance
resource "aws_cloudwatch_metric_alarm" "InstanceCPUMetricAlarm" {
for_each = toset(data.aws_instances.MyInstances.ids)
# format: "InstanceCPUAlarm <tags.Name> (<instance id>)"
alarm_name = "InstanceCPUAlarm ${data.aws_instance.MyInstance[each.value].tags.Name} (${each.value})"
# metric config
comparison_operator = "GreaterThanOrEqualToThreshold"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
# configure metrics as you desire
evaluation_periods = "3"
datapoints_to_alarm = "3"
period = "240"
statistic = "Average"
threshold = "95"
# ... rest of config as needed
dimensions = {
# attach instance id to the metric
InstanceId = each.value
}
}