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?

Loading

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

  • Upgrade RockyLinux 8 to 9
  • Terraform and segregated permissions
  • LVM Compression and Deduplication
  • Edit gpg encrypted file with vim
  • Lelit Elizabeth PL92T Pressure Tuning
  • aws (8)
  • coffee (1)
  • headfi (1)
  • linux (6)
  • others (59)
  • security (2)
  • tech (36)
  • wordpress (2)

Loading

apache apigateway aws awscli azure backup cloud coffee docker ec2 EL8 ElasticBeanstalk enpass espresso featured kernel lelit linux lvm meltdown MFA nat gateway nginx php proliant python rdp Redhat RHEL rpm Ryzen s2s scp serverless site-to-site smartarray snapshot spectre tech terraform transit gateway ubuntu ubuntu upgrade vpn wordpress

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