Create multiple rules in AWS security Group

Hi,

I tried to create an AWS security group with multiple inbound rules, Normally we need to multiple ingresses in the sg for multiple inbound rules. Instead of creating multiple ingress rules separately, I tried to create a list of ingress and so that I can easily reuse the module for different applications.

PFB,

module/sg/sg.tf >>

resource "aws_security_group" "ec2_security_groups" {
  name   = var.name_security_groups
  vpc_id = var.vpc_id
}

module/sg/rules.tf >>

resource "aws_security_group_rule" "ingress_rules" {
  count             = lenght(var.ingress_rules)
  type              = "ingress"
  from_port         = var.ingress_rules[count.index][0]
  to_port           = var.ingress_rules[count.index][1]
  protocol          = var.ingress_rules[count.index][2]
  cidr_blocks       = var.ingress_rules[count.index][3]
  description       = var.ingress_rules[count.index][4]
  security_group_id = aws_security_group.ec2_security_groups.id
}

module/sg/variable.tf >>

variable "vpc_id" {
}
variable "name_security_groups" {
}
variable "ingress_rules" {
    type = list(string)
}

In the application folder,

application/dev/sg.tf >>

module "sg_test" {
  source = "../modules/sg"

  vpc_id                   = "vpc-xxxxxxxxx"
  name_security_groups = "sg_test"
  ingress_rules                     = var.sg_ingress_rules 
}

application/dev/variable.tf >>

variable "sg_ingress_rules" {
    type        = list(string)
    default     = {
        [22, 22, "tcp", "1.2.3.4/32", "test"]
        [23, 23, "tcp", "1.2.3.4/32", "test"]
    }
}

Please help to correct this or if there is any other method please suggest.

Regards,

2 Likes

Hi @jawad846,

Did you see an error when you tried this? If so, please share the full error message so we can focus on the problem Terraform is reporting. Thanks!

```
Error: Missing attribute value

  on test-sgs.tf line 21, in variable "sg_ingress_rules":
  20: 
  21: 
  22: 

Expected an attribute value, introduced by an equals sign ("=").
```

This error seems to be with the default argument inside the variable "sg_ingress_rules" block. You’ve defined this variable as being a list of strings, but the default value is not a list of strings.

Firstly, the default value is using { } instead of [ ], which is the syntax for defining an object or map. Terraform returns a syntax error because it is expecting object or map values to be name = value pairs. We can fix that by changing to use the correct brackets:

variable "sg_ingress_rules" {
    type        = list(string)
    default     = [
        [22, 22, "tcp", "1.2.3.4/32", "test"],
        [23, 23, "tcp", "1.2.3.4/32", "test"],
    ]
}

However, this will still not work because your default value seems to be a list of tuples (sequences of values of different types), which doesn’t match the type constraint list(string). You could fix that by changing the type constraint to match the default value, like this:

variable "sg_ingress_rules" {
    type        = list(tuple([number, number, string, string, string]))
    default     = [
        [22, 22, "tcp", "1.2.3.4/32", "test"],
        [23, 23, "tcp", "1.2.3.4/32", "test"],
    ]
}

However, it’s more common in a Terraform module to use an object type constraint for a situation like this, so it’s clearer for the reader what each of these values will be used for. Here is a version using an object type constraint and an example of how you can use the resulting value in your resource block:

variable "sg_ingress_rules" {
    type = list(object({
      from_port   = number
      to_port     = number
      protocol    = string
      cidr_block  = string
      description = string
    }))
    default     = [
        {
          from_port   = 22
          to_port     = 22
          protocol    = "tcp"
          cidr_block  = "1.2.3.4/32"
          description = "test"
        },
        {
          from_port   = 23
          to_port     = 23
          protocol    = "tcp"
          cidr_block  = "1.2.3.4/32"
          description = "test"
        },
    ]
}

resource "aws_security_group_rule" "ingress_rules" {
  count = length(var.ingress_rules)

  type              = "ingress"
  from_port         = var.ingress_rules[count.index].from_port
  to_port           = var.ingress_rules[count.index].to_port
  protocol          = var.ingress_rules[count.index].protocol
  cidr_blocks       = [var.ingress_rules[count.index].cidr_block]
  description       = var.ingress_rules[count.index].description
  security_group_id = aws_security_group.ec2_security_groups.id
}
2 Likes

Thanks @apparentlymart ,

I really appreciate this, this works for me

variable "sg_ingress_rules" {
    type = list(object({
      from_port   = number
      to_port     = number
      protocol    = string
      cidr_block  = string
      description = string
    }))
    default     = [
        {
          from_port   = 22
          to_port     = 22
          protocol    = "tcp"
          cidr_block  = "1.2.3.4/32"
          description = "test"
        },
        {
          from_port   = 23
          to_port     = 23
          protocol    = "tcp"
          cidr_block  = "1.2.3.4/32"
          description = "test"
        },
    ]
}

resource "aws_security_group_rule" "ingress_rules" {
  count = length(var.ingress_rules)

  type              = "ingress"
  from_port         = var.ingress_rules[count.index].from_port
  to_port           = var.ingress_rules[count.index].to_port
  protocol          = var.ingress_rules[count.index].protocol
  cidr_blocks       = [var.ingress_rules[count.index].cidr_block]
  description       = var.ingress_rules[count.index].description
  security_group_id = aws_security_group.ec2_security_groups.id
}

in variables.tf files>>

variable "ingress_rules" {
    type = list(object({
      from_port   = number
      to_port     = number
      protocol    = string
      cidr_blocks  = string
      description = string
    }))
}

and aws.tfvars files >>

ingress_rules = {
   default     = [
        {
          from_port   = 22
          to_port     = 22
          protocol    = "tcp"
          cidr_blocks  = "1.2.3.4/32"
          description = "test"
        },
   ]
}

I found below error while execute this

Error: Invalid value for input variable

  on aws.tfvars line 10:
  10: ingress_rules = {
  11:    default     = [
  12:         {
  13:           from_port   = 22
  14:           to_port     = 22
  15:           protocol    = "tcp"
  16:           cidr_blocks  = "1.2.3.4/32"
  17:           description = "test"
  18:         },
  19:    ]
  20: }

The given value is not valid for variable “ingress_rules”: list of object
required.

It’s fixed.

ingress_rules = [
        {
          from_port   = 22
          to_port     = 22
          protocol    = "tcp"
          cidr_blocks  = "1.2.3.4/32"
          description = "test"
        },
        {
          from_port   = 23
          to_port     = 23
          protocol    = "tcp"
          cidr_blocks  = "1.2.3.4/32"
          description = "test"
        },
    ]

Thanks all :slight_smile:

1 Like

hi @jawad846 , @iftitutul , @apparentlymart
I have 24 security group and I have to pass rules in each of the sg with many ingress and 1 egress. How can I do with this approach (by creating only one security group rule resource and putting count in it)?