Skip to content

Commit f45df3e

Browse files
authored
Merge pull request #165 from ivan-aws/main
feat: sort subnets to calculate by their netmask to efficiently use ip space + feature flag and tests
2 parents d8376b1 + e214f02 commit f45df3e

File tree

12 files changed

+197
-3
lines changed

12 files changed

+197
-3
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Creating subnets with sorted CIDR ranges
2+
3+
This example shows how you can use this module to efficiently use the VPC CIDR range. Before calculating CIDR ranges, subnets are sorted from largest to smallest.
4+
5+
* The VPC module creates the following:
6+
* Four sets of subnets (*public*, *private*, *database*, and *infrastructure*)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
formatter: markdown
2+
header-from: .header.md
3+
settings:
4+
anchor: true
5+
color: true
6+
default: true
7+
escape: true
8+
html: true
9+
indent: 2
10+
required: true
11+
sensitive: true
12+
type: true
13+
lockfile: false
14+
15+
sort:
16+
enabled: true
17+
by: required
18+
19+
output:
20+
file: README.md
21+
mode: replace
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!-- BEGIN_TF_DOCS -->
2+
# Creating subnets with sorted CIDR ranges
3+
4+
This example shows how you can use this module to efficiently use the VPC CIDR range. Before calculating CIDR ranges, subnets are sorted from largest to smallest.
5+
6+
* The VPC module creates the following:
7+
* Four sets of subnets (*public*, *private*, *database*, and *infrastructure*)
8+
9+
## Requirements
10+
11+
| Name | Version |
12+
|------|---------|
13+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.9 |
14+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.0 |
15+
16+
## Providers
17+
18+
| Name | Version |
19+
|------|---------|
20+
| <a name="provider_aws"></a> [aws](#provider\_aws) | ~> 5.0 |
21+
22+
## Modules
23+
24+
| Name | Source | Version |
25+
|------|--------|---------|
26+
| <a name="module_vpc"></a> [vpc](#module\_vpc) | aws-ia/vpc/aws | ~> 4.4 |
27+
28+
## Resources
29+
30+
| Name | Type |
31+
|------|------|
32+
| [aws_availability_zones.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
33+
34+
## Inputs
35+
36+
| Name | Description | Type | Default | Required |
37+
|------|-------------|------|---------|:--------:|
38+
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | AWS Region to create resources in. | `string` | `"eu-west-2"` | no |
39+
| <a name="input_vpc_cidr"></a> [vpc\_cidr](#input\_vpc\_cidr) | VPC cidr range | `string` | `"10.0.0.0/22"` | no |
40+
41+
## Outputs
42+
43+
| Name | Description |
44+
|------|-------------|
45+
| <a name="output_private_subnets_tags_length"></a> [private\_subnets\_tags\_length](#output\_private\_subnets\_tags\_length) | Count of private subnet tags for a single az. |
46+
<!-- END_TF_DOCS -->
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
data "aws_availability_zones" "current" {
2+
filter {
3+
name = "opt-in-status"
4+
values = ["opt-in-not-required"]
5+
}
6+
}
7+
8+
module "vpc" {
9+
source = "aws-ia/vpc/aws"
10+
version = "~> 4.4"
11+
12+
name = "sorted-subnets"
13+
cidr_block = var.vpc_cidr
14+
az_count = 3
15+
optimize_subnet_cidr_ranges = true
16+
17+
subnets = {
18+
private = {
19+
netmask = 24
20+
}
21+
database = {
22+
netmask = 27
23+
}
24+
public = {
25+
netmask = 28
26+
nat_gateway_configuration = "single_az"
27+
}
28+
infrastructure = {
29+
netmask = 28
30+
}
31+
}
32+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
## Used for Testing, do not delete
2+
3+
output "private_subnets_tags_length" {
4+
description = "Count of private subnet tags for a single az."
5+
value = try(length(module.vpc.private_subnet_attributes_by_az["private/${data.aws_availability_zones.current.names[0]}"].tags), null)
6+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
terraform {
2+
required_version = "~> 1.9"
3+
required_providers {
4+
aws = {
5+
source = "hashicorp/aws"
6+
version = "~> 5.0"
7+
}
8+
}
9+
}
10+
11+
# Provider definition
12+
provider "aws" {
13+
region = var.aws_region
14+
}
15+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
variable "aws_region" {
2+
description = "AWS Region to create resources in."
3+
type = string
4+
default = "eu-west-2"
5+
}
6+
7+
variable "vpc_cidr" {
8+
type = string
9+
description = "VPC cidr range"
10+
default = "10.0.0.0/22"
11+
}

main.tf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ module "calculate_subnets" {
77
cidr = local.cidr_block
88
azs = local.azs
99

10-
subnets = var.subnets
10+
subnets = var.subnets
11+
optimize_subnet_cidr_ranges = var.optimize_subnet_cidr_ranges
1112
}
1213

1314
module "calculate_subnets_ipv6" {

modules/calculate_subnets/main.tf

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,30 @@ locals {
1616
}
1717
]])
1818

19+
# map of subnet names to netmask values for looking up netmask by name
20+
netmasks_for_subnets = { for subnet in local.calculated_subnet_objects : subnet.name => subnet.netmask }
21+
22+
# sorted list of netmasks from largest to smallest so we can efficiently use the ip address space
23+
sorted_subnet_netmasks = reverse(distinct(sort([
24+
for subnet in local.calculated_subnet_objects : format("%05d", subnet.netmask)
25+
])))
26+
27+
# list of subnet names sorted based on their netmask value (large to small)
28+
sorted_subnet_names = compact(flatten([
29+
for netmask in local.sorted_subnet_netmasks : [
30+
for subnet in local.calculated_subnet_objects :
31+
subnet.name if subnet.netmask == tonumber(netmask)
32+
]
33+
]))
34+
35+
# list of subnet the original calculated subnet objects, but sorted based on their netmask value (large to small)
36+
sorted_subnet_objects = [
37+
for name in local.sorted_subnet_names : {
38+
name = name
39+
netmask = local.netmasks_for_subnets[name]
40+
}
41+
]
42+
1943
# map of explicit cidrs to az
2044
explicit_cidrs_grouped = { for _, type in local.types_with_explicit : type => zipmap(var.azs, var.subnets[type].cidrs[*]) }
2145
}
@@ -27,6 +51,5 @@ module "subnet_calculator" {
2751
version = "1.0.2"
2852

2953
base_cidr_block = var.cidr
30-
networks = local.calculated_subnet_objects
54+
networks = var.optimize_subnet_cidr_ranges ? local.sorted_subnet_objects : local.calculated_subnet_objects
3155
}
32-

modules/calculate_subnets/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,9 @@ variable "cidr" {
1212
description = "CIDR value to use as base for calculating IP address prefixes."
1313
type = string
1414
}
15+
16+
variable "optimize_subnet_cidr_ranges" {
17+
description = "Sort subnets to calculate by their netmask to efficiently use IP space."
18+
type = bool
19+
default = false
20+
}

0 commit comments

Comments
 (0)