Templatefile inside dashboard_body

Hello everyone,

I was wondering if its possible to use templatefile inside of a dashboard_body resource. I’m trying the following.

     dashboard_body = <<EOF
{
  "widgets": [
    {
      "type": "metric",
      "x": 0,
      "y": 0,
      "width": 12,
      "height": 6,
      "properties": {
        "metrics": [
          templatefile("${path.module}/backends.tmpl", { instances = aws_instance.web })
        ],
        "period": 300,
        "stat": "Average",
        "region": "us-east-1",
        "title": "EC2 Instance CPU"
      }
    }
  ]
}
EOF

With templatefile

%{ for instance in instances ~}
          [
            "AWS/EC2",
            "CPUUtilization",
            "InstanceId",
            "${instance.id}"
          ]
%{ endfor ~}

However I get the following error message when I run terraform apply.

Error: “dashboard_body” contains an invalid JSON: invalid character ‘e’ in literal true (expecting ‘r’)

on dashboards.tf line 1, in resource “aws_cloudwatch_dashboard” “main”:
1: resource “aws_cloudwatch_dashboard” “main” {

Thanks in advance for your help.

Hi @fpena06,

In order to evaluate an expression inside a template you’ll need to wrap it in ${ ... } markers to tell Terraform you intend to switch into expression mode, rather than literal mode:

        "metrics": [
          ${templatefile("${path.module}/backends.tmpl", { instances = aws_instance.web })}
        ],

With that said, trying to generate JSON with string concatenation (which is what Terraform template are, essentially) will always be tricky to get exactly right, because you need to worry about getting all of the delimiters in the right places and the nesting correct. I’d recommend instead using Terraform’s jsonencode function to generate the final JSON from a normal Terraform data structure, and then the result is guaranteed to have valid JSON syntax:

  dashboard_body = jsonencode({
    "widgets": [
      {
        "type": "metric",
        "x": 0,
        "y": 0,
        "width": 12,
        "height": 6,
        "properties": {
          "metrics": [
            for instance in aws_instance.web : [
                "AWS/EC2",
                "CPUUtilization",
                "InstanceId",
                instance.id,
            ]
          ],
          "period": 300,
          "stat": "Average",
          "region": "us-east-1",
          "title": "EC2 Instance CPU"
        }
      }
    ]
  })

Terraform supports JSON-like object syntax in the language specifically to enable easy conversion from literal JSON strings into normal Terraform expressions like this, so although the above resembles JSON syntax it’s actually a Terraform object constructor expression passed as an argument to jsonencode. The result will be a string with a JSON serialization of the result of that expression, which will include the generated list of lists in "metrics" which I wrote here with a for expression, the expression equivalent of a template for directive.