diff --git a/CHANGELOG.md b/CHANGELOG.md index 45b7e40..2e7ebec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,3 +101,5 @@ * 2.10.0 -- 2026-03-03 - rds-mysql: add prefix list capability +* 2.11.0 -- 2026-03-19 + - it-windows-base: refactor to use prefix lists and a YAML file diff --git a/common/version.tf b/common/version.tf index a57e56b..ac684e2 100644 --- a/common/version.tf +++ b/common/version.tf @@ -1,3 +1,3 @@ locals { - _module_version = "2.10.0" + _module_version = "2.11.0" } diff --git a/it-windows-base/README.md b/it-windows-base/README.md index d5d5402..ac8d962 100644 --- a/it-windows-base/README.md +++ b/it-windows-base/README.md @@ -1,35 +1,15 @@ -# About it-windows-base - -This describes how to use the aws-common-security-groups submodule for it-windows-base. - -Commonly used ports and services are set up here, including ICMP, AD, RDP, NTP, DNS, SNMP, -monit, munin, iperf, netperf, NetBackup and Opsware. - -## Usage - -```hcl -module "it-windows-base" { - source = "git@github.e.it.census.gov:terraform-modules/aws-common-security-groups.git//it-windows-base" - - # name = "it-windows-base" - vpc_id = var.vpc_id - # Name, CostAllocation, and Environment are pre-set, but they can be overriden - # tags = { } -} -``` - ## Requirements | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0.0 | -| [aws](#requirement\_aws) | >= 5.0 | +| [terraform](#requirement\_terraform) | >= 1.10.0 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules @@ -43,8 +23,16 @@ module "it-windows-base" { | Name | Type | |------|------| | [aws_security_group.this_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | -| [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 | +| [aws_vpc_security_group_egress_rule.all](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | +| [aws_vpc_security_group_ingress_rule.cidr_block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | +| [aws_vpc_security_group_ingress_rule.prefix_lists](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | +| [aws_vpc_security_group_ingress_rule.security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | +| [aws_vpc_security_group_ingress_rule.self](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | 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_vpc.selected](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source | | [aws_vpc.this_vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source | @@ -54,9 +42,11 @@ module "it-windows-base" { |------|-------------|------|---------|:--------:| | [description](#input\_description) | Security Group Description | `string` | `"Windows Common Base Security Group"` | no | | [egress\_networks](#input\_egress\_networks) | List of egress networks (all ports) | `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\_security\_groups](#input\_egress\_security\_groups) | List of egress security groups (all ports) | `list(string)` | `[]` | no | | [enable\_self](#input\_enable\_self) | Enable\|Disable self full access | `bool` | `false` | no | | [ingress\_networks](#input\_ingress\_networks) | List of ingress networks for external access (not all ports) | `list(string)` |
[
"0.0.0.0/0"
]
| no | +| [ingress\_prefix\_list\_names](#input\_ingress\_prefix\_list\_names) | List of prefix list names for ingress access | `list(string)` | `[]` | no | | [ingress\_security\_groups](#input\_ingress\_security\_groups) | List of ingress security groups for all ports | `list(string)` | `[]` | no | | [name](#input\_name) | Security Group Name | `string` | `"it-windows-base"` | no | | [short\_description](#input\_short\_description) | Security Group Short Description | `string` | `"Windows"` | no | diff --git a/it-windows-base/data.tf b/it-windows-base/data.tf new file mode 120000 index 0000000..995624d --- /dev/null +++ b/it-windows-base/data.tf @@ -0,0 +1 @@ +../common/data.tf \ No newline at end of file diff --git a/it-windows-base/main.tf b/it-windows-base/main.tf index d7fd70e..ab0a358 100644 --- a/it-windows-base/main.tf +++ b/it-windows-base/main.tf @@ -1,123 +1,140 @@ -/** -* # About it-windows-base -* -* This describes how to use the aws-common-security-groups submodule for it-windows-base. -* -* Commonly used ports and services are set up here, including ICMP, AD, RDP, NTP, DNS, SNMP, -* monit, munin, iperf, netperf, NetBackup and Opsware. -* -* ## Usage -* -* ```hcl -* module "it-windows-base" { -* source = "git@github.e.it.census.gov:terraform-modules/aws-common-security-groups.git//it-windows-base" -* -* # name = "it-windows-base" -* vpc_id = var.vpc_id -* # Name, CostAllocation, and Environment are pre-set, but they can be overriden -* # tags = { } -* } -* ``` -*/ - data "aws_vpc" "this_vpc" { - count = var.use_vpc_cidr ? 1 : 0 - id = var.vpc_id + id = var.vpc_id } -data "aws_security_group" "ingress_security_groups" { - count = length(var.ingress_security_groups) - id = element(var.ingress_security_groups, count.index) -} +locals { + n_all = ["0.0.0.0/0"] -data "aws_security_group" "egress_security_groups" { - count = length(var.egress_security_groups) - id = element(var.egress_security_groups, count.index) + ingress_networks = var.ingress_networks == null ? [] : var.ingress_networks + egress_networks = var.egress_networks == null ? [] : var.egress_networks } locals { - vpc_networks = var.use_vpc_cidr ? [data.aws_vpc.this_vpc[0].cidr_block] : [] + vpc_networks = var.use_vpc_cidr ? [data.aws_vpc.this_vpc.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 ? [1] : [] - short_description = var.short_description == "" ? var.description : var.short_description + # 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) } -resource "aws_security_group" "this_security_group" { - name = local.name - description = var.description - vpc_id = var.vpc_id - # vpc_id = "${data.aws_vpc.selected.id}" - - # 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 ? local.external_ingress_networks : p.value["cidr"] +locals { + _sg = yamldecode(file("${path.module}/ports.yml")) + sg = merge(local._sg, { ingress_networks = flatten(distinct(compact(concat(local.ingress_networks, var.use_vpc_cidr ? [data.aws_vpc.this_vpc.cidr_block] : [])))) }) + sg_ingress_prefix_lists = distinct(compact([for sgr in local.sg.ingress : try(sgr.prefix_list, null)])) + sg_egress_prefix_lists = try(distinct(compact([for sgr in local.sg.egress : try(sgr.prefix_list, null)])), []) + sg_c1 = flatten([for i in local.sg.ingress : merge(i, { + key = local.sg.name, + # label = format("%v:%v:%v", local.sg.name, i.from, i.proto) + label = format("%v:%v:%v", i.from, i.to, i.proto) + # cidr_blocks = try(i.cidr_blocks, null) == "%%INCOMING%%" ? local.ingress_networks : [] + cidr_blocks = try(i.cidr_blocks, []) } - } - - # 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] + )]) + sg_cidr = flatten([for sg in local.sg_c1 : [for c in sg.cidr_blocks : merge(sg, { + cidr_label = format("%v:%v", sg.label, c) + cidr_block = c } - } - - # ingress self (list with one or zero items) - dynamic "ingress" { - for_each = local.self - iterator = sg - content { - description = "${local.short_description}: from self" - from_port = 0 - to_port = 0 - protocol = -1 - self = true + )]]) + sg_sg = flatten([for i in try(local.sg.ingress_security_groups, []) : merge(local.sg, { + key = local.sg.name, + # label = format("%v:%v", local.sg.name, i) + label = i + security_group_name = i } - } + )]) + sg_pl = flatten([for sg in local.sg_c1 : [for plk, plv in data.aws_ec2_managed_prefix_list.ingress : merge(sg, { + prefix_list_label = format("%v:%v", sg.label, plk) + prefix_list_id = plv.id + }) if try(sg.prefix_list, null) == plk + ]]) +} - # egress all - egress { - description = "${local.short_description}: All" - from_port = 0 - to_port = 0 - protocol = -1 - cidr_blocks = local.egress_networks +data "aws_ec2_managed_prefix_list" "ingress" { + for_each = toset(local.sg_ingress_prefix_lists) + filter { + name = "prefix-list-name" + values = [each.key] } +} - # 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] - } +data "aws_ec2_managed_prefix_list" "egress" { + for_each = toset(local.sg_egress_prefix_lists) + filter { + name = "prefix-list-name" + values = [each.key] } +} + +# create group with just egress. Add all ingress via secondary resource +#resource "aws_security_group" "sg" { +resource "aws_security_group" "this_security_group" { + # for_each = { for sg in local.sg: sg.name => sg } + # name = format("%v-%v", var.name_prefix, each.key) + name = local.sg.name + # description = trimspace(format("%v %v", var.description_prefix, each.value.description)) + description = trimspace(local.sg.description) + vpc_id = var.vpc_id tags = merge( + # local.base_tags, 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])) - } + # { "Name" = format("sg-%v-%v", var.name_prefix, each.key) } + { "Name" = format("sg-%v", local.sg.name) } ) } + +# egress: all +resource "aws_vpc_security_group_egress_rule" "all" { + # for_each = { for k, v in local.sg : k => aws_security_group.this_security_group[k].id } + # for_each = { for k, v in local.sg : k => aws_security_group.this_security_group.id } + + security_group_id = aws_security_group.this_security_group.id + description = "ALL" + ip_protocol = -1 + # cidr_block = local.egress_networks + cidr_ipv4 = local.n_all[0] +} + + +# ingress: self +resource "aws_vpc_security_group_ingress_rule" "self" { + for_each = try(local.sg.self, false) ? { (local.sg.name) = aws_security_group.this_security_group.id } : {} + + security_group_id = each.value + description = "self" + ip_protocol = -1 + referenced_security_group_id = each.value +} + +# ingress: by security_group +resource "aws_vpc_security_group_ingress_rule" "security_group" { + for_each = { for x in local.sg_sg : x.label => x } + + security_group_id = aws_security_group.this_security_group.id + description = "self" + ip_protocol = -1 + referenced_security_group_id = aws_security_group.this_security_group.id +} + +# ingress: by cidr_block +resource "aws_vpc_security_group_ingress_rule" "cidr_block" { + for_each = { for x in local.sg_cidr : x.cidr_label => x } + + security_group_id = aws_security_group.this_security_group.id + description = each.value.short + from_port = each.value.from + to_port = each.value.to + ip_protocol = each.value.proto + cidr_ipv4 = each.value.cidr_block +} + +# ingress: by prefix_list +resource "aws_vpc_security_group_ingress_rule" "prefix_lists" { + for_each = { for x in local.sg_pl : x.prefix_list_label => x } + + security_group_id = aws_security_group.this_security_group.id + description = each.value.short + from_port = each.value.from + to_port = each.value.to + ip_protocol = each.value.proto + prefix_list_id = each.value.prefix_list_id +} diff --git a/it-windows-base/main.tf.old b/it-windows-base/main.tf.old new file mode 100644 index 0000000..d7fd70e --- /dev/null +++ b/it-windows-base/main.tf.old @@ -0,0 +1,123 @@ +/** +* # About it-windows-base +* +* This describes how to use the aws-common-security-groups submodule for it-windows-base. +* +* Commonly used ports and services are set up here, including ICMP, AD, RDP, NTP, DNS, SNMP, +* monit, munin, iperf, netperf, NetBackup and Opsware. +* +* ## Usage +* +* ```hcl +* module "it-windows-base" { +* source = "git@github.e.it.census.gov:terraform-modules/aws-common-security-groups.git//it-windows-base" +* +* # name = "it-windows-base" +* vpc_id = var.vpc_id +* # Name, CostAllocation, and Environment are pre-set, but they can be overriden +* # tags = { } +* } +* ``` +*/ + +data "aws_vpc" "this_vpc" { + count = var.use_vpc_cidr ? 1 : 0 + id = var.vpc_id +} + +data "aws_security_group" "ingress_security_groups" { + count = length(var.ingress_security_groups) + id = element(var.ingress_security_groups, count.index) +} + +data "aws_security_group" "egress_security_groups" { + count = length(var.egress_security_groups) + id = element(var.egress_security_groups, count.index) +} + +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 ? [1] : [] + short_description = var.short_description == "" ? var.description : var.short_description +} + +resource "aws_security_group" "this_security_group" { + name = local.name + description = var.description + vpc_id = var.vpc_id + # vpc_id = "${data.aws_vpc.selected.id}" + + # 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 ? local.external_ingress_networks : p.value["cidr"] + } + } + + # 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 (list with one or zero items) + dynamic "ingress" { + for_each = local.self + iterator = sg + content { + description = "${local.short_description}: from self" + from_port = 0 + to_port = 0 + protocol = -1 + self = true + } + } + + # egress all + egress { + description = "${local.short_description}: All" + from_port = 0 + to_port = 0 + protocol = -1 + cidr_blocks = local.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] + } + } + + 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/it-windows-base/ports.tf b/it-windows-base/ports.tf index 7294548..86dc6c1 100644 --- a/it-windows-base/ports.tf +++ b/it-windows-base/ports.tf @@ -17,7 +17,7 @@ ## this adds iperf3 locals { - n_all = ["0.0.0.0/0"] + # n_all = ["0.0.0.0/0"] n_census = ["148.129.0.0/16", "192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8"] n_mgmt = ["148.129.162.0/24", "148.129.95.0/24"] n_backup = ["10.193.0.0/22"] @@ -49,8 +49,8 @@ locals { ] # these are ignored - ingress_networks = var.ingress_networks - egress_networks = var.egress_networks + # ingress_networks = var.ingress_networks + # egress_networks = var.egress_networks # these are ignored ingress_sg = var.ingress_security_groups diff --git a/it-windows-base/ports.yml b/it-windows-base/ports.yml new file mode 100644 index 0000000..692cf7e --- /dev/null +++ b/it-windows-base/ports.yml @@ -0,0 +1,90 @@ +## network-management.base: +## riverbed.base: +## ansible.base: +## encase.base: +## servicenow.base: +## +## all 0/0 +## all-networks.core +## + +name: it-windows-base +description: "Windows Common Base Security Group" +self: false +egress: +egress_security_groups: [] +ingress_security_groups: [] +ingress: + - from: -1 + to: -1 + proto: icmp + short: "ICMP" +# all: true + cidr_blocks: + - 0.0.0.0/0 + - from: 161 + to: 161 + proto: udp + short: "SNMP" + # all: true + cidr_blocks: + - 0.0.0.0/0 + - from: 5201 + to: 5201 + proto: udp + short: "iperf3" + # all: true + cidr_blocks: + - 0.0.0.0/0 + - from: 5201 + to: 5201 + proto: tcp + short: "iperf3" + # all: true + cidr_blocks: + - 0.0.0.0/0 + - from: 1556 + to: 1556 + proto: tcp + short: "NetBackup" + prefix_list: netbackup.base + - from: 13724 + to: 13724 + proto: tcp + short: "NetBackup" + prefix_list: netbackup.base + - from: 13782 + to: 13782 + proto: tcp + short: "NetBackup" + prefix_list: netbackup.base + - from: 3389 + to: 3389 + proto: tcp + short: "RDP" + prefix_list: all-networks.core + - from: 4445 + to: 4445 + proto: tcp + short: "EnCase" + prefix_list: encase.base + - from: 5986 + to: 5986 + proto: tcp + short: "WinRM" + prefix_list: ansible.base + - from: 27401 + to: 27401 + proto: tcp + short: "TransactionAgent" + prefix_list: riverbed.base + - from: 135 + to: 135 + proto: tcp + short: "SNow Discovery" + prefix_list: servicenow.base + - from: 1024 + to: 65535 + proto: tcp + short: "SNow Discovery" + prefix_list: servicenow.base diff --git a/it-windows-base/variables.prefix_lists.tf b/it-windows-base/variables.prefix_lists.tf new file mode 120000 index 0000000..bba5d4d --- /dev/null +++ b/it-windows-base/variables.prefix_lists.tf @@ -0,0 +1 @@ +../common//variables.prefix_lists.tf \ No newline at end of file