I feel like I’m so close but can’t figure out the last bit of formatting when I loop through my data map to create a CloudWatch Dashboard.
Terraform Version
Terraform v1.5.6
Here’s what my code produces:
Terraform will perform the following actions:
# module.test_app_servers_cloudwatch.aws_cloudwatch_dashboard.dashboard will be updated in-place
~ resource "aws_cloudwatch_dashboard" "dashboard" {
~ dashboard_body = jsonencode(
~ {
~ widgets = [
+ {
+ "0" = {
+ properties = {
+ metrics = [
+ "CWAgent",
+ "LogicalDisk",
+ "% Free Space",
+ "Instance",
+ "C:",
+ "InstanceId",
+ "i-01234567890",
+ "objectname",
+ "LogicalDisk",
+ "ImageId",
+ "ami-01234567890",
+ "InstanceType",
+ "t3a.small",
]
+ period = 300
+ region = "us-west-1"
+ stat = "Average"
+ title = "Disk Usage : TESTAPP01"
}
+ type = "metric"
+ width = 12
}
+ "1" = {
+ properties = {
+ metrics = [
+ "CWAgent",
+ "LogicalDisk",
+ "% Free Space",
+ "Instance",
+ "C:",
+ "InstanceId",
+ "i-09876543210",
+ "objectname",
+ "LogicalDisk",
+ "ImageId",
+ "ami-01234567890",
+ "InstanceType",
+ "t3a.small",
]
+ period = 300
+ region = "us-west-1"
+ stat = "Average"
+ title = "Disk Usage : TESTAPP02"
}
+ type = "metric"
+ width = 12
}
},
]
}
)
id = "TESTAPP-test"
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
…as you can see, I’m getting unusable JSON back where my loop structure is producing the keys [ {“0”} and {“1”} ] within the dashboard_body and is producing the following error:
│ Error: Putting dashboard failed: InvalidParameterInput: The dashboard body is invalid, there are 2 validation errors:
│ [
│ {
│ "dataPath": "/widgets/0",
│ "message": "Should have required property 'type'"
│ },
│ {
│ "dataPath": "/widgets/0",
│ "message": "Should have required property 'properties'"
│ }
│ ]
The Code:
resource "aws_cloudwatch_dashboard" "dashboard" {
dashboard_name = "${var.customer_name}-${var.environment}"
dashboard_body = jsonencode({
widgets = [
{
for instance, data in local.instance_data_map[*] : instance => {
type = "metric"
width = 12
properties = {
metrics = [
"CWAgent",
"LogicalDisk",
"% Free Space",
"Instance",
"C:",
"InstanceId",
data.id,
"objectname",
"LogicalDisk",
"ImageId",
data.ami,
"InstanceType",
data.type
],
period = 300
stat = "Average"
region = "${var.region}"
title = "Disk Usage : ${data.name}"
}
}
}
]
})
}
The idea is to create multiple widgets where I plan to display all of the instance’s disks in the widget, 1 widget per instance, if that makes sense.
Alternatively, I’ve also tried the following code:
resource "aws_cloudwatch_dashboard" "dashboard" {
dashboard_name = "${var.customer_name}-${var.environment}"
dashboard_body = jsonencode({
widgets = [
flatten([for instance in local.instance_data_map[*] : [
{
type = "metric"
width = 12
properties = {
metrics = [
"CWAgent",
"LogicalDisk",
"% Free Space",
"Instance",
"C:",
"InstanceId",
"${instance.id}",
"objectname",
"LogicalDisk",
"ImageId",
"${instance.ami}",
"InstanceType",
"${instance.type}"
],
period = 300
stat = "Average"
region = "${var.region}"
title = "Disk Usage : ${instance.name}"
}
}
]])
]
})
}
…which produces the following unusable JSON structure due to the tuple’s brackets:
Terraform will perform the following actions:
# module.test_app_servers_cloudwatch.aws_cloudwatch_dashboard.dashboard will be updated in-place
~ resource "aws_cloudwatch_dashboard" "dashboard" {
~ dashboard_body = jsonencode(
~ {
~ widgets = [
+ [
+ {
+ properties = {
+ metrics = [
+ "CWAgent",
+ "LogicalDisk",
+ "% Free Space",
+ "Instance",
+ "C:",
+ "InstanceId",
+ "i-01234567890",
+ "objectname",
+ "LogicalDisk",
+ "ImageId",
+ "ami-01234567890",
+ "InstanceType",
+ "t3a.small",
]
+ period = 300
+ region = "us-west-1"
+ stat = "Average"
+ title = "Disk Usage : TESTAPP01"
}
+ type = "metric"
+ width = 12
},
+ {
+ properties = {
+ metrics = [
+ "CWAgent",
+ "LogicalDisk",
+ "% Free Space",
+ "Instance",
+ "C:",
+ "InstanceId",
+ "i-09876543210",
+ "objectname",
+ "LogicalDisk",
+ "ImageId",
+ "ami-01234567890",
+ "InstanceType",
+ "t3a.small",
]
+ period = 300
+ region = "us-west-1"
+ stat = "Average"
+ title = "Disk Usage : TESTAPP02"
}
+ type = "metric"
+ width = 12
},
],
]
}
)
id = "TESTAPP-test"
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
…which produces the error:
Error: Putting dashboard failed: InvalidParameterInput: The dashboard body is invalid, there are 1 validation errors:
│ [
│ {
│ "dataPath": "/widgets/0",
│ "message": "Should be object"
│ }
│ ]
But as you saw earlier, when I send an object, I’m stuck with the keys that further invalidates the JSON code.
And here is the way I’m building my “data-map” object:
locals {
instance_data_map = flatten([
for instance, data in data.aws_instance.instance[*] : [
for key, value in data : {
name = key
id = value.instance_id
type = value.instance_type
ami = value.ami
}
]
])
}
The Data Source is the return objects when I pass in the instance-id and am building the “Data-Map” object with only the information I need for the dashboard.
I would certainly appreciate any input here. so far I feel like I’ve tried everything I could find online but the two above are the only ways I can even generate a TF PLan w/o errors, let alone even to try to apply it.
Thanks in advance!