I am trying to create multiple cloudwatch alarms using input from a json file or json locally declared. Either is fine. I've removed the list of
The problem is that this is within a module, so I cannot use file.auto.tfvars.json
which is how I previously did this.
File structure:
.
├── cloudwatch.tf
├── cloudwatchalarms.json
└── locals.tf
What I've tried so far:
jsondecode
to bring in the cloudwatchalarms.json
and loop over it in the following way:cloudwatch.tf
# creates alarms based on what exists within cloudwatchalarms.auto.tfvars.json
resource "aws_cloudwatch_metric_alarm" "alarms" {
for_each = { for alarm in local.all_alarms : alarm => alarm }
alarm_name = "${var.awsResourceName}-${each.value.Identifier}"
namespace = each.value.Namespace
comparison_operator = each.value.ComparisonOperator
evaluation_periods = each.value.EvaluationPeriods
statistic = each.value.Statistic
treat_missing_data = each.value.TreatMissingData
threshold = each.value.Threshold
period = each.value.Period
metric_name = each.value.MetricName
dimensions = {
EcsServiceName = var.awsResourceName
EcsClusterName = var.ecs_cluster_name
}
}
locals.tf
# create locals to pull in cloudwatchalarms.json
locals {
cloudwatchAlarms = jsondecode(file("${path.module}/cloudwatchalarms.json"))
# loop over the cloudwatchalarms json structure
all_alarms = [for alarms in local.cloudwatchAlarms.cloudwatchAlarms : alarms.Identifier]
}
cloudwatchalarms.json
{
"cloudwatchAlarms": [
{
"Identifier": "ServiceMemoryUtilisation",
"Namespace": "AWS/ECS",
"MetricName": "MemoryUtilization",
"Statistic": "Average",
"Threshold": 90,
"Period": 60,
"EvaluationPeriods": 5,
"ComparisonOperator": "GreaterThanThreshold",
"TreatMissingData": "missing"
},
{
"Identifier": "ServiceCPUUtilisation",
"Namespace": "AWS/ECS",
"MetricName": "CPUUtilization",
"Statistic": "Average",
"Threshold": 90,
"Period": 60,
"EvaluationPeriods": 5,
"ComparisonOperator": "GreaterThanThreshold",
"TreatMissingData": "missing"
}
]
}
}
When using this method I get the following errors for every attribute:
Error: Unsupported attribute
on .terraform/modules/terraform-ecs-monitoring/cloudwatch.tf line 4, in resource "aws_cloudwatch_metric_alarm" "alarms":
4: alarm_name = "${var.awsResourceName}-${each.value.Identifier}"
|----------------
| each.value is "TaskStability"
This value does not have any attributes.
locals
cloudwatch.tf
# creates alarms based on what exists within cloudwatchalarms.auto.tfvars.json
resource "aws_cloudwatch_metric_alarm" "alarms" {
for_each = { for alarm in local.cloudwatchAlarms : alarm.Identifier => alarm }
alarm_name = "${var.awsResourceName}-${each.value.Identifier}"
namespace = each.value.Namespace
comparison_operator = each.value.ComparisonOperator
evaluation_periods = each.value.EvaluationPeriods
statistic = each.value.Statistic
treat_missing_data = each.value.TreatMissingData
threshold = each.value.Threshold
period = each.value.Period
metric_name = each.value.MetricName
dimensions = {
EcsServiceName = var.awsResourceName
EcsClusterName = var.ecs_cluster_name
}
}
locals.tf
locals {
cloudwatchAlarms = {
"cloudwatchAlarms": [
{
"Identifier": "ServiceMemoryUtilisation",
"Namespace": "AWS/ECS",
"MetricName": "MemoryUtilization",
"Statistic": "Average",
"Threshold": 90,
"Period": 60,
"EvaluationPeriods": 5,
"ComparisonOperator": "GreaterThanThreshold",
"TreatMissingData": "missing"
},
{
"Identifier": "ServiceCPUUtilisation",
"Namespace": "AWS/ECS",
"MetricName": "CPUUtilization",
"Statistic": "Average",
"Threshold": 90,
"Period": 60,
"EvaluationPeriods": 5,
"ComparisonOperator": "GreaterThanThreshold",
"TreatMissingData": "missing"
}
]
}
}
I then get this error when trying this method:
Error: Unsupported attribute
on .terraform/modules/terraform-ecs-monitoring/cloudwatch.tf line 3, in resource "aws_cloudwatch_metric_alarm" "alarms":
3: for_each = { for alarm in local.cloudwatchAlarms : alarm.Identifier => alarm }
This value does not have any attributes.
Throw to stop pipeline
Any help or pointing in the right direction would be great. Thanks.
You are closer to correctly constructing the set(object)
in the first attempt, and restructuring the data in the second attempt. When combining the two, we arrive at:
locals {
cloudwatchAlarms = jsondecode(file("${path.module}/cloudwatchalarms.json"))
}
resource "aws_cloudwatch_metric_alarm" "alarms" {
for_each = { for alarm in local.cloudwatchAlarms : alarm.Identifier => alarm }
...
}
and your for_each
meta-argument iterates over the set(object)
(implicitly converted from the tuple
type) and retrieves the value for the Identifier
key in each object
and the entire object
. Your Map constructor in the for
expression assigns the former to the key and the latter to the value, which when iterated upon will give you the desired behavior for the body of your resource block as it currently exists in the question.
Note also that:
alarm_name = "${var.awsResourceName}-${each.value.Identifier}"
can be simplified to:
alarm_name = "${var.awsResourceName}-${each.key}"