Storing Route53 info in a YAML file and using yamldecode

So I’m trying to follow what this poster is doing. The module I’m using is this.

My code works fine if I only have one type of record in the YAML file, if I try to mix a normal record along with an alias then the code blows up. I noticed in the other discussion, there was mention of using a splat. Correct me if I’m wrong but I believe the module I’m using takes this into consideration, it’s just the for loop I’m using may not satisfy the code within the module.

Error: Inconsistent conditional result types

  on .terraform/modules/records/modules/records/main.tf line 15, in resource "aws_route53_record" "this":
  15:   for_each = var.create && (var.zone_id != null || var.zone_name != null) ? local.recordsets : tomap({})
    |----------------
    | local.recordsets is object with 2 attributes

The true result value has the wrong type: object is required.

Working YAML file

zone_id: Z12345678
records:
- name: route53-alias1.example.com
  type: A
  alias:
    name: abc.com
    zone_id: Z12345678
- name: route53-alias.example.com
  type: A
  alias:
    name: alias.elb.amazon.com
    zone_id: Z12345678

Non-working YAML file

zone_id: Z12345678
records:
- name: route53-alias1.example.com
  type: A
  records:
  - 192.168.0.1
- name: route53-alias.example.com
  type: A
  alias:
    name: alias.elb.amazon.com
    zone_id: Z12345678

main.tf

locals {
  zone_data_raw = yamldecode(file("${path.module}//zone_data.yaml"))
  zone_data = {
      zone_id = local.zone_data_raw.zone_id
      records = [for r in local.zone_data_raw.records : {
        name    = r.name
        type    = r.type
        ttl     = lookup(r, "ttl", null)
        alias   = lookup(r, "alias", {})
        records = lookup(r, "records", null)
      }]
    }
}

module "records" {
  source  = "terraform-aws-modules/route53/aws//modules/records"
  zone_id = local.zone_data_raw.zone_id
  private_zone = true
  records = local.zone_data.records
}

After days of staring at the code and Googling everywhere, I figured out my issue. The module I’m using turns the records into a map with the tomap function. I used code from another post I found on here, but then I realized I didn’t need the for loop within locals. Once I removed it, everything works as expected.

Updated code that works:

locals {
  zone_data_raw = yamldecode(file("${path.module}//zone_data.yaml"))
}

module "records" {
  source  = "terraform-aws-modules/route53/aws//modules/records"
  zone_id = local.zone_data_raw.zone_id
  private_zone = true
  records = local.zone_data_raw.records
}

Hi @luckytaxi,

I’m not sure I followed exactly what was going on in your original problem or in your solution, because the error message here was from a nested module whose source code you didn’t include, but in case it’s helpful I wanted to share a tip for writing conditional for_each in a way that doesn’t run into this sort of type mismatch error:

  for_each = {
    for k, v in local.recordsets : k => v
    if var.create && (var.zone_id != null || var.zone_name != null)
  }

The above uses a for expression with an if clause. Although typically an if clause will include references to the iteration symbols (k and v in this case), that’s not actually required and so we can also use an if clause in this sort of situation where we either want to take all elements of local.recordsets or no elements at all, because the if clause can filter them all out and thus leave an empty mapping.

Doing this with a conditional expression ... ? ... : ... is trickier because you need to make sure that the true and false outcomes both have compatible types, which I think was what your original error message was reporting: local.recordsets has an object type while tomap({}) has a map type, and so Terraform can’t determine a single type for the conditional expression to return. The for expression approach avoids that problem because both outcomes are derived from the same source collection (local.recordsets) and so Terraform can automatically infer the result type in both situations by deriving it from that source value’s type.

1 Like