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/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/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 8989373..971300c 100644
--- a/custom/README.md
+++ b/custom/README.md
@@ -100,17 +100,36 @@ 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 |
|------|---------|
-| [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 +142,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 +154,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)` | [| 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 |
"0.0.0.0/0"
]
list(object({
from = number
to = number
proto = optional(string, "tcp")
label = optional(string)
})) | [| 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) |
{
"from": 0,
"label": "all",
"proto": -1,
"to": 0
}
]
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..b99e90d
--- /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
+ }
+ }
+
+ # 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
+ 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.ingress : 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) > 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_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/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/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" }]
+}
+
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
+ )
+}