Terraform with EC2 instances as data source

Creating AWS cloudwatch CPU alarms for all instances using a terraform data source.
, Updated: 1 min read

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
  }
}

Subscribe to my Newsletter

Like this post? Subscribe to get notified for future posts like this.

Change Log

  • 2/1/2023 - Initial Revision

Found a typo or technical problem? file an issue!