Home > Back-end >  Dynamic aws security groups and rules creation(terraform)
Dynamic aws security groups and rules creation(terraform)

Time:11-11

  • I’ve created primary_sg, secondary_sg_tcpci, secondary_sg_tcpqa, secondary_sq_tcpprod security groups. primary_sg has three outbound rules to it and each one is associated with secondary_sg securitygroup as destination.
  • For secondary, I created three security groups secondary_sg_tcpci, secondary_sg_tcpqa and secondary_sg_tcpprod. Each of these security group inbound rules are associated again with same primary_sg securitygroup as source in it and referring to some local variables.

main.tf

data "aws_vpc" "default" {
  id = "vpc-1234"
}

data "aws_security_groups" "server_sg"{
  tags = {
    name = "server_sg"
  }
}
locals {
  vpc_id                        = data.aws_vpc.default.id
  server_jumpbox_security_groups = data.aws_security_groups.server_sg.id
}


####### primary_sg###########
resource "aws_security_group" "primary_sg" {
  name        = "primary_security_group"
  description = "Allows outbound rules"
  vpc_id      = local.vpc_id
} 

resource "aws_security_group_rule" "rule_01" {
  type                     = "egress"
  from_port                = 22
  to_port                  = 22
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.secondary_sg_tcpci.id       ## Associating secondary_sg_tcpci as destination security group   ####
  description              = "primary_sg rule01"
  security_group_id        = aws_security_group.primary_sg.id
}

resource "aws_security_group_rule" "rule_02" {
  type                     = "egress"
  from_port                = 22
  to_port                  = 22
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.secondary_sg_tcpqa.id            ## Associating secondary_sg_tcpqa as destination security group   ####
  description              = "primary_sg rule02"
  security_group_id        = aws_security_group.primary_sg.id
}

resource "aws_security_group_rule" "rule_03" {
  type                     = "egress"
  from_port                = 22
  to_port                  = 22
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.secondary_sg_tcpprod.id          ## Associating secondary_sg_tcpprod as destination security group   ####
  description              = "primary_sg rule02"
  security_group_id        = aws_security_group.primary_sg.id
}

########secondary_sg#########
resource "aws_security_group" "secondary_sg_tcpci" {
  name        = "secondary_sg_tcpci"
  description = "RDS SG for TCPCI env"
  vpc_id      = data.aws_vpc.default.id
  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [local.server_jumpbox_security_groups]
    description     = "Server SG"
  }
  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = ["${aws_security_group.primary_sg.id}"]
    description     = "Secondary SG"
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group" "secondary_sg_tcpqa" {
  name        = "secondary_sg_tcpqa"
  description = "RDS SG for TCPQA env"
  vpc_id      = data.aws_vpc.default.id
  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [local.server_jumpbox_security_groups]
    description     = "Server SG"
  }
  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = ["${aws_security_group.primary_sg.id}"]
    description     = "Secondary SG"
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group" "secondary_sg_tcpprod" {
  name        = "secondary_sg_tcpprod"
  description = "RDS SG for TCPQA env"
  vpc_id      = data.aws_vpc.default.id
  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [local.server_jumpbox_security_groups]
    description     = "Server SG"
  }
  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = ["${aws_security_group.primary_sg.id}"]
    description     = "Secondary SG"
  }

  lifecycle {
    create_before_destroy = true
  }
}

I tried using for_each loop. main.tf

 resource "aws_security_group" "secondary_sg_tcpci" {
  for_each =  var.config
  name     = "${each.key}-rds"
  description = "RDS SG for TCPCI env"
  vpc_id      = data.aws_vpc.default.id
  dynamic "ingress" {
    for_each = var.ingress_rules
    iterator = port
    content {
      from_port       = ingress.value.port
      to_port         = ingress.value.port
      protocol        = ingress.value.protocol
      security_groups = ["${aws_security_group.primary_sg.id}"]
    }

  }
 }  
resource "aws_security_group" "primary_sg" {
  
  name        = "primary_security_group"
  description = "Allows outbound rules"
  vpc_id      = local.vpc_id
} 


resource "aws_security_group_rule" "rule_01" {
  type                     = "egress"
  from_port                = 22
  to_port                  = 22
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.secondary_sg_tcpci.id            ## Associating secondary_sg_tcpci as destination security group   ####
  description              = "primary_sg rule01"
  security_group_id        = aws_security_group.primary_sg.id
}

resource "aws_security_group_rule" "rule_02" {
  type                     = "egress"
  from_port                = 22
  to_port                  = 22
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.secondary_sg_tcpqa.id            ## Associating secondary_sg_tcpqa as destination security group   ####
  description              = "primary_sg rule02"
  security_group_id        = aws_security_group.primary_sg.id
}

vars.tf

variable "config" {
  description = "Security groups configuration"
  type = map(object({
    name = string
  }))
  default = {
    "sg-1" = {
      name = "sg-1"
    }
    "sg-2" = {
      name = "sg-2"
    }
  }
}
variable "ingress_rules" {
  type = map(object({
    port            = number
    protocol        = string
    
   
    
  }))
  default = {
    "22" = {
      cidr_block      = ["0.0.0.0/0"]
      port            = 22
      protocol        = "tcp"
      
      
    }
    "22" = {
      cidr_block = ["0.0.0.0/0"]
      port       = 22
      protocol   = "tcp"
    }
  }
}

CodePudding user response:

Since you are creating the SG with for_each, you can only access the SG attributes but using proper keys. There is also a small error in other parts of code as in the dynamic block you are setting the iterator and then later on in the content you are not using it, so you can drop it (as it will default to the dynamic block name which is ingress):

 resource "aws_security_group" "secondary_sg_tcpci" {
  for_each =  var.config
  name     = "${each.key}-rds"
  description = "RDS SG for TCPCI env"
  vpc_id      = data.aws_vpc.default.id
  dynamic "ingress" {
    for_each = var.ingress_rules 
    content {
      from_port       = ingress.value.port
      to_port         = ingress.value.port
      protocol        = ingress.value.protocol
      security_groups = [aws_security_group.primary_sg.id]
    }
  }
 }

resource "aws_security_group" "primary_sg" {
  name        = "primary_security_group"
  description = "Allows outbound rules"
  vpc_id      = local.vpc_id
}

resource "aws_security_group_rule" "rule_01" {
  for_each                 = aws_security_group.secondary_sg_tcpci
  type                     = "egress"
  from_port                = 22
  to_port                  = 22
  protocol                 = "tcp"
  source_security_group_id = each.value.id # <--- this is how you would reference the security IDs for all the SGs created with `for_each`
  description              = "primary_sg rule01"
  security_group_id        = aws_security_group.primary_sg.id
}

This is only for the one SG created with for_each. This technique is called resource chaining with for_each [1]. If you need to repeat the same for other SGs (tcpqa, tcpprod etc.) you would have to adjust the rest of the code.


[1] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each#chaining-for_each-between-resources

  • Related