amazon-web-servicesterraformaws-cloudformation

Invalid template interpolation value


I'm trying to use terraform to create an AnomalySubscription with AWS Cloudformation. Based on: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ce-anomalysubscription.html

When I tested Terraform plan, I'm receiving:

│ Error: Invalid template interpolation value


│     │ each.value.NotificationsEmail is tuple with 1 element
│
│ Cannot include the given value in a string template: string required.

resource.tf

resource "aws_cloudformation_stack" "anomaly_detection" {
  name = "${var.StackName}${each.value.Key}"

  for_each = { for i in local.anomalies : "${i.Key}" => i }

  template_body = <<EOF
  AWSTemplateFormatVersion: "2010-09-09"
  Description: Cost Anomaly Detection
  Resources:
    CostExplorer:
      Type: AWS::CE::AnomalySubscription
      Properties:
        Frequency: DAILY
        MonitorArnList: [!Ref CostExplorerServiceAnomalyMonitor]
        Subscribers: ${each.value.NotificationsEmail}
        SubscriptionName: DailyAnomalySubscription
        Threshold: ${each.value.Threshold}

EOF
}
locals {
  anomalies = flatten([
    for k, v in var.Anomalies : [
        {
          Key                    = k
          Type                   = v.Type
          NotificationsEmail     = v.NotificationsEmail
          Threshold              = v.Threshold
        }
    ]
  ])
}
Anomalies = {
  Subscription = {
    Type  = "Daily"
    Threshold = 500
    NotificationsEmail = [
      {
        Type = "EMAIL"
        Address = "test@xyz.com"
      },
      {
        Type = "EMAIL"
        Address = "test2@xyz.com"
      }]
  }
}

Based on: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ce-anomalysubscription-subscriber.html

    NotificationsEmail = [
      {
        Type = "EMAIL"
        Address = "test@xyz.com"
      },
      {
        Type = "EMAIL"
        Address = "test2@xyz.com"
      }]

Solution

  • An heredoc structure is always a string, this is why terraform complains that it cannot interpolate your tuple.

    Since what you are actually feeding in the heredoc is YAML document, you can encode your tuple to YAML using jsonencode.

    Why JSON encode? As pointed in the documentation, YAML is just a superset of JSON:

    yamlencode always uses YAML's "block style" for mappings and sequences, unless the mapping or sequence is empty. To generate flow-style YAML, use jsonencode instead: YAML flow-style is a superset of JSON syntax.

    Source: https://www.terraform.io/docs/language/functions/yamlencode.html


    Given a simplified:

    variable "NotificationsEmail" {
      default = {
        Type = "EMAIL"
        Address = "test2@xyz.com"
      }
    }
    
    output "test" {
      value = <<EOF
      AWSTemplateFormatVersion: "2010-09-09"
      Description: Cost Anomaly Detection
      Resources:
        CostExplorer:
          Type: AWS::CE::AnomalySubscription
          Properties:
            Frequency: DAILY
            MonitorArnList: [!Ref CostExplorerServiceAnomalyMonitor]
            Subscribers: ${jsonencode(var.NotificationsEmail)}
            SubscriptionName: DailyAnomalySubscription
      EOF
    }
    

    This yields the expected:

    AWSTemplateFormatVersion: "2010-09-09"
    Description: Cost Anomaly Detection
    Resources:
      CostExplorer:
        Type: AWS::CE::AnomalySubscription
        Properties:
          Frequency: DAILY
          MonitorArnList: [!Ref CostExplorerServiceAnomalyMonitor]
          Subscribers: {"Address":"test2@xyz.com","Type":"EMAIL"}
          SubscriptionName: DailyAnomalySubscription
    

    Which is a perfectly valid YAML document.