In-line For loop over a data-map object into a jsonencode structure

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!