From 9c454cebc64f2f63e47a53c5c31e70cb5a033846 Mon Sep 17 00:00:00 2001 From: badra001 Date: Tue, 26 Aug 2025 10:23:55 -0400 Subject: [PATCH 1/4] add prefix-list capability to custom --- common/README.md | 2 +- common/variables.common.tf | 2 +- custom/README.md | 19 ++- custom/custom.tf | 185 +++++++++++++++++++++++++ custom/data.prefix_lists.tf | 1 + custom/resources.tf | 1 - custom/settings.tf | 2 +- custom/variables.prefix_lists.tf | 1 + custom/variables.prefix_lists_ports.tf | 22 +++ 9 files changed, 225 insertions(+), 10 deletions(-) create mode 100644 custom/custom.tf create mode 120000 custom/data.prefix_lists.tf delete mode 120000 custom/resources.tf create mode 120000 custom/variables.prefix_lists.tf create mode 100644 custom/variables.prefix_lists_ports.tf diff --git a/common/README.md b/common/README.md index e3e1098..16f8b4c 100644 --- a/common/README.md +++ b/common/README.md @@ -45,7 +45,7 @@ No modules. | [ingress\_security\_groups](#input\_ingress\_security\_groups) | List of ingress security groups for all ports | `list(string)` | `[]` | no | | [ingress\_self\_port\_list](#input\_ingress\_self\_port\_list) | Ingress port list of 4-tuple: from, to, proto, description | `list` | `[]` | no | | [ingress\_self\_port\_map](#input\_ingress\_self\_port\_map) | Ingress self access port list of objects: from, to, proto, description |
list(object({
from = number
to = number
proto = any
description = string
}))
| `[]` | no | -| [tags](#input\_tags) | Extra security group tags | `map` | `{}` | no | +| [tags](#input\_tags) | Extra security group tags | `map(any)` | `{}` | no | | [use\_vpc\_cidr](#input\_use\_vpc\_cidr) | Enable\|Disable use of VPC CIDR block in the ingress\_networks | `bool` | `false` | no | | [vpc\_full\_name](#input\_vpc\_full\_name) | VPC Name | `string` | `""` | no | | [vpc\_id](#input\_vpc\_id) | VPC ID Number | `string` | n/a | yes | diff --git a/common/variables.common.tf b/common/variables.common.tf index 1464ab7..53222d8 100644 --- a/common/variables.common.tf +++ b/common/variables.common.tf @@ -36,7 +36,7 @@ variable "egress_security_groups" { variable "tags" { description = "Extra security group tags" - type = map + type = map(any) default = {} } diff --git a/custom/README.md b/custom/README.md index 8989373..81737ed 100644 --- a/custom/README.md +++ b/custom/README.md @@ -104,13 +104,14 @@ module "sg_test" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.12 | +| [terraform](#requirement\_terraform) | >= 1.0.0 | +| [aws](#requirement\_aws) | >= 5.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | n/a | +| [aws](#provider\_aws) | >= 5.0 | ## Modules @@ -123,6 +124,8 @@ No modules. | [aws_security_group.this_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_arn.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_ec2_managed_prefix_list.egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_managed_prefix_list) | data source | +| [aws_ec2_managed_prefix_list.ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_managed_prefix_list) | data source | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | | [aws_security_group.egress_security_groups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/security_group) | data source | | [aws_security_group.ingress_security_groups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/security_group) | data source | @@ -133,19 +136,23 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [description](#input\_description) | Security Group Description | `string` | `""` | no | -| [egress\_networks](#input\_egress\_networks) | List of egress networks (with all pre-defined egress ports) (default: any) | `list(string)` |
[
"0.0.0.0/0"
]
| no | +| [egress\_networks](#input\_egress\_networks) | List of egress networks (with all pre-defined egress ports) (default: any) | `list(string)` |
[
"0.0.0.0/0"
]
| no | +| [egress\_prefix\_list\_names](#input\_egress\_prefix\_list\_names) | List of prefix list names for eggress access | `list(string)` | `[]` | no | +| [egress\_prefix\_list\_ports](#input\_egress\_prefix\_list\_ports) | List of port objects (from,to,proto,label) for egress prefix lists |
list(object({
from = number
to = number
proto = optional(string, "tcp")
label = optional(string)
}))
|
[
{
"from": 0,
"label": "all",
"proto": -1,
"to": 0
}
]
| no | | [egress\_security\_groups](#input\_egress\_security\_groups) | List of egress security groups (all ports) | `list(string)` | `[]` | no | | [enable\_default\_egress](#input\_enable\_default\_egress) | Enable\|Disable default egress of ALL | `bool` | `true` | no | | [enable\_self](#input\_enable\_self) | Enable\|Disable self full access | `bool` | `false` | no | | [ingress\_networks](#input\_ingress\_networks) | List of ingress networks for access (with all pre-defined ingress ports) | `list(string)` | `[]` | no | | [ingress\_port\_list](#input\_ingress\_port\_list) | Ingress port list of 5-tuple: from, to, proto, description, and cidr(list) | `list` | `[]` | no | -| [ingress\_port\_map](#input\_ingress\_port\_map) | Ingress port list of objects: from, to, proto, description and cidr(list) |
list(object({
from = number
to = number
proto = any
description = string
cidr = list(string)
}))
| `[]` | no | +| [ingress\_port\_map](#input\_ingress\_port\_map) | Ingress port list of objects: from, to, proto, description and cidr(list) |
list(object({
from = number
to = number
proto = any
description = string
cidr = list(string)
}))
| `[]` | no | +| [ingress\_prefix\_list\_names](#input\_ingress\_prefix\_list\_names) | List of prefix list names for ingress access | `list(string)` | `[]` | no | +| [ingress\_prefix\_list\_ports](#input\_ingress\_prefix\_list\_ports) | List of port objects (from,to,proto,label) for ingress prefix lists |
list(object({
from = number
to = number
proto = optional(string, "tcp")
label = optional(string)
}))
| `[]` | no | | [ingress\_security\_groups](#input\_ingress\_security\_groups) | List of ingress security groups for all ports | `list(string)` | `[]` | no | | [ingress\_self\_port\_list](#input\_ingress\_self\_port\_list) | Ingress port list of 4-tuple: from, to, proto, description | `list` | `[]` | no | -| [ingress\_self\_port\_map](#input\_ingress\_self\_port\_map) | Ingress self access port list of objects: from, to, proto, description |
list(object({
from = number
to = number
proto = any
description = string
}))
| `[]` | no | +| [ingress\_self\_port\_map](#input\_ingress\_self\_port\_map) | Ingress self access port list of objects: from, to, proto, description |
list(object({
from = number
to = number
proto = any
description = string
}))
| `[]` | no | | [name](#input\_name) | Security Group Name (required) | `string` | n/a | yes | | [short\_description](#input\_short\_description) | Security Group Short Description | `string` | `""` | no | -| [tags](#input\_tags) | Extra security group tags | `map` | `{}` | no | +| [tags](#input\_tags) | Extra security group tags | `map(any)` | `{}` | no | | [use\_vpc\_cidr](#input\_use\_vpc\_cidr) | Enable\|Disable use of VPC CIDR block in the ingress\_networks | `bool` | `false` | no | | [vpc\_full\_name](#input\_vpc\_full\_name) | VPC Name | `string` | `""` | no | | [vpc\_id](#input\_vpc\_id) | VPC ID Number | `string` | n/a | yes | diff --git a/custom/custom.tf b/custom/custom.tf new file mode 100644 index 0000000..1eed1d4 --- /dev/null +++ b/custom/custom.tf @@ -0,0 +1,185 @@ +locals { + vpc_networks = var.use_vpc_cidr ? [data.aws_vpc.this_vpc[0].cidr_block] : [] + external_ingress_networks = compact(concat(local.vpc_networks, local.ingress_networks)) + ingress_sg_names = zipmap(var.ingress_security_groups, data.aws_security_group.ingress_security_groups[*].name) + egress_sg_names = zipmap(var.egress_security_groups, data.aws_security_group.egress_security_groups[*].name) + # self = var.enable_self ? local.self_ports : [] +} + +resource "aws_security_group" "this_security_group" { + name = local.name + description = var.description + vpc_id = var.vpc_id + + #--- + # ingress + #--- + # ingresss external port list (list + vpc if enabaled) + dynamic "ingress" { + for_each = local.port_map["external"] + iterator = p + content { + description = "${local.short_description}: ${p.value["description"]}" + from_port = p.value["from"] + to_port = p.value["to"] + protocol = p.value["proto"] + cidr_blocks = length(p.value["cidr"]) == 0 ? distinct(flatten(compact(concat(local.external_ingress_networks, var.ingress_networks)))) : distinct(flatten(compact(concat(p.value["cidr"], var.ingress_networks)))) + } + } + + # ingress module-defined ports + dynamic "ingress" { + for_each = local.port_map["module_ports"] + iterator = p + content { + description = "${local.short_description}: ${p.value["description"]}" + from_port = p.value["from"] + to_port = p.value["to"] + protocol = p.value["proto"] + cidr_blocks = length(p.value["cidr"]) == 0 ? distinct(flatten(compact(concat(local.external_ingress_networks, var.ingress_networks)))) : distinct(flatten(compact(concat(p.value["cidr"], var.ingress_networks)))) + } + } + + # ingress_ports + dynamic "ingress" { + for_each = local.port_map["ingress_ports"] + iterator = p + content { + description = "${local.short_description}: ${p.value["description"]}" + from_port = p.value["from"] + to_port = p.value["to"] + protocol = p.value["proto"] + cidr_blocks = length(p.value["cidr"]) == 0 ? distinct(flatten(compact(concat(local.external_ingress_networks, var.ingress_networks)))) : distinct(flatten(compact(concat(p.value["cidr"], var.ingress_networks)))) + } + } + + # ingress map + dynamic "ingress" { + for_each = local.port_map["ingress_map"] + iterator = p + content { + description = "${local.short_description}: ${p.value["description"]}" + from_port = p.value["from"] + to_port = p.value["to"] + protocol = p.value["proto"] + cidr_blocks = length(p.value["cidr"]) == 0 ? distinct(flatten(compact(concat(local.external_ingress_networks, var.ingress_networks)))) : distinct(flatten(compact(concat(p.value["cidr"], var.ingress_networks)))) + } + } + + # ingress security group ids (all) + dynamic "ingress" { + for_each = local.ingress_sg + iterator = sg + content { + description = "${local.short_description}: ${local.ingress_sg_names[sg.value]}" + from_port = 0 + to_port = 0 + protocol = -1 + security_groups = [sg.value] + } + } + + #--- + # ingress self + #--- + # ingress self port list + dynamic "ingress" { + for_each = var.enable_self ? local.self_port_map["ingress_ports"] : [] + iterator = sg + content { + description = "${local.short_description}: self ${sg.value["description"]}" + from_port = sg.value["from"] + to_port = sg.value["to"] + protocol = sg.value["proto"] + self = true + } + } + + # ingress self port map + dynamic "ingress" { + for_each = var.enable_self ? local.self_port_map["ingress_map"] : [] + iterator = sg + content { + description = "${local.short_description}: self ${sg.value["description"]}" + from_port = sg.value["from"] + to_port = sg.value["to"] + protocol = sg.value["proto"] + self = true + } + } + + # ingress self port default + dynamic "ingress" { + for_each = var.enable_self ? local.self_port_map["default"] : [] + iterator = sg + content { + description = "${local.short_description}: self ${sg.value["description"]}" + from_port = sg.value["from"] + to_port = sg.value["to"] + protocol = sg.value["proto"] + self = true + } + } + + # inress with prefix lists + dynamic "ingress" { + for_each = length(var.ingress_prefix_list_names) && length(var.ingress_prefix_list_ports) > 0 ? toset(var.ingress_prefix_list_ports) : toset([]) + iterator = p + content { + description = try(p.value.label, local.short_description) + from_port = p.value.from + to_port = p.value.to + protocol = p.value.proto + prefix_list_ids = [for pl in data.aws_ec2_managed_prefix_list.inress : pl.id] + } + } + + # egress all (with flag enable_default_egress) + dynamic "egress" { + for_each = var.enable_default_egress ? [1] : [] + iterator = sg + content { + description = "${local.short_description}: All" + from_port = 0 + to_port = 0 + protocol = -1 + cidr_blocks = distinct(flatten(compact(concat(local.egress_networks, var.egress_networks)))) + } + } + + # egress security group ids (all) + dynamic "egress" { + for_each = local.egress_sg + iterator = sg + content { + description = "${local.short_description}: ${local.egress_sg_names[sg]}" + from_port = 0 + to_port = 0 + protocol = -1 + security_groups = [sg] + } + } + + # egress with prefix lists + dynamic "egress" { + for_each = length(var.egress_prefix_list_names) && length(var.egress_prefix_list_ports) > 0 ? toset(var.egress_prefix_list_ports) : toset([]) + iterator = p + content { + description = try(p.value.label, local.short_description) + from_port = p.value.from + to_port = p.value.to + protocol = p.value.proto + prefix_list_ids = [for pl in data.aws_ec2_managed_prefix_list.egress : pl.id] + } + } + + tags = merge( + var.tags, + { + "Name" = "sg-${local.name}" + "boc:created_by" = "terraform" + "boc:tf_module_version" = local._module_version + "boc:vpc:info" = join(" ", compact([var.vpc_id, var.vpc_full_name])) + } + ) +} diff --git a/custom/data.prefix_lists.tf b/custom/data.prefix_lists.tf new file mode 120000 index 0000000..08df656 --- /dev/null +++ b/custom/data.prefix_lists.tf @@ -0,0 +1 @@ +../common/data.prefix_lists.tf \ No newline at end of file diff --git a/custom/resources.tf b/custom/resources.tf deleted file mode 120000 index 6dd8c84..0000000 --- a/custom/resources.tf +++ /dev/null @@ -1 +0,0 @@ -../common/resources.tf \ No newline at end of file diff --git a/custom/settings.tf b/custom/settings.tf index ffe1549..f8f933b 100644 --- a/custom/settings.tf +++ b/custom/settings.tf @@ -1,7 +1,7 @@ locals { name = var.name != "" ? var.name : local._defaults["name"] is_modular = var.name == "" || length(regexall("^m-", var.name)) > 0 - enable_self = var.enable_self ? ! local.is_modular : false + enable_self = var.enable_self ? !local.is_modular : false description = var.description != "" ? var.description : local._defaults["description"] short_description = var.short_description != "" ? var.short_description : local._defaults["short_description"] } diff --git a/custom/variables.prefix_lists.tf b/custom/variables.prefix_lists.tf new file mode 120000 index 0000000..86cbd3c --- /dev/null +++ b/custom/variables.prefix_lists.tf @@ -0,0 +1 @@ +../common/variables.prefix_lists.tf \ No newline at end of file diff --git a/custom/variables.prefix_lists_ports.tf b/custom/variables.prefix_lists_ports.tf new file mode 100644 index 0000000..b3063dc --- /dev/null +++ b/custom/variables.prefix_lists_ports.tf @@ -0,0 +1,22 @@ +variable "ingress_prefix_list_ports" { + description = "List of port objects (from,to,proto,label) for ingress prefix lists" + type = list(object({ + from = number + to = number + proto = optional(string, "tcp") + label = optional(string) + })) + default = [] +} + +variable "egress_prefix_list_ports" { + description = "List of port objects (from,to,proto,label) for egress prefix lists" + type = list(object({ + from = number + to = number + proto = optional(string, "tcp") + label = optional(string) + })) + default = [{ from = 0, to = 0, proto = -1, label = "all" }] +} + From 16a1e5a1892f322208b52b7bae828955ea61c8d6 Mon Sep 17 00:00:00 2001 From: badra001 Date: Tue, 26 Aug 2025 10:34:24 -0400 Subject: [PATCH 2/4] fix --- custom/custom.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom/custom.tf b/custom/custom.tf index 1eed1d4..c1c2d7e 100644 --- a/custom/custom.tf +++ b/custom/custom.tf @@ -123,7 +123,7 @@ resource "aws_security_group" "this_security_group" { # inress with prefix lists dynamic "ingress" { - for_each = length(var.ingress_prefix_list_names) && length(var.ingress_prefix_list_ports) > 0 ? toset(var.ingress_prefix_list_ports) : toset([]) + for_each = length(var.ingress_prefix_list_names) > 0 && length(var.ingress_prefix_list_ports) > 0 ? toset(var.ingress_prefix_list_ports) : toset([]) iterator = p content { description = try(p.value.label, local.short_description) @@ -162,7 +162,7 @@ resource "aws_security_group" "this_security_group" { # egress with prefix lists dynamic "egress" { - for_each = length(var.egress_prefix_list_names) && length(var.egress_prefix_list_ports) > 0 ? toset(var.egress_prefix_list_ports) : toset([]) + for_each = length(var.egress_prefix_list_names) > 0 && length(var.egress_prefix_list_ports) > 0 ? toset(var.egress_prefix_list_ports) : toset([]) iterator = p content { description = try(p.value.label, local.short_description) From 8acc43efaf7daed80c2a2ad0b8900785e5083376 Mon Sep 17 00:00:00 2001 From: badra001 Date: Tue, 26 Aug 2025 10:38:06 -0400 Subject: [PATCH 3/4] fix --- custom/custom.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom/custom.tf b/custom/custom.tf index c1c2d7e..b99e90d 100644 --- a/custom/custom.tf +++ b/custom/custom.tf @@ -121,7 +121,7 @@ resource "aws_security_group" "this_security_group" { } } - # inress with prefix lists + # ingress with prefix lists dynamic "ingress" { for_each = length(var.ingress_prefix_list_names) > 0 && length(var.ingress_prefix_list_ports) > 0 ? toset(var.ingress_prefix_list_ports) : toset([]) iterator = p @@ -130,7 +130,7 @@ resource "aws_security_group" "this_security_group" { from_port = p.value.from to_port = p.value.to protocol = p.value.proto - prefix_list_ids = [for pl in data.aws_ec2_managed_prefix_list.inress : pl.id] + prefix_list_ids = [for pl in data.aws_ec2_managed_prefix_list.ingress : pl.id] } } From aa508ce926eea0a5ce98c800fcdef63d1c95a4e8 Mon Sep 17 00:00:00 2001 From: badra001 Date: Tue, 26 Aug 2025 10:53:20 -0400 Subject: [PATCH 4/4] add docs and example --- CHANGELOG.md | 3 +++ common/version.tf | 2 +- custom/README.md | 18 ++++++++++++++++++ custom/main.tf | 18 ++++++++++++++++++ examples/custom-prefix-lists/main.tf | 26 ++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 examples/custom-prefix-lists/main.tf diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e94eee..4af48d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,3 +82,6 @@ * 2.6.2 -- 2025-05-29 - it-windows-base: remove app28.csvd.census.gov from hosts + +* 2.7.0 -- 2025-08-26 + - custom: add prefix list capability diff --git a/common/version.tf b/common/version.tf index 4d32dce..2499cf3 100644 --- a/common/version.tf +++ b/common/version.tf @@ -1,3 +1,3 @@ locals { - _module_version = "2.6.2" + _module_version = "2.7.0" } diff --git a/custom/README.md b/custom/README.md index 81737ed..971300c 100644 --- a/custom/README.md +++ b/custom/README.md @@ -100,6 +100,24 @@ module "sg_test" { } ``` +## ingress\_prefix\_list\_names and ingress\_prefix\_list\_ports +In order to use a managed prefix list, you may pass a list of names in this field. The prefix lists +will be looked up and the resultant IDs used in the security group for inbound port access to +the port structure in `ingress_prefix_list_ports` as follows: + +```hcl +[ { from = NUMBER, to = NUMBER, proto = NUMBER-OR-STRING, label = STRING }, ] +``` + +## egress\_prefix\_list\_names and egress\_prefix\_list\_ports +In order to use a managed prefix list, you may pass a list of names in this field. The prefix lists +will be looked up and the resultant IDs used in the security group for outbound port access to +the port structure in `egress_prefix_list_ports` as follows: + +```hcl +[ { from = NUMBER, to = NUMBER, proto = NUMBER-OR-STRING, label = STRING }, ] +``` + ## Requirements | Name | Version | diff --git a/custom/main.tf b/custom/main.tf index 57f4377..7164807 100644 --- a/custom/main.tf +++ b/custom/main.tf @@ -100,6 +100,24 @@ * ) * } * ``` +* +* ## ingress_prefix_list_names and ingress_prefix_list_ports +* In order to use a managed prefix list, you may pass a list of names in this field. The prefix lists +* will be looked up and the resultant IDs used in the security group for inbound port access to +* the port structure in `ingress_prefix_list_ports` as follows: +* +* ```hcl +* [ { from = NUMBER, to = NUMBER, proto = NUMBER-OR-STRING, label = STRING }, ] +* ``` +* +* ## egress_prefix_list_names and egress_prefix_list_ports +* In order to use a managed prefix list, you may pass a list of names in this field. The prefix lists +* will be looked up and the resultant IDs used in the security group for outbound port access to +* the port structure in `egress_prefix_list_ports` as follows: +* +* ```hcl +* [ { from = NUMBER, to = NUMBER, proto = NUMBER-OR-STRING, label = STRING }, ] +* ``` */ # all of the code is in resource.tf, this is here for documention diff --git a/examples/custom-prefix-lists/main.tf b/examples/custom-prefix-lists/main.tf new file mode 100644 index 0000000..71bb23e --- /dev/null +++ b/examples/custom-prefix-lists/main.tf @@ -0,0 +1,26 @@ +data "aws_vpc" "vpc" { + filter { + name = "tag:Name" + values = [var.vpc_full_name] + } +} + +module "sg_bigfix" { + source = "git@github.e.it.census.gov:terraform-modules/aws-common-security-groups.git//custom?ref=feature/add-prefix-list" + vpc_id = data.aws_vpc.vpc.id + name = "ois-bigfix" + description = "OIS Bigfix Service Port" + short_description = "BigFix" + enable_self = false + + ingress_prefix_list_names = ["all-cloud.core"] + ingress_prefix_list_ports = [ + { from = 52311, to = 52311, proto = "tcp", label = "BigFix-Relay" }, + ] + + tags = merge( + local.base_tags, + # var.application_tags, + # etc + ) +}