0
votes

I´m using the aws_cloudformation_stack resource in Terraform to gather IDs about cloudformation security group stacks like so:

data "aws_cloudformation_stack" "vpc-prod-sg" {
  name = "vpc-prod-sg"
}

I define a list in my main.tf file with names that represent these security groups like this:

sg_ingress = ["DevMyAppLinkingSecurityGroup", "DevDBLinkingSecurityGroup"]

In my module I assign the values from the Cloufformation stacks to the names in the list this:

security_groups = [contains(var.sg_ingress, "DevMyAppLinkingSecurityGroup") ? "${data.aws_cloudformation_stack.vpc-prod-sg.outputs["DevMyAppLinkingSecurityGroup"]}" : 0, contains(var.sg_ingress, "DevDBLinkingSecurityGroup") ? "${data.aws_cloudformation_stack.vpc-prod-sg.outputs["DevDBLinkingSecurityGroup"]}" : 0]

However, when I run the terraform plan, the list is populated with the values I want, but it also add an additional entry to the list with value of zero. It looks like this:

+ security_groups  = [
                  + "0",
                  + "sg-05443559898348",
                  + "sg-05435345443545593"

I'm baffled as to where this zero is coming from or how I can deal with it. Has anyone come across anything similar?

1
If the first ternary resolves to false, then the 0th element of your list will be 0. That seems the most likely cause here.Matt Schuchard

1 Answers

1
votes

Let's first add some vertical whitespace to your expression so it's easier to read:

security_groups = [
  contains(var.sg_ingress, "DevMyAppLinkingSecurityGroup") ?
  "${data.aws_cloudformation_stack.vpc-prod-sg.outputs["DevMyAppLinkingSecurityGroup"]}" :
  0,
  contains(var.sg_ingress, "DevDBLinkingSecurityGroup") ?
  "${data.aws_cloudformation_stack.vpc-prod-sg.outputs["DevDBLinkingSecurityGroup"]}" :
  0
]

Both of these element expressions are conditionals that produce a zero if their expression is false, and so it's likely that the zero you are seeing is produced by one of these conditions being false. The zero is then converted to a string because security_groups is defined as being a collection of strings.

Taking a step back and looking at the original problem, it seems like your goal here is to map from some symbolic names (exported by your CloudFormation stack) to the physical security group ids they represent. For this sort of mapping problem, I'd suggest using for expressions, like this:

security_groups = [
  for n in var.sg_ingress : data.aws_cloudformation_stack.vpc-prod-sg.outputs[n]
]

If there are other outputs from this CloudFormation Stack and you want to ensure that var.sg_ingress can only refer to these two, you can add some additional indirection to ensure that:

locals {
  allowed_security_group_outputs = ["DevMyAppLinkingSecurityGroup", "DevDBLinkingSecurityGroup"]
  security_group_ids = {
    for n in local.allowed_security_group_outputs :
    n => data.aws_cloudformation_stack.vpc-prod-sg.outputs[n]
  }
}

...and then:

security_groups = [
  for n in var.sg_ingress : local.security_group_ids[n]
]