Menu
blog.headdesk.me
blog.headdesk.me

Create security groups with terraform using a map

Posted on 2022/05/282022/05/28

If you have 10s of security group rules, it’s not effective to use aws_security_group_rule directly. Here I use a map and feed it to a module.

Sometimes, it is necessary to create security groups with many ingress and egress rules. For me, putting the rules in a map makes it easier to read and modify. First, let’s go through my main.tf. security-groups is a list of maps. Each map contains the security group name, description, and a list of rules. My main.tf tells the module to create 2 security groups.

The rules list of lists requires a rule id. It can be anything unique, so I use r1, r2, etc. Without the id, terraform process the list randomly. Next time when I run terraform, even if there is no change, terraform will try to destroy existing rules and create new ones because the list is ordered differently.

module bast-sg {
  source = "../../modules/compute/security_groups"
  security-groups = [
    {
      name        = "WebAccess"
      description = "Allow web access"
      rules = [
        ["r1", "tcp", "172.20.0.0/16", "80", "80", "ingress", "HTTP"],
        ["r2", "tcp", "172.20.0.0/16", "443", "443", "ingress", "HTTPS"],
      ]
    },
    {
      name        = "SysadminAccess"
      description = "Allow rdp access from sysadmin"
      rules = [
        ["r1","tcp", "192.168.100.101/32", "3389", "3389", "ingress", "RDP"],
        ["r2", "tcp", "192.168.200.0/24", "3389", "3389", "ingress", "RDP"],
        ["r3","-1", "0.0.0.0/0", "0", "0", "egress", "Outbound"]
      ]
    }
  ]
  tags              = local.default-tags
  vpc-id          = "vpc-12345678"
}

Next, let’s go through my module. The aws_security_group resource loops through the list and create security groups with description.

resource "aws_security_group" "sg" {
  count       = length(var.security-groups)
  name        = var.security-groups[count.index].name
  description = var.security-groups[count.index].description
  vpc_id      = var.vpc-id
  tags = merge(
    var.tags,
    {Name = var.security-groups[count.index].name}
  )
}

Then flatten the rules list into a local rules variable. sg_key is generated, but rule_key is set using the rule id. Using the local rules variable, I can create aws_security_group_rule using the for_each loop.

// see https://www.terraform.io/docs/configuration/functions/flatten.html
locals {
  rules = flatten([
  for sg_key, sg in var.security-groups : [
  for rule_key, rule in sg.rules : {
    sg_key      = sg_key
    rule_key    = rule[0]
    sg_name     = sg.name
    protocol    = rule[1]
    cidr_blocks = rule[2]
    from_port   = rule[3]
    to_port     = rule[4]
    type        = rule[5]
    description = rule[6]
  }
  ]
  ])
}

resource "aws_security_group_rule" "rules" {
  for_each = {
    for rule in local.rules : "${rule.sg_key}.${rule.rule_key}" => rule
  }

  security_group_id = matchkeys(aws_security_group.sg.*.id, aws_security_group.sg.*.name, [each.value.sg_name])[0]
  protocol          = each.value.protocol
  source_security_group_id = substr(each.value.cidr_blocks,0,2) == "sg" ? each.value.cidr_blocks : null
  cidr_blocks       = substr(each.value.cidr_blocks,0,2) != "sg" ? [each.value.cidr_blocks] : null
  from_port         = each.value.from_port
  to_port           = each.value.to_port
  type              = each.value.type
  description       = each.value.description
}

It’s a bit difficult to explain the transformation. Essentially, when terraform runs, it will generate rules [0.r1], [0.r2], etc. I also examine the value of cidr_blocks. If it starts with sg, then source_security_group_id is set. Otherwise, it will set the cidr_block parameter.

Give it a try and let me know if you think of a better way. Perhaps read the rules from a csv?

facebookShare on Facebook
TwitterTweet

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Full text search

Recent Posts

  • Create security groups with terraform using a map
  • Dumping AWS Organization tree
  • Free is the most expensive
  • Terraform conditional resource and blocks
  • Upgrade Ubuntu 16.04 to latest release
  • aws (8)
  • coffee (1)
  • headfi (1)
  • linux (7)
  • others (56)
  • security (2)
  • tech (36)
  • wordpress (2)

apache aws awscli azure backup c5 clearlinux cloud coffee coreos deb docker DOCP ec2 EL8 featured gpu ipfs jenkins kernel linux m5 meltdown memory merge MFA php python rdp recovery Redhat RHEL rhel8 roasting rpm Ryzen snapshot spectre tech terraform tmux ubuntu ubuntu upgrade vpn wordpress

©2022 blog.headdesk.me | Powered by SuperbThemes & WordPress