Create resource if variable matches

I want to create a resource of aws_security_group, only if my_var == “dev”.

I tried it with the setting count variable, but it keeps failing

resource "aws_security_group" "test" {
  count = var.env == "dev" ? 1 : 0
  name        = "${var.env}-mysg"
  description = "${var.env}-mysg"
  vpc_id      = var.vpc_id
  ....
}

Then I’ve tried to reference it by:

security_groups = [compact(aws_security_group.test[0].id)]

I’ve tried various variations, but all ended in errors

What are some other approaches to doing this?

Thanks

Could you please share an error you end up with?

Herei is the error:

Inappropriate value for attribute "security_groups": element 4: string required.

If you goal is to set this security_groups argument only when an instance of aws_security_group.test is created, you could use a splat expression as a concise way to express that:

  security_groups = aws_security_group.test[*].id

The above means to take the id attribute of each of the objects in aws_security_group.test. Because of how you’ve written your count expression, there will either be zero elements or one element in that list.


Incidentally, please note that the security_groups argument for aws_instance is for EC2-Classic only. If you are using VPC (likely, unless this is a very old AWS account) then you will need to set vpc_security_group_ids instead in order to get correct behavior:

  vpc_security_group_ids = aws_security_group.test[*].id

You didn’t mention which resource type you are setting security_groups for, so I just guessed aws_instance here. If you’re setting that argument in some other resource type then this advice may not apply.

This is for resource type aws_lb.

I tried using the splat, but still get aws_security_group.test is empty tuple

Hi @popopanda,

It would be helpful if you could share a complete configuration example and the full error messages you are receiving, including all of the context about where in the configuration the problem is occurring. Otherwise we can only guess what’s going on here. Thanks!

Here is my full configuration:

resource "aws_security_group" "test" {
  count = var.env == "dev" ? 1 : 0
  name        = "${var.env}-mysg"
  description = "${var.env}-mysg"
  vpc_id      = var.vpc_id
  
  ingress {
    cidr_blocks = ["192.168.0.0/16"]
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
  }

  ingress {
    cidr_blocks = ["192.168.0.0/16"]
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
  }
}

resource "aws_lb" "internal_alb" {
  name            = "${var.environment}-int-${var.role}-website"
  internal        = true

  security_groups = [aws_security_group.permit_alb_apache.id, aws_security_group.permit_vpn.id, aws_security_group.permit_backend.id, aws_security_group.permit_k8.id, aws_security_group.test[*].id]

  subnets         = var.subnet_ids

Error message:

  on ../../../../modules/rs-apache/main.tf line 535, in resource "aws_lb" "internal_alb":
 535:   security_groups = [aws_security_group.permit_alb_apache.id, aws_security_group.permit_vpn.id, aws_security_group.permit_backend.id, aws_security_group.permit_k8.id, aws_security_group.test[*].id]
    |----------------
    | aws_security_group.test is empty tuple
    | aws_security_group.permit_alb_apache.id is "sg-<removed>"
    | aws_security_group.permit_backend.id is "sg-<removed>"
    | aws_security_group.permit_k8.id is "sg-<removed>"
    | aws_security_group.permit_vpn.id is "sg-<removed>"

Inappropriate value for attribute "security_groups": element 4: string
required.

All the other SGs work as intended, as they do not rely on any environment differences

Thanks

I have the same question. I want to set a specific variable for S3 but only if the environment is dev.

Thanks for the extra context.

It looks like the problem is that the aws_security_group.test[*].id expression returns a list (or, more accurately, a tuple) of ids of the instances of that resource, and so it’s introducing a nested list into your list, and thus that doesn’t match the expected type (set of strings) for security_groups.

One way to get that working is to employ the flatten function which will remove the intermediate list and produce a flat list of strings, which Terraform can then automatically convert into the flat set of strings the argument is expecting:

  security_groups = flatten([
    aws_security_group.permit_alb_apache.id,
    aws_security_group.permit_vpn.id,
    aws_security_group.permit_backend.id,
    aws_security_group.permit_k8.id,
    aws_security_group.test[*].id,
  ])

Alternatively, you could explicitly concatenate the lists using concat:

  security_groups = concat(
    [aws_security_group.permit_alb_apache.id, aws_security_group.permit_vpn.id, aws_security_group.permit_backend.id, aws_security_group.permit_k8.id],
    aws_security_group.test[*].id,
  )

Both of these should produce the same result, so which to use is a subjective matter of which one seems to more clearly represent your intent to future readers.

1 Like