0.14.11 to 1.1.2 type casting

Hello there,

Just wonder if someone faced such issue before and have some clear explanation.
I can’t find exact cause in documentation, sorry…

Context:
Doing code migration from 0.14.11 directly to 1.1.2.
We have 140ish modules so bypassing 0.15 and 1.0.0.
During planning faced following issue struggling to explain.

Issue:

  1. Have working module on 0.14.11 version resulting into Plan: 0 to add, 14 to change, 0 to destroy
  2. Running same code on 1.1.2 result into Inconsistent conditional result types error:
Error: Inconsistent conditional result types
│
│   on main.tf line 71, in locals:
│   71:             detail = type_key == "sfn" ? {
│   72:               status          = state
│   73:               stateMachineArn = type.event_template.detail.stateMachineArn
│   74:               } : type_key == "codebuild" ? {
│   75:               build-status = state
│   76:               project-name = type.event_template.detail.project-name
│   77:             } : {}
│     ├────────────────
│     │ type.event_template.detail.stateMachineArn is tuple with 1 element
│
│ The true result value has the wrong type: element types must all match for conversion to map.
  1. Flattening is kind odd but don’t judge me on that
  cw_notify_events = flatten([
    for type_key, type in local.notification_types : [
      for state_key, state in type.states : [
        for index in range(0, lookup(var.event_rules_count, type_key, 1)) : {
          type = type_key
          name = join("-", [type_key, state_key, index])
          template = {
            source      = type.event_template.source
            detail-type = type.event_template.detail-type
            detail = type_key == "sfn" ? {
              status          = state
              stateMachineArn = type.event_template.detail.stateMachineArn
              } : type_key == "codebuild" ? {
              build-status = state
              project-name = type.event_template.detail.project-name
            } : {}
          }
        }
      ]
    ]
  ])
  1. indeed notification_types holds stateMachineArn as list with one element.
  notification_types = {
    sfn = {
      states = {
        start   = ["RUNNING"]
        success = ["SUCCEEDED"]
        failure = [
          "ABORTED",
          "FAILED",
          "TIMED_OUT"
        ]
      }
      event_template = {
        source      = ["aws.states"]
        detail-type = ["Step Functions Execution Status Change"]
        detail = {
          status          = []
          stateMachineArn = [local.stub_sfn_arn]
        }
      }
    }
    codebuild = {
      states = {
        start   = ["IN_PROGRESS"]
        success = ["SUCCEEDED"]
        failure = [
          "FAILED",
          "STOPPED"
        ]
      }
      event_template = {
        source      = ["aws.codebuild"]
        detail-type = ["CodeBuild Build State Change"]
        detail = {
          build-status = []
          project-name = [local.stub_codebild_project]
        }
      }
    }
  }

Question
With new version 1.1.2 clear exception, to remediate you need access by index.
But how it works on 0.14.11?
Don’t want to catch some pitfall in production

Thanks,
Serhii

Hi @chell0veck,

I cannot explain why this was working on Terraform v0.14 because there does seem to be a type mismatch in what you shared: each of your conditional arms is returning a different object type and so Terraform is assuming that your intention was to return maps instead (since that’s the only way these expressions could possibly be valid conditional results) but then it also failed to convert to map because there is no single element type for the resulting maps to have: you have a mixture of strings and tuples.

The fact that this was working in Terraform v0.14 suggests some sort of bug that has been fixed in the meantime, but I don’t know of a specific bug that would have allowed this to work before.

I’m not sure what to suggest instead because there are a number of different approaches here depending on what you intend to do with this resulting data structure. What most of the solutions will have in common is to make a separate collection for each distinct type of object so that Terraform will be able to infer valid types for all of the results. If you can show how you are using this local.cw_notify_events data structure to populate then I might be able to make a more specific suggestion.

Thanks @apparentlymart ,

Looks like just need to do it right, would appreciate any practical suggestions coming from seeing the spinets and even more some rules of thums how to decompose and to make it readable :slight_smile:

╰─ grep "local.cw_notify_events" main.tf -A 5 -B2 -n                                                                        ─╯
90-
91-resource "aws_cloudwatch_event_rule" "notify_sender" {
92:  count         = length(local.cw_notify_events)
93:  name          = join("-", [var.name, local.cw_notify_events[count.index].name])
94:  description   = "Notification event for ${local.cw_notify_events[count.index].name}"
95:  event_pattern = jsonencode(local.cw_notify_events[count.index].template)
96-  lifecycle {
97-    ignore_changes = [event_pattern]
98-  }
99-}
100-
--
106-
107-resource "aws_cloudwatch_event_target" "sender" {
108:  count     = length(local.cw_notify_events)
109-  rule      = aws_cloudwatch_event_rule.notify_sender[count.index].name
110-  target_id = "SenderNotifier"
111-  arn       = aws_sns_topic.sender.arn
112-}
113-
--
284-
285-resource "aws_lambda_permission" "sender_email" {
286:  count         = length(local.cw_notify_events)
287-  action        = "lambda:InvokeFunction"
288-  function_name = aws_lambda_function.sender.arn
289-  principal     = "sns.amazonaws.com"
290-  source_arn    = aws_sns_topic.sender.arn
291-}
292-
293-resource "aws_lambda_permission" "sender_email_dqf" {
294:  count         = length(local.cw_notify_events)
295-  action        = "lambda:InvokeFunction"
296-  function_name = aws_lambda_function.dqf_sender.arn
297-  principal     = "sns.amazonaws.com"
298-  source_arn    = aws_sns_topic.sender.arn
299-}

This topic was automatically closed 62 days after the last reply. New replies are no longer allowed.