Variable validation for nested list(strings)

I’m using the below variable to create dynamic blocks within a aws_s3_bucket_notification resource:

variable "event" {
  type = list(object({
    type       = string,
    target_arn = string,
    s3_event   = list(string)
  }))

An example of expected inputs are:

  event = [
    { type = "SQS", target_arn = "arn:aws:sqs:us-east-1:123456789000:my_queue", s3_event = ["s3:ObjectCreated:Put"] },
    { type = "SNS", target_arn = "arn:aws:sns:us-east-1:123456789000:my_topic", s3_event = ["s3:ObjectCreated:Post", "s3:ObjectRemoved:Delete"] },
    { type = "SNS", target_arn = "arn:aws:sns:us-east-1:123456789000:my_second_topic", s3_event = ["s3:ObjectCreated:Copy"] },
    { type = "lambda", target_arn = "arn:aws:lambda:us-east-1:123456789000:function:my_lambda", s3_event = ["s3:ObjectRestore:Completed"] },
    { type = "lambda", target_arn = "arn:aws:lambda:us-east-1:123456789000:function:another_lambda", s3_event = ["s3:ReducedRedundancyLostObject"] }
  ]

The dynamic blocks work as expected, but I would like to create a validation block, for this variable, to ensure that the s3_event lists do not overlap because a terraform plan doesn’t catch this issue (a terraform apply will and this error will be generated: Configurations on the same bucket cannot share a common event type).

Along with other google search results, I reviewed Best way to validate if a list(object) variable has unique attribute values in each object and I have been unable to add upon it to make this work → Ensure that there are no duplicate entries in any s3_event list in any object.

I think the below is getting me close, but my tests were not successful.

for e in var.event.s3_event[*] : length(var.event[*].s3_event[*]) ==  [for x in e["s3_event"] :length(distinct(x[*]))]

Any help would be appreciated. Thank you in advance

Hi @c.k,

A collection of values that must be unique and that are not in any particular order seems like a natural fit for using a set type rather than a list type.

Perhaps you could change the type constraint for that attribute from list(string) to set(string) and then Terraform will automatically coalesce any duplicate values as part of converting the caller’s given value to that type.

Thank you for the reply. How would the validation code work for that update? It would be helpful for consumers of my module to know that they have duplicate s3_event entries before running an apply and I can’t figure that piece out.

Hi @c.k,

It’s part of the definition of a set type that there can be only one element of each distinct value. Terraform will silently coalesce them. There is no way to return an error in that case, because in a set there is only one element of each value; writing the same value twice is meaningless.

If you want to make it an error then you’ll need to keep it as a list, but I would suggest that an error here is unnecessary because writing the same event twice does not change the meaning of the event subscription in any way.

Thank you again for your reply. Having s3_event be a set(list) would cause unexpected issues because if the user mistakingly added a duplicate, one of the duplicates would be removed and the plan/apply would go through without notifying the user of the duplicate entry that was removed.

For my own learning, can you help me figure out the validation block for this use case? It has gotten to the point where this is is bugging me so much, I need to solve it :slight_smile: