# Putting csvdecode in security group rule

**URL:** <https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409>\
**Category:** Terraform\
**Created:** [May 16, 2021, 11:29am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409 "2021-05-16T11:29:59Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 16, 2021, 11:29am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/1 "2021-05-16T11:29:59Z")

</div>

I want to call csv function from variable.

Here is my main.tf file of security group

```auto
resource "aws_security_group" "names" {
  count = length(var.ams_prod_sg_list)
  name = var.ams_prod_sg_list[count.index].sg_name
  vpc_id = module.vpc.vpc_id_sg
  tags = {
    Name = var.ams_prod_sg_list[count.index].sg_tags
  }
}

resource "aws_security_group_rule" "sg_rule" {
  count = length(var.ams_prod_sg_list)
  security_group_id = "${aws_security_group.this.*.id}"
  type = var.ams_prod_sg_list[count.index].sg_rules.type
  protocol = var.ams_prod_sg_list[count.index].sg_rules.protocol
  from_port = var.ams_prod_sg_list[count.index].sg_rules.from
  to_port = var.ams_prod_sg_list[count.index].sg_rules.to
  cidr_blocks = [var.ams_prod_sg_list[count.index].sg_rules.cidr_blocks]
  description = var.ams_prod_sg_list[count.index].sg_rules.description

```

Here is the variable.tf file

```auto
locals {
  test = csvdecode(file("${path.module}/csv/test.csv"))
  test1 = csvdecode(file("${path.module}/csv/test1.csv"))
}

variable "ams_prod_sg_list" {
  description = "sg_name rules"
  type = list(map(string))
  default = [
    {
      sg_name = "test"
      sg_rules = local.test
      sg_tags = "sg"
    },
    {
      sg_name = "test1"
      sg_rules = local.test1
      sg_tags = ""
    },
  ]
}

```

When I do `terraform apply` , it shows `Variables may not be used here` which means we cannot use local in variable. And also when I directly put `sg_rules = csvdecode(file("${path.module}/csv/test.csv"))` , it shows `Functions may not be called here`

Here is the test.csv file

```auto
type,protocol,from,to,cidr_blocks,description
ingress,-1,0,0,10.100.0.0/16,test
ingress,tcp,80,80,10.100.0.0/16,

```

Please help me solve this issue.

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [May 17, 2021, 5:25pm UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/2 "2021-05-17T17:25:23Z")

</div>

Hi @dipendra.chaudhary,

Input variables are Terraform’s equivalent of arguments to a function in a general-purpose language, so they get their values only from _outside_ the module, and thus default values can only be constants to use in the case where no such value is provided.

However, you can get a similar effect by handling the “default” as a local value rather than directly as a value of the variable, and just declare the variable as optional with the generic default `null`:

```auto
variable "ams_prod_sg_list" {
  description = "sg_name rules"
  type = list(map(string))
  default = null
}

locals {
  default_ams_prod_sg_list = [
    {
      sg_name = "test"
      sg_rules = local.test
      sg_tags = "sg"
    },
    {
      sg_name = "test1"
      sg_rules = local.test1
      sg_tags = ""
    },
  ]

  ams_prod_sg_list = var.ams_prod_sg_list != null ? var.ams_prod_sg_list : local.default_ams_prod_sg_list
}

```

Elsewhere in your module then you can use `local.ams_prod_sg_list` instead of `var.ams_prod_sg_list` to get either the user’s provided value or the default.

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 20, 2021, 3:56am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/5 "2021-05-20T03:56:38Z")

</div>

hello, @apparentlymart, thanks for your response

Tried your method.

```auto
resource "aws_security_group" "this" {
  count = length(var.ams_prod_sg_list)
  name = var.ams_prod_sg_list[count.index].sg_name
  //description = var.sg_name[count.index]
  vpc_id = module.vpc.vpc_id_sg
  
tags = {
    Name = var.ams_prod_sg_list[count.index].sg_tags
  }
}

resource "aws_security_group_rule" "Synoptek-Edge" {
  count = length(var.ams_prod_sg_list)
  security_group_id = aws_security_group.this.*.id[count.index]
  type = var.ams_prod_sg_list[count.index].sg_rule
  protocol = var.ams_prod_sg_list[count.index].sg_rule
  from_port = var.ams_prod_sg_list[count.index].sg_rule
  to_port = var.ams_prod_sg_list[count.index].sg_rule
  cidr_blocks = [var.ams_prod_sg_list[count.index].sg_rule]
  description = var.ams_prod_sg_list[count.index].sg_rule

```

```auto
locals {
  test = "${csvdecode(file("${path.module}/csv/Synoptek_Edge.csv"))}"
  test1 = "${csvdecode(file("${path.module}/csv/EMRApps-SG.csv"))}"
}

```

```auto
variable "ams_prod_sg_list" {
  description = "sg_name rules"
  type = list(map(string))
  default = null
}

locals {
  default_ams_prod_sg_list = [
    {
      sg_name = "test"
      sg_rule = "${local.test}"
      sg_tags = "sg"
    },
    {
      sg_name = "test1"
      sg_rule = "${local.test1}"
      sg_tags = ""
    },
  ]

  ams_prod_sg_list = var.ams_prod_sg_list != null ? var.ams_prod_sg_list : local.default_ams_prod_sg_list
}

```

csv files

```auto
type,protocol,from,to,cidr_blocks,description
ingress,-1,0,0,10.100.0.0/16,
ingress,tcp,80,80,10.100.0.0/16,

```

```auto
type,protocol,from,to,cidr_blocks,description
ingress,tcp,80,80,10.100.0.0/16,

```

getting the following error

```auto
Error: Inconsistent conditional result types
│ 
│ on sg-variable.tf line 46, in locals:
│ 46: ams_prod_sg_list = var.ams_prod_sg_list != null ? var.ams_prod_sg_list : local.default_ams_prod_sg_list
│ ├────────────────
│ │ local.default_ams_prod_sg_list is tuple with 2 elements
│ │ var.ams_prod_sg_list is a list of map of string, known only after apply
│ 
│ The true and false result expressions must have consistent types. The given
│ expressions are list of map of string and tuple, respectively.

```

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [May 20, 2021, 6:03pm UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/6 "2021-05-20T18:03:34Z")

</div>

Hi @dipendra.chaudhary,

It seems like something here is making Terraform’s automatic type inference not succeed in unifying the two result types. I’m not sure what’s causing that, but when I get errors like this I usually debug them by changing the values to have explicit type conversions rather than relying on the automatic ones, which in this case would mean writing the local value like this:

```auto
  default_ams_prod_sg_list = tolist([
    tomap({
      sg_name = "test"
      sg_rule = local.test
      sg_tags = "sg"
    }),
    tomap({
      sg_name = "test1"
      sg_rule = local.test1
      sg_tags = ""
    }),
  ])

```

The extra [`tolist`](https://www.terraform.io/docs/language/functions/tolist.html) and [`tomap`](https://www.terraform.io/docs/language/functions/tomap.html) calls here will make explicit the type conversions that Terraform would normally do automatically in order to make the conditional expression have a consistent type. Given that the previous configuration failed I expect that this one will _also_ return an error, but it will hopefully be one that makes it clearer where the problem is because Terraform will be able to better understand our intention rather than relying on automatic type inference.

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 21, 2021, 4:22am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/7 "2021-05-21T04:22:46Z")

</div>

hello @apparentlymart

used this

```auto
variable "ams_prod_sg_list" {
  description = "sg_name rules"
  type = list(map(string))
  default = null
}

locals {
  default_ams_prod_sg_list = tolist([
    tomap({
      sg_name = "test"
      sg_rule = "${local.test}"
      sg_tags = "sg"
    }),
    tomap({
      sg_name = "test1"
      sg_rule = "${local.test1}"
      sg_tags = ""
    }),
  ])

  ams_prod_sg_list = var.ams_prod_sg_list != null ? var.ams_prod_sg_list : local.default_ams_prod_sg_list
}

```

error

```auto
Error: Unsupported attribute
│ 
│ on security-group.tf line 91, in resource "aws_security_group_rule" "sg_rule":
│ 91: type = var.ams_prod_sg_list[count.index].sg_rules.type
│ ├────────────────
│ │ count.index is a number, known only after apply
│ │ var.ams_prod_sg_list is a list of map of string, known only after apply
│ 
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│ 
│ on security-group.tf line 92, in resource "aws_security_group_rule" "sg_rule":
│ 92: protocol = var.ams_prod_sg_list[count.index].sg_rules.protocol
│ ├────────────────
│ │ count.index is a number, known only after apply
│ │ var.ams_prod_sg_list is a list of map of string, known only after apply
│ 
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│ 
│ on security-group.tf line 93, in resource "aws_security_group_rule" "sg_rule":
│ 93: from_port = var.ams_prod_sg_list[count.index].sg_rules.from
│ ├────────────────
│ │ count.index is a number, known only after apply
│ │ var.ams_prod_sg_list is a list of map of string, known only after apply
│ 
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│ 
│ on security-group.tf line 94, in resource "aws_security_group_rule" "sg_rule":
│ 94: to_port = var.ams_prod_sg_list[count.index].sg_rules.to
│ ├────────────────
│ │ count.index is a number, known only after apply
│ │ var.ams_prod_sg_list is a list of map of string, known only after apply
│ 
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│ 
│ on security-group.tf line 95, in resource "aws_security_group_rule" "sg_rule":
│ 95: cidr_blocks = [var.ams_prod_sg_list[count.index].sg_rules.cidr_blocks]
│ ├────────────────
│ │ count.index is a number, known only after apply
│ │ var.ams_prod_sg_list is a list of map of string, known only after apply
│ 
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│ 
│ on security-group.tf line 96, in resource "aws_security_group_rule" "sg_rule":
│ 96: description = var.ams_prod_sg_list[count.index].sg_rules.description
│ ├────────────────
│ │ count.index is a number, known only after apply
│ │ var.ams_prod_sg_list is a list of map of string, known only after apply
│ 
│ This value does not have any attributes.
╵
╷
│ Error: Invalid function argument
│ 
│ on sg-variable.tf line 34, in locals:
│ 34: tomap({
│ 35: sg_name = "test"
│ 36: sg_rule = "${local.test}"
│ 37: sg_tags = "sg"
│ 38: }),
│ ├────────────────
│ │ local.test is list of object with 2 elements
│ 
│ Invalid value for "v" parameter: cannot convert object to map of any single
│ type.
╵
╷
│ Error: Invalid function argument
│ 
│ on sg-variable.tf line 39, in locals:
│ 39: tomap({
│ 40: sg_name = "test1"
│ 41: sg_rule = "${local.test1}"
│ 42: sg_tags = ""
│ 43: }),
│ ├────────────────
│ │ local.test1 is list of object with 1 element
│ 
│ Invalid value for "v" parameter: cannot convert object to map of any single
│ type.

```

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [May 21, 2021, 11:27pm UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/8 "2021-05-21T23:27:24Z")

</div>

Ahh, I hadn’t spotted that `local.test` in your first example was the result of `csvdecode`. This is therefore correct to fail, because `local.test` isn’t a valid value for a map of strings: as the error message says, it’s a list of objects.

My initial answer was about how to provide a default value for a variable where the default includes dynamic values from other objects in the module, so I didn’t look closely at the rest of what you showed. Unfortunately it’s hard to guess what your goals are here with everything just being named “test”… can you say a little more about what your intended result is here, so that I can perhaps suggest a different way to achieve it that would be valid in the Terraform language?

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 23, 2021, 4:06pm UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/9 "2021-05-23T16:06:47Z")

</div>

@apparentlymart local.test value is there sorry I forgot to mention

```auto
locals {
  test = "${csvdecode(file("${path.module}/csv/Synoptek_Edge.csv"))}"
  test1 = "${csvdecode(file("${path.module}/csv/EMRApps-SG.csv"))}"
}

variable "ams_prod_sg_list" {
  description = "sg_name rules"
  type = list(map(string))
  default = null
}

locals {
  default_ams_prod_sg_list = tolist([
    tomap({
      sg_name = "test"
      sg_rule = "${local.test}"
      sg_tags = "sg"
    }),
    tomap({
      sg_name = "test1"
      sg_rule = "${local.test1}"
      sg_tags = ""
    }),
  ])

  ams_prod_sg_list = var.ams_prod_sg_list != null ? var.ams_prod_sg_list : local.default_ams_prod_sg_list
}

```

I’ll explain What I want to achieve

I have 1 default security group and 24 custom security group that I have made by terraform code. Each security group have several rules. I have used the csv file to put the rules. I have used count in security group resource. But for security group rule, I have defined each rule for each security group and it happened to be 24. And also for the source security group id in security group rule, when I put aws\_security\_group..id, it doesnot get the id and says to put exact the id of the security group i.e.“sg-xxxx”. So I have put it in the security group rule. And also for the IPv6, since in the csv file it is already cidr\_block ipv4, I cannot put there IPv6. The code is being so long.

**security\_group.tf**

```auto
resource "aws_default_security_group" "this" {
  count = var.create_vpc && var.manage_default_security_group ? 1 : 0

  vpc_id = module.vpc.vpc_id_sg
}

resource "aws_security_group_rule" "default-sg-ingress" {
  source_security_group_id = aws_default_security_group.this[0].id
  security_group_id = aws_default_security_group.this[0].id
  type = "ingress"
  protocol = -1
  from_port = 0
  to_port = 0
}

resource "aws_security_group_rule" "default-sg-egress" {
  security_group_id = aws_default_security_group.this[0].id
  cidr_blocks = ["0.0.0.0/0"]
  type = "egress"
  protocol = -1
  from_port = 0
  to_port = 0
}

resource "aws_security_group" "this" {
  count = var.sg_count
  name = element(local.sg_name, count.index)
  vpc_id = module.vpc.vpc_id_sg

  tags = {
    Name = "${element(local.sg_name_tag, count.index)}"
  }
}

resource "aws_security_group_rule" "Synoptek-Edge" {
  count = length(local.security_group_rules)
  security_group_id = aws_security_group.this[0].id
  type = local.security_group_rules[count.index].type
  protocol = local.security_group_rules[count.index].protocol
  from_port = local.security_group_rules[count.index].from
  to_port = local.security_group_rules[count.index].to
  cidr_blocks = [local.security_group_rules[count.index].cidr_blocks]
  description = local.security_group_rules[count.index].description
}

resource "aws_security_group_rule" "EMRApps-SG" {
  count = length(local.security_group_rules_1)
  security_group_id = aws_security_group.this[1].id
  type = local.security_group_rules_1[count.index].type
  protocol = local.security_group_rules_1[count.index].protocol
  from_port = local.security_group_rules_1[count.index].from
  to_port = local.security_group_rules_1[count.index].to
  cidr_blocks = [local.security_group_rules_1[count.index].cidr_blocks]
  description = local.security_group_rules_1[count.index].description
}

resource "aws_security_group_rule" "AD-SG" {
  count = length(local.security_group_rules_2)
  security_group_id = aws_security_group.this[2].id
  type = local.security_group_rules_2[count.index].type
  protocol = local.security_group_rules_2[count.index].protocol
  from_port = local.security_group_rules_2[count.index].from
  to_port = local.security_group_rules_2[count.index].to
  cidr_blocks = [local.security_group_rules_2[count.index].cidr_blocks]
  description = local.security_group_rules_2[count.index].description
}

resource "aws_security_group_rule" "AccCntrl-SG" {
  count = length(local.security_group_rules_3)
  security_group_id = aws_security_group.this[3].id
  type = local.security_group_rules_3[count.index].type
  protocol = local.security_group_rules_3[count.index].protocol
  from_port = local.security_group_rules_3[count.index].from
  to_port = local.security_group_rules_3[count.index].to
  cidr_blocks = [local.security_group_rules_3[count.index].cidr_blocks]
  description = local.security_group_rules_3[count.index].description
}

resource "aws_security_group_rule" "EMR-Database" {
  count = length(local.security_group_rules_4)
  security_group_id = aws_security_group.this[4].id
  type = local.security_group_rules_4[count.index].type
  protocol = local.security_group_rules_4[count.index].protocol
  from_port = local.security_group_rules_4[count.index].from
  to_port = local.security_group_rules_4[count.index].to
  cidr_blocks = [local.security_group_rules_4[count.index].cidr_blocks]
  description = local.security_group_rules_4[count.index].description
}

resource "aws_security_group_rule" "Test-eCW-Access-SG" {
  count = length(local.security_group_rules_5)
  security_group_id = aws_security_group.this[5].id
  type = local.security_group_rules_5[count.index].type
  protocol = local.security_group_rules_5[count.index].protocol
  from_port = local.security_group_rules_5[count.index].from
  to_port = local.security_group_rules_5[count.index].to
  cidr_blocks = [local.security_group_rules_5[count.index].cidr_blocks]
  description = local.security_group_rules_5[count.index].description
}

resource "aws_security_group_rule" "Datica-Interface-SG" {
  count = length(local.security_group_rules_6)
  security_group_id = aws_security_group.this[6].id
  type = local.security_group_rules_6[count.index].type
  protocol = local.security_group_rules_6[count.index].protocol
  from_port = local.security_group_rules_6[count.index].from
  to_port = local.security_group_rules_6[count.index].to
  cidr_blocks = [local.security_group_rules_6[count.index].cidr_blocks]
  description = local.security_group_rules_6[count.index].description
}

resource "aws_security_group_rule" "Tolmar-Interface-SG" {
  count = length(local.security_group_rules_7)
  security_group_id = aws_security_group.this[7].id
  type = local.security_group_rules_7[count.index].type
  protocol = local.security_group_rules_7[count.index].protocol
  from_port = local.security_group_rules_7[count.index].from
  to_port = local.security_group_rules_7[count.index].to
  cidr_blocks = [local.security_group_rules_7[count.index].cidr_blocks]
  description = local.security_group_rules_7[count.index].description
}

resource "aws_security_group_rule" "eBO-http-SG" {
  count = length(local.security_group_rules_8)
  security_group_id = aws_security_group.this[8].id
  type = local.security_group_rules_8[count.index].type
  protocol = local.security_group_rules_8[count.index].protocol
  from_port = local.security_group_rules_8[count.index].from
  to_port = local.security_group_rules_8[count.index].to
  cidr_blocks = [local.security_group_rules_8[count.index].cidr_blocks]
  description = local.security_group_rules_8[count.index].description
}

resource "aws_security_group_rule" "DMZ-SFTP" {
  count = length(local.security_group_rules_9)
  security_group_id = aws_security_group.this[9].id
  type = local.security_group_rules_9[count.index].type
  protocol = local.security_group_rules_9[count.index].protocol
  from_port = local.security_group_rules_9[count.index].from
  to_port = local.security_group_rules_9[count.index].to
  cidr_blocks = [local.security_group_rules_9[count.index].cidr_blocks]
  description = local.security_group_rules_9[count.index].description
}

resource "aws_security_group_rule" "Internal-web_SG" {
  count = length(local.security_group_rules_10)
  security_group_id = aws_security_group.this[10].id
  type = local.security_group_rules_10[count.index].type
  protocol = local.security_group_rules_10[count.index].protocol
  from_port = local.security_group_rules_10[count.index].from
  to_port = local.security_group_rules_10[count.index].to
  cidr_blocks = [local.security_group_rules_10[count.index].cidr_blocks]
  description = local.security_group_rules_10[count.index].description
}

resource "aws_security_group_rule" "sg-app-server-elb" {
  count = length(local.security_group_rules_11)
  security_group_id = aws_security_group.this[11].id
  type = local.security_group_rules_11[count.index].type
  protocol = local.security_group_rules_11[count.index].protocol
  from_port = local.security_group_rules_11[count.index].from
  to_port = local.security_group_rules_11[count.index].to
  cidr_blocks = [local.security_group_rules_11[count.index].cidr_blocks]
  description = local.security_group_rules_11[count.index].description
}

resource "aws_security_group_rule" "ams-temp-sg" {
  count = length(local.security_group_rules_12)
  security_group_id = aws_security_group.this[12].id
  type = local.security_group_rules_12[count.index].type
  protocol = local.security_group_rules_12[count.index].protocol
  from_port = local.security_group_rules_12[count.index].from
  to_port = local.security_group_rules_12[count.index].to
  cidr_blocks = [local.security_group_rules_12[count.index].cidr_blocks]
  description = local.security_group_rules_12[count.index].description
}

resource "aws_security_group_rule" "sg-app-server-elb-instance" {
  count = length(local.security_group_rules_13)
  security_group_id = aws_security_group.this[13].id
  type = local.security_group_rules_13[count.index].type
  protocol = local.security_group_rules_13[count.index].protocol
  from_port = local.security_group_rules_13[count.index].from
  to_port = local.security_group_rules_13[count.index].to
  cidr_blocks = [local.security_group_rules_13[count.index].cidr_blocks]
  description = local.security_group_rules_13[count.index].description
}

resource "aws_security_group_rule" "sh-enable-ssh-access" {
  count = length(local.security_group_rules_14)
  security_group_id = aws_security_group.this[14].id
  type = local.security_group_rules_14[count.index].type
  protocol = local.security_group_rules_14[count.index].protocol
  from_port = local.security_group_rules_14[count.index].from
  to_port = local.security_group_rules_14[count.index].to
  cidr_blocks = [local.security_group_rules_14[count.index].cidr_blocks]
  description = local.security_group_rules_14[count.index].description
}

resource "aws_security_group_rule" "sg-reverse-proxy-dmz" {
  count = length(local.security_group_rules_15)
  security_group_id = aws_security_group.this[15].id
  type = local.security_group_rules_15[count.index].type
  protocol = local.security_group_rules_15[count.index].protocol
  from_port = local.security_group_rules_15[count.index].from
  to_port = local.security_group_rules_15[count.index].to
  cidr_blocks = [local.security_group_rules_15[count.index].cidr_blocks]
  description = local.security_group_rules_15[count.index].description
}

resource "aws_security_group_rule" "sg-reverse-proxy-dmz-instances" {
  count = length(local.security_group_rules_16)
  security_group_id = aws_security_group.this[16].id
  type = local.security_group_rules_16[count.index].type
  protocol = local.security_group_rules_16[count.index].protocol
  from_port = local.security_group_rules_16[count.index].from
  to_port = local.security_group_rules_16[count.index].to
  cidr_blocks = [local.security_group_rules_16[count.index].cidr_blocks]
  description = local.security_group_rules_16[count.index].description
}

resource "aws_security_group_rule" "sg-ssh-access-from-management-vpc" {
  count = length(local.security_group_rules_17)
  security_group_id = aws_security_group.this[17].id
  type = local.security_group_rules_17[count.index].type
  protocol = local.security_group_rules_17[count.index].protocol
  from_port = local.security_group_rules_17[count.index].from
  to_port = local.security_group_rules_17[count.index].to
  cidr_blocks = [local.security_group_rules_17[count.index].cidr_blocks]
  description = local.security_group_rules_17[count.index].description
}

resource "aws_security_group_rule" "sg-web-access-ports-from-production" {
  count = length(local.security_group_rules_18)
  security_group_id = aws_security_group.this[18].id
  type = local.security_group_rules_18[count.index].type
  protocol = local.security_group_rules_18[count.index].protocol
  from_port = local.security_group_rules_18[count.index].from
  to_port = local.security_group_rules_18[count.index].to
  cidr_blocks = [local.security_group_rules_18[count.index].cidr_blocks]
  description = local.security_group_rules_18[count.index].description
}

resource "aws_security_group_rule" "AMS-EphemAC-SG" {
  count = length(local.security_group_rules_19)
  security_group_id = aws_security_group.this[19].id
  type = local.security_group_rules_19[count.index].type
  protocol = local.security_group_rules_19[count.index].protocol
  from_port = local.security_group_rules_19[count.index].from
  to_port = local.security_group_rules_19[count.index].to
  cidr_blocks = [local.security_group_rules_19[count.index].cidr_blocks]
  description = local.security_group_rules_19[count.index].description
}

resource "aws_security_group_rule" "sg-database-access" {
  count = length(local.security_group_rules_20)
  security_group_id = aws_security_group.this[20].id
  type = local.security_group_rules_20[count.index].type
  protocol = local.security_group_rules_20[count.index].protocol
  from_port = local.security_group_rules_20[count.index].from
  to_port = local.security_group_rules_20[count.index].to
  cidr_blocks = [local.security_group_rules_20[count.index].cidr_blocks]
  description = local.security_group_rules_20[count.index].description
}

resource "aws_security_group_rule" "d-906701493f_controllers" {
  count = length(local.security_group_rules_21)
  security_group_id = aws_security_group.this[21].id
  type = local.security_group_rules_21[count.index].type
  protocol = local.security_group_rules_21[count.index].protocol
  from_port = local.security_group_rules_21[count.index].from
  to_port = local.security_group_rules_21[count.index].to
  cidr_blocks = [local.security_group_rules_21[count.index].cidr_blocks]
  description = local.security_group_rules_21[count.index].description
}

resource "aws_security_group_rule" "launch-wizard-5" {
  count = length(local.security_group_rules_22)
  security_group_id = aws_security_group.this[22].id
  type = local.security_group_rules_22[count.index].type
  protocol = local.security_group_rules_22[count.index].protocol
  from_port = local.security_group_rules_22[count.index].from
  to_port = local.security_group_rules_22[count.index].to
  cidr_blocks = [local.security_group_rules_22[count.index].cidr_blocks]
  description = local.security_group_rules_22[count.index].description
}

resource "aws_security_group_rule" "EMRApps-SG_rule_source" {

  type = "ingress"
  from_port = 0
  to_port = 0
  protocol = -1
  description = "Synoptek Edge"
  source_security_group_id = aws_security_group.this[0].id
  security_group_id = aws_security_group.this[1].id
}

resource "aws_security_group_rule" "EMRApps-SG_rule_source_1" {

  type = "ingress"
  from_port = "8080"
  to_port = "8080"
  protocol = "tcp"
  source_security_group_id = aws_security_group.this[11].id
  security_group_id = aws_security_group.this[1].id
}

resource "aws_security_group_rule" "EMR-Database_rule_source" {

  type = "ingress"
  from_port = 0
  to_port = 0
  protocol = -1
  description = "Synoptek Edge"
  source_security_group_id = aws_security_group.this[0].id
  security_group_id = aws_security_group.this[4].id
}

resource "aws_security_group_rule" "Internal-web_SG_rule_source" {

  type = "ingress"
  from_port = 0
  to_port = 0
  protocol = -1
  description = "Synoptek Edge"
  source_security_group_id = aws_security_group.this[0].id
  security_group_id = aws_security_group.this[10].id
}

resource "aws_security_group_rule" "Internal-web_SG_rule_source_1" {

  type = "ingress"
  from_port = "8080"
  to_port = "8080"
  protocol = "tcp"
  source_security_group_id = aws_security_group.this[11].id
  security_group_id = aws_security_group.this[10].id
}

resource "aws_security_group_rule" "EMR-Database_ip_v6" {

  type = "ingress"
  from_port = "80"
  to_port = "80"
  protocol = "tcp"
  ipv6_cidr_blocks = ["::/0"]
  security_group_id = aws_security_group.this[4].id
}

resource "aws_security_group_rule" "sg-app-server-elb_ip_v6" {

  type = "ingress"
  from_port = "80"
  to_port = "80"
  protocol = "tcp"
  ipv6_cidr_blocks = ["::/0"]
  security_group_id = aws_security_group.this[11].id
}

resource "aws_security_group_rule" "sg-app-server-elb_rule_source" {

  type = "egress"
  from_port = "8080"
  to_port = "8080"
  protocol = "tcp"
  source_security_group_id = aws_security_group.this[1].id
  security_group_id = aws_security_group.this[11].id
}

resource "aws_security_group_rule" "sg-app-server-elb-instance_rule_source" {

  type = "ingress"
  from_port = 0
  to_port = 0
  protocol = -1
  description = "Synoptek Edge"
  source_security_group_id = aws_security_group.this[0].id
  security_group_id = aws_security_group.this[13].id
}

resource "aws_security_group_rule" "sg-reverse-proxy-dmz_rule_source" {

  type = "ingress"
  from_port = 0
  to_port = 0
  protocol = -1
  description = "Synoptek Edge"
  source_security_group_id = aws_security_group.this[0].id
  security_group_id = aws_security_group.this[15].id
}

resource "aws_security_group_rule" "sg-database-access_rule_source_1" {

  type = "ingress"
  from_port = 0
  to_port = 0
  protocol = -1
  description = "Synoptek Edge"
  source_security_group_id = aws_security_group.this[0].id
  security_group_id = aws_security_group.this[20].id
}

resource "aws_security_group_rule" "sg-database-access_rule_source_2" {

  type = "ingress"
  from_port = "3306"
  to_port = "3306"
  protocol = "tcp"
  source_security_group_id = aws_security_group.this[13].id
  security_group_id = aws_security_group.this[20].id
}

resource "aws_security_group_rule" "DMZ-SFTP_rule_source" {

  type = "ingress"
  from_port = 0
  to_port = 0
  protocol = -1
  description = "Synoptek Edge"
  source_security_group_id = aws_security_group.this[0].id
  security_group_id = aws_security_group.this[9].id
}

resource "aws_security_group_rule" "d-906701493f_controllers_rule_source" {

  type = "ingress"
  from_port = 0
  to_port = 0
  protocol = -1
  source_security_group_id = aws_security_group.this[21].id
  security_group_id = aws_security_group.this[21].id
}

```

locals.tf

```auto
sg_name = ["Synoptek Edge", "AMS-EMRApps-SG", ......so on]

sg_name_tag = ["Synoptek Edge", "AMS-EMRApps-SG", ....so on]

security_group_rules = csvdecode(file("${path.module}/csv/Synoptek_Edge.csv"))

  security_group_rules_1 = csvdecode(file("${path.module}/csv/EMRApps-SG.csv"))

  security_group_rules_2 = csvdecode(file("${path.module}/csv/AD-SG.csv"))

  security_group_rules_3 = csvdecode(file("${path.module}/csv/AccCntrl-SG.csv"))

  security_group_rules_4 = csvdecode(file("${path.module}/csv/EMR-Database.csv"))

  security_group_rules_5 = csvdecode(file("${path.module}/csv/Test-eCW-Access-SG.csv"))

  security_group_rules_6 = csvdecode(file("${path.module}/csv/Datica-Interface-SG.csv"))

  security_group_rules_7 = csvdecode(file("${path.module}/csv/Tolmar-Interface-SG.csv"))

  security_group_rules_8 = csvdecode(file("${path.module}/csv/eBO-http-SG.csv"))

  security_group_rules_9 = csvdecode(file("${path.module}/csv/DMZ-SFTP.csv"))

  security_group_rules_10 = csvdecode(file("${path.module}/csv/Internal-web_SG.csv"))

  security_group_rules_11 = csvdecode(file("${path.module}/csv/sg-app-server-elb.csv"))

  security_group_rules_12 = csvdecode(file("${path.module}/csv/ams-temp-sg.csv"))

  security_group_rules_13 = csvdecode(file("${path.module}/csv/sg-app-server-elb-instance.csv"))

  security_group_rules_14 = csvdecode(file("${path.module}/csv/sg-enable-ssh-access.csv"))

  security_group_rules_15 = csvdecode(file("${path.module}/csv/sg-reserve-proxy-dmz.csv"))

  security_group_rules_16 = csvdecode(file("${path.module}/csv/sg-reserve-proxy-dmz-instances.csv"))

  security_group_rules_17 = csvdecode(file("${path.module}/csv/sg-ssh-access-from-management-vpc.csv"))

  security_group_rules_18 = csvdecode(file("${path.module}/csv/sg-web-access-ports-from-production.csv"))

  security_group_rules_19 = csvdecode(file("${path.module}/csv/AMS-EphemAC-SG.csv"))

  security_group_rules_20 = csvdecode(file("${path.module}/csv/sg-database-access.csv"))

  security_group_rules_21 = csvdecode(file("${path.module}/csv/d-906701493f_controllers.csv"))

  security_group_rules_22 = csvdecode(file("${path.module}/csv/launch-wizard-5.csv"))
}

```

I have list of csv files. One of them is mentioned below. The others are same as it

**Synoptek-Edge.csv**

```auto
type,protocol,from,to,cidr_blocks,description
ingress,-1,0,0,10.100.0.0/16,
ingress,tcp,3389,3389,10.100.10.81/32,AWS
ingress,tcp,3389,3389,104.32.122.85/32,Synoptek
ingress,tcp,3389,3389,204.227.0.127/32,Synoptek
ingress,tcp,3389,3389,204.227.0.254/32,Synoptek
ingress,icmp,-1,-1,10.100.0.0/16,
egress,-1,0,0,0.0.0.0/0,

```

Now when I do terraform apply, it works fine. But the code is too long and its unmanaged.

What I want to achieve that is minimize the lines of code. In the code mentioned above, there is 24 aws\_security\_group\_rule resources. But I want to make it only one and put it in count and make the variable look as mentioned below:

```auto
Synoptek_Edge_Sg = csvdecode(file("${path.module}/csv/Synoptek_Edge.csv"))
AnotherSG = csvdecode(file("${path.module}/csv/antoherSG.csv"))
antoherSG2 = csvdecode(file("${path.module}/csv/antoherSG2.csv"))

variable "Ams_Prod_Sg_List" {
  description = "sg_name rules"
  type = list(map(string))
  default = [
    {
      sg_name = "Synoptek Edge"
      sg_rules = Synoptek_Edge_Sg
      sg_tags = [""]
    },
    {
      sg_name = "AnotherSG"
      sg_rules = AnotherSG
      sg_tags = [""]
    },
    {
      sg_name = "AnotherSG2"
      antoherSG2 = antoherSG2
      sg_tags = [""]
    },
  ]
}

```

How can I achieve this? If this is not possible, please let me know another approach to do so but I want to minimize the lines of code and make it as module to use in future.

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [May 24, 2021, 6:23pm UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/10 "2021-05-24T18:23:30Z")

</div>

Thanks for the extra details, @dipendra.chaudhary.

You mentioned at the end of your comment that your goal is to make this a reusable module for declaring security groups and their rules, so I want to start from that requirement and work backwards to discuss ways to achieve that. A Terraform module typically takes the data it will use directly as input variables, rather than indirectly via files on disk, and so I would typically start by defining what data this module will need as input variables:

```auto
variable "vpc_id" {
  type = string
}

variable "default_security_group" {
  type = object({
    manage_ingress = bool
    manage_egress = bool
  })

  # This variable is optional. Setting it
  # means that the caller wants the module
  # to manage the VPC's default security
  # group.
  default = null
}

variable "security_groups" {
  type = map(object({
    name = string
    tags = map(string)

    # AWS allows both rules that are matched by
    # source network address and rules matched
    # by source security group. They each need
    # different handling by our module so we'll
    # use two different attributes.
    cidr_rules = map(object({
      type = string
      protocol = string
      from_port = number
      to_port = number
      cidr_blocks = set(string)
      description = string
    }))
    source_group_rules = map(object({
      type = string
      protocol = string
      from_port = number
      to_port = number
      source_group_key = string
      description = string
    }))
  }))
  default = {}
}

```

Above I’ve defined three variables, the third of which is the main one that will take all of the data related to the security groups and their rules. It’s a two-level hierarchical structure because from your example it seemed like you wanted to have different rules for each group.

Let’s first write out the resource blocks for the default security group, since that isn’t changed much from what you had in your example. I made some changes here you didn’t actually call for just because I thought it useful to show the pattern of deciding whether to activate a particular feature based on whether a variable is `null`, which is pretty common in shared modules where the optional feature also needs some customizable parameters:

```auto
resource "aws_default_security_group" "this" {
  # The [*] operator converts a non-list value
  # that might be null into a list of either
  # zero or one elements.
  count = length(var.default_security_group[*])

  vpc_id = var.vpc_id
}

resource "aws_security_group_rule" "default_ingress" {
  # We'll filter out a non-null object if it
  # doesn't also specify to manage ingress.
  count = length([
    for o in var.default_security_group[*] : o
    if o.manage_ingress
  ])

  security_group_id = aws_default_security_group.this[count.index]
  source_security_group_id = aws_default_security_group.this[count.index]

  type = "ingress"
  protocol = -1
  from_port = 0
  to_port = 0
}

resource "aws_security_group_rule" "default_egress" {
  count = length([
    for o in var.default_security_group[*] : o
    if o.manage_egress
  ])

  security_group_id = aws_default_security_group.this[count.index]
  cidr_blocks = ["0.0.0.0/0"]

  type = "egress"
  protocol = -1
  from_port = 0
  to_port = 0
}

```

With those defined we can now move on to the part which I think you are most interested in: defining the additional security groups and their associated rules. I designed the input variables above to use maps rather than lists because that allows us to use [resource `for_each`](https://www.terraform.io/docs/language/meta-arguments/for_each.html) to define the multiple objects in a way that will allow you to add and remove security groups and rules in future without disturbing other groups/rules. (There’s some more detail on this decision in [When to use `for_each` instead of `count`](https://www.terraform.io/docs/language/meta-arguments/count.html#when-to-use-for_each-instead-of-count).)

To meet the requirements for `for_each` we need a map that has one element for each instance we want to declare. `var.security_groups` is already of a suitable shape for the security groups themselves, but in order to declare the security group rules systematically with `for_each` we will need a collection with one element per rule _across all security groups_, and so we can use [the `flatten` function](https://www.terraform.io/docs/language/functions/flatten.html) in a local value to derive such a structure:

```auto
locals {
  cidr_rules = flatten([
    for group_key, group in var.security_groups : [
      for rule_key, rule in group.cidr_rules : {
        rule_key = rule_key
        group_key = group_key
        rule = rule
        group = group
      }
    ]
  ])
  source_group_rules = flatten([
    for group_key, group in var.security_groups : [
      for rule_key, rule in group.source_group_rules : {
        rule_key = rule_key
        group_key = group_key
        rule = rule
        group = group
      }
    ]
  ])
}

```

Each of these is a list of objects with `rule`, `rule_key`, `group`, and `group_key` attributes that describe all of the rules across all of the groups. We need to include the keys in there as well as the groups because the other requirement for `for_each` is that we can construct a unique key for every element, which in this case will be a combination of the group and rule keys, as we’ll see in a moment.

Now we can declare all of the groups and all of the rules as just three resources with `for_each` set:

```auto
resource "aws_security_group" "additional" {
  for_each = var.security_groups

  vpc_id = var.vpc_id
  name = each.value.count
  tags = each.value.tags
}

resource "aws_security_group_rule" "additional_cidr" {
  for_each = {
    for o in local.cidr_rules : "${o.group_key}:${o.rule_key}" => o
  }

  security_group_id = aws_security_group.additional[each.value.group_key]
  type = each.value.rule.type
  protocol = each.value.rule.protocol
  from_port = each.value.from_port
  to_port = each.value.to_port
  cidr_blocks = each.value.cidr_blocks
  description = each.value.description
}

resource "aws_security_group_rule" "additional_source_group" {
  for_each = {
    for o in local.source_group_rules : "${o.group_key}:${o.rule_key}" => o
  }

  security_group_id = aws_security_group.additional[each.value.group_key]
  source_security_group_id = aws_security_group.additional[each.value.rule.source_group_key]

  type = each.value.rule.type
  protocol = each.value.rule.protocol
  from_port = each.value.from_port
  to_port = each.value.to_port
  description = each.value.description
}

```

So with all of this together what we’ve achieved is a module which can take a hierarchical description of various security groups and their rules, where some rules possibly refer to other security groups, and produce the flat set of security group and rule declarations to produce that result with all of the needed interconnections.

This module _doesn’t_ manage a VPC and it doesn’t do anything with CSV files. It _could_ do those things, but I suggest keeping each module focused on a particular goal and then connecting multiple modules together in order to construct a larger system. In this case, perhaps you’d have a separate module that declares a VPC, but for the sake of keeping things simple to wrap this up I’m going to just assume that the root module will directly declare a VPC and pass it in to the `vpc_id` variable I declared above.

One way to use this module now would be to eschew the CSV files altogether and to just write the definitions out directly inside the module block:

```auto
resource "aws_vpc" "example" {
  # ...
}

module "security" {
  source = "./modules/vpc-security"

  vpc_id = aws_vpc.example.id
  default_security_group = {
    manage_ingress = true
    manage_egress = true
  }
  security_groups = {
    "Synoptek-Edge" = {
      name = "Synoptek Edge"
      tags = { Name = "Synoptek Edge" }
      cidr_rules = {
        "internal" = {
          type = "ingress"
          protocol = -1
          from_port = 0
          to_port = 0
          cidr_blocks = ["10.100.0.0/16"]
          description = ""
        },
        "AWS" = {
          type = "ingress"
          protocol = "tcp"
          from_port = 3389
          to_port = 3389
          cidr_blocks = ["10.100.10.81/32"]
          description = "AWS"
        },
        # (and so on)
      }
      source_group_rules = {}
    }
  }
}

```

However, you can still use your CSV strategy with this module, by having the calling module dynamically construct the `security_groups` data structure based on the CSV files it finds on disk:

```auto
locals {
  rule_files = fileset("${path.module}/security-groups", "*.csv")
  rule_file_names = {
    # Trim the .csv suffix
    for fn in local.rule_files : fn => substr(fn, 0, length(fn) - 4)
  }
  rule_file_contents = {
    for fn in local.rule_files : fn => csvdecode(file("${path.module}/security-groups/${fn}"))
  }
  rule_file_cidr = {
    for fn, raws in local.rule_file_contents : fn => [
      for raw in raws : raw
      if try(raw.cidr_blocks, "") != ""
    ]
  }
  rule_file_srcgrp = {
    for fn, raws in local.rule_file_contents : fn => [
      for raw in raws : raw
      if try(raw.source_group, "") != ""
    ]
  }
  groups = {
    for fn, raw in local.rule_file_contents : local.rule_file_names[fn] => {
      name = local.rule_file_names[fn]
      tags = { Name = local.rule_file_names[fn] }
      cidr_rules = {
        for raw in local.rule_file_cidr[fn] : raw.name => {
          type = raw.type
          protocol = raw.protocol
          from_port = tonumber(raw.from)
          to_port = tonumber(raw.to)
          cidr_blocks = split(" ", raw.cidr_blocks)
          description = try(raw.description, raw.name)
        }
      }
      source_group_rules = {
        for raw in local.rule_file_srcgrp[fn] : raw.name => {
          type = raw.type
          protocol = raw.protocol
          from_port = tonumber(raw.from)
          to_port = tonumber(raw.to)
          source_group_key = raw.source_group
          description = try(raw.description, raw.name)
        }
      }
    }
  }
}

```

The above will search a subdirectory of the module directory for files named with a `.csv` suffix and create a security group object for each one, with a nested rule for each row in the CSV file that has either `cidr_blocks` or `source_group` set. I made a few assumptions here that I want to be explicit about:

- This assumes that all of your CSV files would have an additional column `name` which is a unique key for each rule within a particular file. It’ll use that as the key in one of the two rule maps.
- I assumed that a particular rule is _either_ CIDR-based or source-group-based, never both at the same time. If it’s source-group-based then I assumed you’d indicate that by writing the unique name of the other group in that field, which would be the same as the filename defining the other group but without the `.csv` suffix.

I’ve also just written all of this code directly into the comment box and not tested it against real Terraform, so I expect I’ve made some syntax errors and other mistakes along the way. Hopefully any error messages Terraform returns will give you a hint about how to fix my errors. If not, feel free to post another comment with the full text of the error and I’ll try to explain what mistake it’s reporting.

I hope this helps show some different patterns that’ll be useful when building your module! Of course, you don’t have to take exactly what I showed here for your final module, but I tried to show a few different common techniques here which come up in many reusable Terraform module designs, so you can take those patterns to use in future modules too.

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 25, 2021, 3:03am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/11 "2021-05-25T03:03:31Z")

</div>

hi, @apparentlymart. thanks for your answer

I used the first method which is without using csv. I have made these directories and folders

```auto
[terraform-community]$ ls
main.tf modules

```

```auto
[terraform-community]$ cd modules/
[modules]$ ls
vpc-security

```

```auto
[modules]$ cd vpc-security/
[vpc-security]$ ls
locals.tf main.tf variables.tf

```

Now inside vpc-security, main.tf

```auto
resource "aws_default_security_group" "this" {
  # The [*] operator converts a non-list value
  # that might be null into a list of either
  # zero or one elements.
  count = length(var.default_security_group[*])

  vpc_id = var.vpc_id
}

resource "aws_security_group_rule" "default_ingress" {
  # We'll filter out a non-null object if it
  # doesn't also specify to manage ingress.
  count = length([
    for o in var.default_security_group[*] : o
    if o.manage_ingress
  ])

  security_group_id = aws_default_security_group.this[count.index]
  source_security_group_id = aws_default_security_group.this[count.index]

  type = "ingress"
  protocol = -1
  from_port = 0
  to_port = 0
}

resource "aws_security_group_rule" "default_egress" {
  count = length([
    for o in var.default_security_group[*] : o
    if o.manage_egress
  ])

  security_group_id = aws_default_security_group.this[count.index]
  cidr_blocks = ["0.0.0.0/0"]

  type = "egress"
  protocol = -1
  from_port = 0
  to_port = 0
}

resource "aws_security_group" "additional" {
  for_each = var.security_groups

  vpc_id = var.vpc_id
  name = each.value.count
  tags = each.value.tags
}

resource "aws_security_group_rule" "additional_cidr" {
  for_each = {
    for o in local.cidr_rules : "${o.group_key}:${o.rule_key}" => o
  }

  security_group_id = aws_security_group.additional[each.value.group_key]
  type = each.value.rule.type
  protocol = each.value.rule.protocol
  from_port = each.value.from_port
  to_port = each.value.to_port
  cidr_blocks = each.value.cidr_blocks
  description = each.value.description
}

resource "aws_security_group_rule" "additional_source_group" {
  for_each = {
    for o in local.source_group_rules : "${o.group_key}:${o.rule_key}" => o
  }

  security_group_id = aws_security_group.additional[each.value.group_key]
  source_security_group_id = aws_security_group.additional[each.value.rule.source_group_key]

  type = each.value.rule.type
  protocol = each.value.rule.protocol
  from_port = each.value.from_port
  to_port = each.value.to_port
  description = each.value.description
}

```

inside vpc-security, variables.tf

```auto
variable "vpc_id" {
  type = string
}

variable "default_security_group" {
  type = object({
    manage_ingress = bool
    manage_egress = bool
  })

  # This variable is optional. Setting it
  # means that the caller wants the module
  # to manage the VPC's default security
  # group.
  default = null
}

variable "security_groups" {
  type = map(object({
    name = string
    tags = map(string)

    # AWS allows both rules that are matched by
    # source network address and rules matched
    # by source security group. They each need
    # different handling by our module so we'll
    # use two different attributes.
    cidr_rules = map(object({
      type = string
      protocol = string
      from_port = number
      to_port = number
      cidr_blocks = set(string)
      description = string
    }))
    source_group_rules = map(object({
      type = string
      protocol = string
      from_port = number
      to_port = number
      source_group_key = string
      description = string
    }))
  }))
  default = {}
}

```

inside vpc-security, locals.tf

```auto
locals {
  cidr_rules = flatten([
    for group_key, group in var.security_groups : [
      for rule_key, rule in group.cidr_rules : {
        rule_key = rule_key
        group_key = group_key
        rule = rule
        group = group
      }
    ]
  ])
  source_group_rules = flatten([
    for group_key, group in var.security_groups : [
      for rule_key, rule in group.source_group_rules : {
        rule_key = rule_key
        group_key = group_key
        rule = rule
        group = group
      }
    ]
  ])
}

```

outside the module main.tf

```auto
resource "aws_vpc" "example" {
  cidr_block = "10.0.0.0/16"
}

module "security" {
  source = "./modules/vpc-security"

  vpc_id = aws_vpc.example.id
  default_security_group = {
    manage_ingress = true
    manage_egress = true
  }
  security_groups = {
    "Synoptek-Edge" = {
      name = "Synoptek Edge"
      tags = { Name = "Synoptek Edge" }
      cidr_rules = {
        "internal" = {
          type = "ingress"
          protocol = -1
          from_port = 0
          to_port = 0
          cidr_blocks = ["10.100.0.0/16"]
          description = ""
        },
        "AWS" = {
          type = "ingress"
          protocol = "tcp"
          from_port = 3389
          to_port = 3389
          cidr_blocks = ["10.100.10.81/32"]
          description = "AWS"
        },
        # (and so on)
      }
      source_group_rules = {}
    }
  }
}

```

I’m getting this error

```auto
provider.aws.region
  The region where AWS operations will take place. Examples
  are us-east-1, us-west-2, etc.

  Enter a value: us-west-2 

╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 18, in resource "aws_security_group_rule" "default_ingress":
│ 18: security_group_id = aws_default_security_group.this[count.index]
│ ├────────────────
│ │ aws_default_security_group.this is tuple with 1 element
│ │ count.index is 0
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 19, in resource "aws_security_group_rule" "default_ingress":
│ 19: source_security_group_id = aws_default_security_group.this[count.index]
│ ├────────────────
│ │ aws_default_security_group.this is tuple with 1 element
│ │ count.index is 0
│ 
│ Inappropriate value for attribute "source_security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 33, in resource "aws_security_group_rule" "default_egress":
│ 33: security_group_id = aws_default_security_group.this[count.index]
│ ├────────────────
│ │ aws_default_security_group.this is tuple with 1 element
│ │ count.index is 0
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Unsupported attribute
│ 
│ on modules/vpc-security/main.tf line 46, in resource "aws_security_group" "additional":
│ 46: name = each.value.count
│ ├────────────────
│ │ each.value is object with 4 attributes
│ 
│ This object does not have an attribute named "count".

```

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 25, 2021, 3:24am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/12 "2021-05-25T03:24:44Z")

</div>

@apparentlymart

I have commented the default security group codes as I just want the additional security group for now and test on it. I have also edited some codes line. I was having following error so I have put rule after the each.value

```auto
provider.aws.region
  The region where AWS operations will take place. Examples
  are us-east-1, us-west-2, etc.

  Enter a value: us-west-2

╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 1 attribute "Synoptek-Edge"
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 1 attribute "Synoptek-Edge"
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Unsupported attribute
│ 
│ on modules/vpc-security/main.tf line 60, in resource "aws_security_group_rule" "additional_cidr":
│ 60: from_port = each.value.from_port
│ ├────────────────
│ │ each.value is object with 4 attributes
│ 
│ This object does not have an attribute named "from_port".
╵
╷
│ Error: Unsupported attribute
│ 
│ on modules/vpc-security/main.tf line 60, in resource "aws_security_group_rule" "additional_cidr":
│ 60: from_port = each.value.from_port
│ ├────────────────
│ │ each.value is object with 4 attributes
│ 
│ This object does not have an attribute named "from_port".
╵
╷
│ Error: Unsupported attribute
│ 
│ on modules/vpc-security/main.tf line 61, in resource "aws_security_group_rule" "additional_cidr":
│ 61: to_port = each.value.to_port
│ ├────────────────
│ │ each.value is object with 4 attributes
│ 
│ This object does not have an attribute named "to_port".
╵
╷
│ Error: Unsupported attribute
│ 
│ on modules/vpc-security/main.tf line 61, in resource "aws_security_group_rule" "additional_cidr":
│ 61: to_port = each.value.to_port
│ ├────────────────
│ │ each.value is object with 4 attributes
│ 
│ This object does not have an attribute named "to_port".
╵
╷
│ Error: Unsupported attribute
│ 
│ on modules/vpc-security/main.tf line 62, in resource "aws_security_group_rule" "additional_cidr":
│ 62: cidr_blocks = each.value.cidr_blocks
│ ├────────────────
│ │ each.value is object with 4 attributes
│ 
│ This object does not have an attribute named "cidr_blocks".
╵
╷
│ Error: Unsupported attribute
│ 
│ on modules/vpc-security/main.tf line 62, in resource "aws_security_group_rule" "additional_cidr":
│ 62: cidr_blocks = each.value.cidr_blocks
│ ├────────────────
│ │ each.value is object with 4 attributes
│ 
│ This object does not have an attribute named "cidr_blocks".
╵
╷
│ Error: Unsupported attribute
│ 
│ on modules/vpc-security/main.tf line 63, in resource "aws_security_group_rule" "additional_cidr":
│ 63: description = each.value.description
│ ├────────────────
│ │ each.value is object with 4 attributes
│ 
│ This object does not have an attribute named "description".
╵
╷
│ Error: Unsupported attribute
│ 
│ on modules/vpc-security/main.tf line 63, in resource "aws_security_group_rule" "additional_cidr":
│ 63: description = each.value.description
│ ├────────────────
│ │ each.value is object with 4 attributes
│ 
│ This object does not have an attribute named "description".

```

```auto
  protocol = each.value.rule.protocol
  from_port = each.value.rule.from_port
  to_port = each.value.rule.to_port
  cidr_blocks = each.value.rule.cidr_blocks

```

Please find the all codes below

```auto
/*
resource "aws_default_security_group" "this" {
  # The [*] operator converts a non-list value
  # that might be null into a list of either
  # zero or one elements.
  count = length(var.default_security_group[*])

  vpc_id = var.vpc_id
}

resource "aws_security_group_rule" "default_ingress" {
  # We'll filter out a non-null object if it
  # doesn't also specify to manage ingress.
  count = length([
    for o in var.default_security_group[*] : o
    if o.manage_ingress
  ])

  security_group_id = aws_default_security_group.this[count.index]
  source_security_group_id = aws_default_security_group.this[count.index]

  type = "ingress"
  protocol = -1
  from_port = 0
  to_port = 0
}

resource "aws_security_group_rule" "default_egress" {
  count = length([
    for o in var.default_security_group[*] : o
    if o.manage_egress
  ])

  security_group_id = aws_default_security_group.this[count.index]
  cidr_blocks = ["0.0.0.0/0"]

  type = "egress"
  protocol = -1
  from_port = 0
  to_port = 0
}
*/

resource "aws_security_group" "additional" {
  for_each = var.security_groups

  vpc_id = var.vpc_id
  name = each.value.name
  tags = each.value.tags
}

resource "aws_security_group_rule" "additional_cidr" {
  for_each = {
    for o in local.cidr_rules : "${o.group_key}:${o.rule_key}" => o
  }

  security_group_id = "${aws_security_group.additional[each.value.group_key]}"
  type = each.value.rule.type
  protocol = each.value.rule.protocol
  from_port = each.value.rule.from_port
  to_port = each.value.rule.to_port
  cidr_blocks = each.value.rule.cidr_blocks
  description = each.value.rule.description
}

resource "aws_security_group_rule" "additional_source_group" {
  for_each = {
    for o in local.source_group_rules : "${o.group_key}:${o.rule_key}" => o
  }

  security_group_id = "${aws_security_group.additional[each.value.group_key]}"
  source_security_group_id = aws_security_group.additional[each.value.rule.source_group_key]

  type = each.value.rule.type
  protocol = each.value.rule.protocol
  from_port = each.value.rule.from_port
  to_port = each.value.rule.to_port
  description = each.value.rule.description
}

```

error message

```auto
terraform plan
provider.aws.region
  The region where AWS operations will take place. Examples
  are us-east-1, us-west-2, etc.

  Enter a value: us-west-2

╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = "${aws_security_group.additional[each.value.group_key]}"
│ ├────────────────
│ │ aws_security_group.additional is object with 1 attribute "Synoptek-Edge"
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = "${aws_security_group.additional[each.value.group_key]}"
│ ├────────────────
│ │ aws_security_group.additional is object with 1 attribute "Synoptek-Edge"
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.

```

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 25, 2021, 10:54am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/13 "2021-05-25T10:54:37Z")

</div>

@apparentlymart  
I have also tested the second approach.

```auto
[security-groups]$ ls
nextsg.csv Synoptek-Edge.csv

```

Synoptek-Edge.csv I have added additional column name at first as you told before.

```auto
name,type,protocol,from,to,cidr_blocks,description
a,ingress,-1,0,0,10.100.0.0/16,
b,ingress,tcp,3389,3389,10.100.10.81/32,AWS-SYN-JUMP02
c,ingress,tcp,3389,3389,104.32.122.85/32,Synoptek
d,ingress,tcp,3389,3389,204.227.0.127/32,Synoptek
e,ingress,tcp,3389,3389,204.227.0.254/32,Synoptek
f,ingress,icmp,-1,-1,10.100.0.0/16,
g,egress,-1,0,0,0.0.0.0/0,

```

```auto
locals {
  rule_files = fileset("${path.module}/security-groups", "*.csv")
  rule_file_names = {
    # Trim the .csv suffix
    for fn in local.rule_files : fn => substr(fn, 0, length(fn) - 4)
  }
  rule_file_contents = {
    for fn in local.rule_files : fn => csvdecode(file("${path.module}/security-groups/${fn}"))
  }
  rule_file_cidr = {
    for fn, raws in local.rule_file_contents : fn => [
      for raw in raws : raw
      if try(raw.cidr_blocks, "") != ""
    ]
  }
  rule_file_srcgrp = {
    for fn, raws in local.rule_file_contents : fn => [
      for raw in raws : raw
      if try(raw.source_group, "") != ""
    ]
  }
  groups = {
    for fn, raw in local.rule_file_contents : local.rule_file_names[fn] => {
      name = local.rule_file_names[fn]
      tags = { Name = local.rule_file_names[fn] }
      cidr_rules = {
        for raw in local.rule_file_cidr[fn] : raw.name => {
          type = raw.type
          protocol = raw.protocol
          from_port = tonumber(raw.from)
          to_port = tonumber(raw.to)
          cidr_blocks = split(" ", raw.cidr_blocks)
          description = try(raw.description, raw.name)
        }
      }
      source_group_rules = {
        for raw in local.rule_file_srcgrp[fn] : raw.name => {
          type = raw.type
          protocol = raw.protocol
          from_port = tonumber(raw.from)
          to_port = tonumber(raw.to)
          source_group_key = raw.source_group
          description = try(raw.description, raw.name)
        }
      }
    }
  }
}

```

```auto
resource "aws_vpc" "example" {
  cidr_block = "10.0.0.0/16"
}
provider "aws" {
  region = "us-west-2"
}
module "security" {
  source = "./modules/vpc-security"
  vpc_id = aws_vpc.example.id
  security_groups = local.groups
}

```

error message

```auto
Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "nextsg"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "nextsg"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "nextsg"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "nextsg"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "nextsg"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "nextsg"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "Synoptek-Edge"
│ 
│ Inappropriate value for attribute "security_group_id": string required.
╵
╷
│ Error: Incorrect attribute value type
│ 
│ on modules/vpc-security/main.tf line 57, in resource "aws_security_group_rule" "additional_cidr":
│ 57: security_group_id = aws_security_group.additional[each.value.group_key]
│ ├────────────────
│ │ aws_security_group.additional is object with 2 attributes
│ │ each.value.group_key is "nextsg"
│ 
│ Inappropriate value for attribute "security_group_id": string required.

```

modules/vpc-security/main.tf

```auto
44 resource "aws_security_group" "additional" {
 45 for_each = var.security_groups
 46 
 47 vpc_id = var.vpc_id
 48 name = each.value.name
 49 tags = each.value.tags
 50 }
 51 
 52 resource "aws_security_group_rule" "additional_cidr" {
 53 for_each = {
 54 for o in local.cidr_rules : "${o.group_key}:${o.rule_key}" => o
 55 }
 56 
 57 security_group_id = aws_security_group.additional[each.value.group_key]
 58 type = each.value.rule.type
 59 protocol = each.value.rule.protocol
 60 from_port = each.value.rule.from_port
 61 to_port = each.value.rule.to_port
 62 cidr_blocks = each.value.rule.cidr_blocks
 63 description = each.value.rule.description
 64 }
 65 
 66 resource "aws_security_group_rule" "additional_source_group" {
 67 for_each = {
 68 for o in local.source_group_rules : "${o.group_key}:${o.rule_key}" => o
 69 }
 70 
 71 security_group_id = "${aws_security_group.additional[each.value.group_key]}"
 72 source_security_group_id = aws_security_group.additional[each.value.rule.source_group_key]
 73 
 74 type = each.value.rule.type
 75 protocol = each.value.rule.protocol
 76 from_port = each.value.rule.from_port
 77 to_port = each.value.rule.to_port
 78 description = each.value.rule.description
 79 }

```

I want this approach. This looks nice to me. But facing error

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [May 25, 2021, 4:03pm UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/14 "2021-05-25T16:03:18Z")

</div>

For this default security group situation, it looks like I just forgot the `.id` attribute on the end of this:

```auto
  security_group_id = aws_default_security_group.this[count.index].id

```

We need the _id_ of the security group here, not the entire object representing a security group.

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [May 25, 2021, 4:04pm UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/15 "2021-05-25T16:04:38Z")

</div>

…and indeed, looks like I made the same mistake in all of the other rules too. This is, of course, the result of copy and paste! I think you add `.id` to the end of all of these in a way similar to what I showed in my last comment then it should get past this particular set of errors.

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 26, 2021, 6:16am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/16 "2021-05-26T06:16:23Z")

</div>

Hi, @apparentlymart, Thanks for the answer. I tried and it worked.  
I want to put ipv6\_cidr\_blocks. You can see in main.tf, variable.tf and locals.tf I have put ipv6\_cidr\_blocks section. I there is some error in that. please look into it

I have edited the code however, it is giving me error

inside modules/vpc-security/main.tf

```auto
resource "aws_default_security_group" "this" {
  # The [*] operator converts a non-list value
  # that might be null into a list of either
  # zero or one elements.
  count = length(var.default_security_group[*])

  vpc_id = var.vpc_id
}

resource "aws_security_group_rule" "default_ingress" {
  # We'll filter out a non-null object if it
  # doesn't also specify to manage ingress.
  count = length([
    for o in var.default_security_group[*] : o
    if o.manage_ingress
  ])

  security_group_id = aws_default_security_group.this[count.index].id
  source_security_group_id = aws_default_security_group.this[count.index].id

  type = "ingress"
  protocol = -1
  from_port = 0
  to_port = 0
}

resource "aws_security_group_rule" "default_egress" {
  count = length([
    for o in var.default_security_group[*] : o
    if o.manage_egress
  ])

  security_group_id = aws_default_security_group.this[count.index].id
  cidr_blocks = ["0.0.0.0/0"]

  type = "egress"
  protocol = -1
  from_port = 0
  to_port = 0
}

resource "aws_security_group" "additional" {
  for_each = var.security_groups

  vpc_id = var.vpc_id
  name = each.value.name
  tags = each.value.tags
}

resource "aws_security_group_rule" "additional_cidr" {
  for_each = {
    for o in local.cidr_rules : "${o.group_key}:${o.rule_key}" => o
  }

  security_group_id = aws_security_group.additional[each.value.group_key].id
  type = each.value.rule.type
  protocol = each.value.rule.protocol
  from_port = each.value.rule.from_port
  to_port = each.value.rule.to_port
  cidr_blocks = each.value.rule.cidr_blocks
  description = each.value.rule.description
}

resource "aws_security_group_rule" "additional_cidr_v6" {
  for_each = {
    for o in local.ipv6_cidr_rules : "${o.group_key}:${o.rule_key}" => o
  }

  security_group_id = aws_security_group.additional[each.value.group_key].id
  type = each.value.rule.type
  protocol = each.value.rule.protocol
  from_port = each.value.rule.from_port
  to_port = each.value.rule.to_port
  ipv6_cidr_blocks = each.value.rule.ipv6_cidr_blocks
  description = each.value.rule.description
}

resource "aws_security_group_rule" "additional_source_group" {
  for_each = {
    for o in local.source_group_rules : "${o.group_key}:${o.rule_key}" => o
  }

  security_group_id = aws_security_group.additional[each.value.group_key].id
  source_security_group_id = aws_security_group.additional[each.value.rule.source_group_key].id

  type = each.value.rule.type
  protocol = each.value.rule.protocol
  from_port = each.value.rule.from_port
  to_port = each.value.rule.to_port
  description = each.value.rule.description
}

```

inside modules/vpc-security/variables.tf

```auto
variable "vpc_id" {
  type = string
}

variable "default_security_group" {
  type = object({
    manage_ingress = bool
    manage_egress = bool
  })

  # This variable is optional. Setting it
  # means that the caller wants the module
  # to manage the VPC's default security
  # group.
  default = null
}

variable "security_groups" {
  type = map(object({
    name = string
    tags = map(string)

    # AWS allows both rules that are matched by
    # source network address and rules matched
    # by source security group. They each need
    # different handling by our module so we'll
    # use two different attributes.
    cidr_rules = map(object({
      type = string
      protocol = string
      from_port = number
      to_port = number
      cidr_blocks = set(string)
      description = string
    }))
    ipv6_cidr_rules = map(object({
      type = string
      protocol = string
      from_port = number
      to_port = number
      ipv6_cidr_blocks = set(string)
      description = string
    }))
    source_group_rules = map(object({
      type = string
      protocol = string
      from_port = number
      to_port = number
      source_group_key = string
      description = string
    }))
  }))
  default = {}
}

```

inside modules/vpc-security/locals.tf

```auto
locals {
  cidr_rules = flatten([
    for group_key, group in var.security_groups : [
      for rule_key, rule in group.cidr_rules : {
        rule_key = rule_key
        group_key = group_key
        rule = rule
        group = group
      }
    ]
  ])
  ipv6_cidr_rules = flatten([
    for group_key, group in var.security_groups : [
      for rule_key, rule in group.ipv6_cidr_rules : {
        rule_key = rule_key
        group_key = group_key
        rule = rule
        group = group
      }
    ]
  ])
  source_group_rules = flatten([
    for group_key, group in var.security_groups : [
      for rule_key, rule in group.source_group_rules : {
        rule_key = rule_key
        group_key = group_key
        rule = rule
        group = group
      }
    ]
  ])
}

```

local.tf outside the module directory

```auto
locals {
  rule_files = fileset("${path.module}/security-groups", "*.csv")
  rule_file_names = {
    # Trim the .csv suffix
    for fn in local.rule_files : fn => substr(fn, 0, length(fn) - 4)
  }
  rule_file_contents = {
    for fn in local.rule_files : fn => csvdecode(file("${path.module}/security-groups/${fn}"))
  }
  rule_file_cidr = {
    for fn, raws in local.rule_file_contents : fn => [
      for raw in raws : raw
      if try(raw.cidr_blocks, "") != ""
    ]
  }
  rule_file_ipv6_cidr = {
    for fn, raws in local.rule_file_contents : fn => [
      for raw in raws : raw
      if try(raw.ipv6_cidr_blocks, "") != ""
    ]
  }

  rule_file_srcgrp = {
    for fn, raws in local.rule_file_contents : fn => [
      for raw in raws : raw
      if try(raw.source_group, "") != ""
    ]
  }
  groups = {
    for fn, raw in local.rule_file_contents : local.rule_file_names[fn] => {
      name = local.rule_file_names[fn]
      tags = { Name = local.rule_file_names[fn] }
      cidr_rules = {
        for raw in local.rule_file_cidr[fn] : raw.name => {
          type = raw.type
          protocol = raw.protocol
          from_port = tonumber(raw.from)
          to_port = tonumber(raw.to)
          cidr_blocks = split(" ", raw.cidr_blocks)
          description = try(raw.description, raw.name)
        }
      }
      ipv6_cidr_rules = {
        for raw in local.rule_file_ipv6_cidr[fn] : raw.name => {
          type = raw.type
          protocol = raw.protocol
          from_port = tonumber(raw.from)
          to_port = tonumber(raw.to)
          cidr_blocks = split(" ", raw.ipv6_cidr_blocks)
          description = try(raw.description, raw.name)
        }
      }
      source_group_rules = {
        for raw in local.rule_file_srcgrp[fn] : raw.name => {
          type = raw.type
          protocol = raw.protocol
          from_port = tonumber(raw.from)
          to_port = tonumber(raw.to)
          source_group_key = raw.source_group
          description = try(raw.description, raw.name)
        }
      }
    }
  }
}

```

main.tf outisde module folder

```auto
resource "aws_vpc" "example" {
  cidr_block = "10.0.0.0/16"
}

provider "aws" {
  region = "us-west-2"
}

module "security" {
  source = "./modules/vpc-security"

  vpc_id = aws_vpc.example.id

  security_groups = local.groups

  default_security_group = {
    manage_ingress = true
    manage_egress = true
  }
}

```

csv file

```auto
name,type,protocol,from,to,cidr_blocks,ipv6_cidr_blocks,source_group,description
a,ingress,tcp,443,443,0.0.0.0/0,::/0,,AWS-SYN-JUMP02
b,ingress,tcp,3389,3389,10.100.10.81/32,,,AWS-SYN-JUMP02
c,ingress,tcp,80,80,,,Synoptek-Edge,AWS-SYN-JUMP02
g,egress,-1,0,0,0.0.0.0/0,,,

```

error message

```auto
Error: Invalid value for module argument
│ 
│ on main.tf line 14, in module "security":
│ 14: security_groups = local.groups
│ 
│ The given value is not suitable for child module variable "security_groups"
│ defined at modules/vpc-security/variables.tf:18,1-27: element "nextsg":
│ attribute "ipv6_cidr_rules": element "a": attribute "ipv6_cidr_blocks" is
│ required.

```

I want to the sg rule of cidr\_blocks, ipv6\_cidr\_blocks and source security group id

 ![1](https://global.discourse-cdn.com/hashicorp/original/3X/0/6/067c66874575f2265c6e0e4454c101ad8a6985c7.png)  
as shown in the image

 ![2](https://global.discourse-cdn.com/hashicorp/original/3X/e/d/ed10de3e0101774ab37a70b78cb79bcce6a62357.png)

 ![3](https://global.discourse-cdn.com/hashicorp/original/3X/6/3/636407df9ef2478f8e82a9f61a68611f4990a011.png)

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 26, 2021, 7:06am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/17 "2021-05-26T07:06:02Z")

</div>

its fine now

edited the code of locals.tf outside module folder. I put ipv6\_cidr\_blocks instead of cidr\_blocks

```auto
ipv6_cidr_blocks = split(" ", raw.ipv6_cidr_blocks)

```

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [May 27, 2021, 11:58am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/18 "2021-05-27T11:58:46Z")

</div>

Hi, @apparentlymart

Got another issue

The code is taking the tag:Name and Security Group Name from the csv file name. But I have some csv files that name starts with “sg-” and when I do apply, it says that the security group name should not start with “sg-”. Now what I want to do is that I want to replace those Security Group Name from sg- to different name. How can I do so?

I have used replace function

but how can I use it for multiple names?

```auto
groups = {
    for fn, raw in local.rule_file_contents : local.rule_file_names[fn] => {
      name = replace(local.rule_file_names[fn], "/sg-a/", "test-a")

```

There are many csv files whose name starts with sg- like sg-a, sg-b, sg-c. I the code I have only put for sg-a. How can I do for all.

I want to change sg-a as test-a, sg-b as test-b and so on

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [June 2, 2021, 1:14am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/19 "2021-06-02T01:14:58Z")

</div>

Hi @dipendra.chaudhary,

If you want to systematically always replace and `sg-` prefix with a `test-` prefix then I think you can match that with a regular expression pattern like this:

```auto
replace(input, "/^sg-/", "test-")

```

The `^` symbol here represents “start of string”, so this regular expression will only match the characters `sg-` at the start of the given string, and if it finds them it’ll replace them with the string given in the third argument, “test-”. If you use that with the input string set to `local.rule_file_names[fn]` as in your example then I think it should work.

You can read more about the regular expression pattern syntax as part of the documentation of [the `regex` function](https://www.terraform.io/docs/language/functions/regex.html).

---

<div class="post-metadata">

**Author:** ![dipendra.chaudhary](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/dipendra.chaudhary/32/18352_2.png) [@dipendra.chaudhary](https://discuss.hashicorp.com/u/dipendra.chaudhary)\
**Post date:** [June 2, 2021, 6:23am UTC](https://discuss.hashicorp.com/t/putting-csvdecode-in-security-group-rule/24409/20 "2021-06-02T06:23:14Z")

</div>

thanks @apparentlymart

lets say if I have 3 sgs with csv file name (of course the tag name will be taken from csv file name) sg-1.csv, sg-2.csv and sg-3.csv. And I want to make the security group name test-1, test-2 and test-3 respectively. How can I do that?
