pandaPowder
pandaPowder

Reputation: 2125

How can I modify string literals in Terraform when the string contains JSON?

I am using Terraform to manage some cloudwatch alarms, and when Terraform destroys the alarms I want it to also delete the anomaly detection on the underlying metrics. As far as I can tell, I cannot manage the metrics themselves directly in Terraform (see https://github.com/hashicorp/terraform-provider-aws/issues/18344).

I am using a local-exec provisioner block and I think my problem is the command I'm trying to run involves some JSON. I was also trying to leave some newlines in the command itself for readability sake. I am really struggling with the way Terraform handles string literals. I've tried EOT, EOF, using a variable but I just can't get it to work.

Here's the command I'm trying to get Terraform to execute (this command works from my terminal)

    aws cloudwatch delete-anomaly-detector \
        --single-metric-anomaly-detector 
        '{
        "Namespace": "AWS/ApplicationELB",
        "AccountId": "redacted",
        "MetricName": "RequestCountPerTarget",
        "Dimensions": [
            {
                "Name": "TargetGroup",
                "Value": "redacted"
            }
        ],
        "Stat": "Average"
        }'

Here's my Terraform configuration (truncated for brevity)

resource "aws_cloudwatch_metric_alarm" "example" {
  alarm_name            = "example"
  #....
  #redacted for brevity
  #....
  provisioner "local-exec" {
    when = destroy
    command = format("aws cloudwatch delete-anomaly-detector --single-metric-anomoly-detector '%s'", jsonencode({
        Namespace   = "AWS\\ApplicationELB"
        AccountId   = "redacted"
        MetricName  = "RequestCountPerTarget"
        Dimensions  = [{
          Name  = "TargetGroup"
          Value = "redacted"
         }]
         Stat        = "Average"
       }
    ))
  }
}

When I run terraform apply it does execute, but the command isn't executed properly by the interpreter.

module.example.aws_cloudwatch_metric_alarm.example[0] (local-exec): Executing: ["/bin/sh" "-c" "aws cloudwatch delete-anomaly-detector --single-metric-anomoly-detector '{"AccountId":"redacted","Dimensions":[{"Name":"TargetGroup","Value":"redacted"}],"MetricName":"RequestCountPerTarget","Namespace":"AWS\\ApplicationELB","Stat":"Average"}'"]

module.example.aws_cloudwatch_metric_alarm.example[0] (local-exec): usage: aws [options] [ ...] [parameters] (local-exec): usage: aws [options] [ ...] [parameters]

I also created some gists if that's easier to read.

AWS command I'm trying to get Terraform to run

Terraform config (truncated)

Upvotes: 0

Views: 77

Answers (1)

Fedi Bounouh
Fedi Bounouh

Reputation: 1356

<<EOT Block: The <<EOT (a Here Document) is used to manage multi-line strings without worrying about escaping newlines or quotes. This makes the JSON string more readable.

resource "aws_cloudwatch_metric_alarm" "example" {
      alarm_name            = "example"
      #....
      #redacted for brevity
      #....
      provisioner "local-exec" {
        when    = destroy
        command = <<EOT
    aws cloudwatch delete-anomaly-detector --single-metric-anomaly-detector '{
      "Namespace": "AWS/ApplicationELB",
      "AccountId": "redacted",
      "MetricName": "RequestCountPerTarget",
      "Dimensions": [
        {
          "Name": "TargetGroup",
          "Value": "redacted"
        }
      ],
      "Stat": "Average"
    }'
    EOT
      }
    }

I also updated Namespace to use AWS/ApplicationELB directly without double backslashes (\), as the double backslashes were likely misinterpreted.

Upvotes: 0

Related Questions