Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ state/
*.tfplan
*.tfstate
*.tfstate.backup

# Test run files
spec/integration/test_runs/**/*
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
## Unreleased

BACKWARDS INCOMPATIBILITIES / NOTES:

* `for_each` is used instead of `count` for creating resources for each
availability zone, so the availability zone will be used as the resource key,
not an index.

As a consequence, if resources have been created with a
previous version of this module, they will need to be `moved` to avoid them
being destroyed and recreated.

e.g.

```terraform
moved {
from = module.base-network.aws_subnet.public[0]
to = module.base-network.aws_subnet.public["eu-west-1a"]
}

moved {
from = module.base-network.aws_subnet.private[0]
to = module.base-network.aws_subnet.private["eu-west-1a"]
}

# etc..
```


* The default value for the `private_subnets_offset` variable has been changed
from 0 to 128. This means that if the offsets are not provided, there will
be sufficient space between the private and public CIDR blocks such that new
availability_zones can be added without needing to destroy existing private
subnets.

ENHANCEMENTS

* As an alternative to the `availability_zones` variable, an
`availability_zone_configuration` variable is also supported, which takes a
list of objects with the keys `zone`, `public_subnet_cidr` and
`private_subnet_cidr`. This can be useful in cases where a new availability
zone is being added, but inference of the CIDR blocks could result in existing
subnets being destroyed and recreated, in which case the existing public and
private subnet CIDRs can be explicitly supplied to prevent this.

## 5.1.0 (13th Feb 2023)

ENHANCEMENTS
Expand Down
23 changes: 23 additions & 0 deletions defaults.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,27 @@ locals {
private_subnets_offset = var.private_subnets_offset == null ? 0 : var.private_subnets_offset
include_route53_zone_association = var.include_route53_zone_association == null ? "yes" : var.include_route53_zone_association
include_nat_gateways = var.include_nat_gateways == null ? "yes" : var.include_nat_gateways

# Validation: ensure only one of availability_zones or availability_zone_configurations is provided
_validate_az_input = (
(var.availability_zones == null && var.availability_zone_configurations == null) ||
(var.availability_zones != null && var.availability_zone_configurations != null)
) ? tobool("ERROR: Exactly one of availability_zones or availability_zone_configurations must be specified.") : true

# Normalize AZ configuration to a consistent format
# If availability_zone_configurations is provided, use it directly
# Otherwise, generate configurations from availability_zones list
az_configurations = var.availability_zone_configurations != null ? var.availability_zone_configurations : [
for idx, az in var.availability_zones : {
zone = az
public_subnet_cidr = cidrsubnet(var.vpc_cidr, 8, idx + local.public_subnets_offset)
private_subnet_cidr = cidrsubnet(var.vpc_cidr, 8, idx + local.private_subnets_offset)
}
]

# Create a map keyed by AZ name for easy lookup in resources
az_map = { for config in local.az_configurations : config.zone => config }

# List of AZ names for outputs and other references
availability_zones = [for config in local.az_configurations : config.zone]
}
4 changes: 2 additions & 2 deletions examples/full/base_networking.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ module "base_networking" {
component = var.component
deployment_identifier = var.deployment_identifier

region = var.region
region = var.region
availability_zones = var.availability_zones
vpc_cidr = var.vpc_cidr
vpc_cidr = var.vpc_cidr

dependencies = ["other_vpc_1", "other_vpc_2"]

Expand Down
8 changes: 4 additions & 4 deletions examples/full/prerequisites.tf
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
resource "aws_default_vpc" "default" {}

module "dns_zones" {
source = "infrablocks/dns-zones/aws"
source = "infrablocks/dns-zones/aws"
version = "2.0.0"

domain_name = var.domain_name
private_domain_name = var.domain_name
private_zone_vpc_id = aws_default_vpc.default.id
domain_name = var.domain_name
private_domain_name = var.domain_name
private_zone_vpc_id = aws_default_vpc.default.id
private_zone_vpc_region = var.region
}
2 changes: 1 addition & 1 deletion examples/full/terraform.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ terraform {

required_providers {
aws = {
source = "hashicorp/aws"
source = "hashicorp/aws"
version = "4.33"
}
}
Expand Down
6 changes: 3 additions & 3 deletions igw.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ resource "aws_internet_gateway" "base_igw" {
vpc_id = aws_vpc.base.id

tags = {
Name = "igw-${var.component}-${var.deployment_identifier}"
Component = var.component
Name = "igw-${var.component}-${var.deployment_identifier}"
Component = var.component
DeploymentIdentifier = var.deployment_identifier
Tier = "public"
Tier = "public"
}
}
16 changes: 8 additions & 8 deletions nat.tf
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
resource "aws_eip" "nat" {
count = local.include_nat_gateways == "yes" ? length(var.availability_zones) : 0
for_each = local.include_nat_gateways == "yes" ? local.az_map : {}

vpc = true

tags = {
Name = "eip-nat-${var.component}-${var.deployment_identifier}-${element(var.availability_zones, count.index)}"
Component = var.component
Name = "eip-nat-${var.component}-${var.deployment_identifier}-${each.value.zone}"
Component = var.component
DeploymentIdentifier = var.deployment_identifier
}
}

resource "aws_nat_gateway" "base" {
count = local.include_nat_gateways == "yes" ? length(var.availability_zones) : 0
for_each = local.include_nat_gateways == "yes" ? local.az_map : {}

allocation_id = element(aws_eip.nat.*.id, count.index)
subnet_id = element(aws_subnet.public.*.id, count.index)
allocation_id = aws_eip.nat[each.key].id
subnet_id = aws_subnet.public[each.key].id

depends_on = [
aws_internet_gateway.base_igw
]

tags = {
Name = "nat-${var.component}-${var.deployment_identifier}-${element(var.availability_zones, count.index)}"
Component = var.component
Name = "nat-${var.component}-${var.deployment_identifier}-${each.value.zone}"
Component = var.component
DeploymentIdentifier = var.deployment_identifier
}
}
24 changes: 12 additions & 12 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -1,59 +1,59 @@
output "vpc_id" {
description = "The ID of the created VPC."
value = aws_vpc.base.id
value = aws_vpc.base.id
}

output "vpc_cidr" {
description = "The CIDR of the created VPC."
value = aws_vpc.base.cidr_block
value = aws_vpc.base.cidr_block
}

output "availability_zones" {
description = "The availability zones in which subnets were created."
value = var.availability_zones
value = local.availability_zones
}

output "number_of_availability_zones" {
description = "The number of populated availability zones available."
value = length(var.availability_zones)
value = length(local.availability_zones)
}

output "public_subnet_ids" {
description = "The IDs of the public subnets."
value = aws_subnet.public.*.id
value = [for az in local.availability_zones : aws_subnet.public[az].id]
}

output "public_subnet_cidr_blocks" {
description = "The CIDRs of the public subnets."
value = aws_subnet.public.*.cidr_block
value = [for az in local.availability_zones : aws_subnet.public[az].cidr_block]
}

output "public_route_table_ids" {
description = "The IDs of the public route tables."
value = aws_route_table.public.*.id
value = [for az in local.availability_zones : aws_route_table.public[az].id]
}

output "private_subnet_ids" {
description = "The IDs of the private subnets."
value = aws_subnet.private.*.id
value = [for az in local.availability_zones : aws_subnet.private[az].id]
}

output "private_subnet_cidr_blocks" {
description = "The CIDRs of the private subnets."
value = aws_subnet.private.*.cidr_block
value = [for az in local.availability_zones : aws_subnet.private[az].cidr_block]
}

output "private_route_table_ids" {
description = "The IDs of the private route tables."
value = aws_route_table.private.*.id
value = [for az in local.availability_zones : aws_route_table.private[az].id]
}

output "nat_public_ips" {
description = "The EIPs attached to the NAT gateways."
value = aws_eip.nat.*.public_ip
value = local.include_nat_gateways == "yes" ? [for az in local.availability_zones : aws_eip.nat[az].public_ip] : []
}

output "internet_gateway_id" {
description = "The ID of IGW attached to the VPC."
value = aws_internet_gateway.base_igw.id
value = aws_internet_gateway.base_igw.id
}
38 changes: 21 additions & 17 deletions private_subnets.tf
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
resource "aws_subnet" "private" {
vpc_id = aws_vpc.base.id
count = length(var.availability_zones)
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + length(var.availability_zones) + local.private_subnets_offset)
availability_zone = element(var.availability_zones, count.index)
for_each = local.az_map

vpc_id = aws_vpc.base.id
cidr_block = each.value.private_subnet_cidr
availability_zone = each.value.zone

tags = {
Name = "private-subnet-${var.component}-${var.deployment_identifier}-${element(var.availability_zones, count.index)}"
Component = var.component
Name = "private-subnet-${var.component}-${var.deployment_identifier}-${each.value.zone}"
Component = var.component
DeploymentIdentifier = var.deployment_identifier
Tier = "private"
Tier = "private"
}
}

resource "aws_route_table" "private" {
for_each = local.az_map

vpc_id = aws_vpc.base.id
count = length(var.availability_zones)

tags = {
Name = "private-routetable-${var.component}-${var.deployment_identifier}-${element(var.availability_zones, count.index)}"
Component = var.component
Name = "private-routetable-${var.component}-${var.deployment_identifier}-${each.value.zone}"
Component = var.component
DeploymentIdentifier = var.deployment_identifier
Tier = "private"
Tier = "private"
}
}

resource "aws_route" "private_internet" {
count = local.include_nat_gateways == "yes" ? length(var.availability_zones) : 0
route_table_id = element(aws_route_table.private.*.id, count.index)
nat_gateway_id = element(aws_nat_gateway.base.*.id, count.index)
for_each = local.include_nat_gateways == "yes" ? local.az_map : {}

route_table_id = aws_route_table.private[each.key].id
nat_gateway_id = aws_nat_gateway.base[each.key].id
destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "private" {
count = length(var.availability_zones)
subnet_id = element(aws_subnet.private.*.id, count.index)
route_table_id = element(aws_route_table.private.*.id, count.index)
for_each = local.az_map

subnet_id = aws_subnet.private[each.key].id
route_table_id = aws_route_table.private[each.key].id
}
38 changes: 21 additions & 17 deletions public_subnets.tf
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
resource "aws_subnet" "public" {
vpc_id = aws_vpc.base.id
count = length(var.availability_zones)
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + local.public_subnets_offset)
availability_zone = element(var.availability_zones, count.index)
for_each = local.az_map

vpc_id = aws_vpc.base.id
cidr_block = each.value.public_subnet_cidr
availability_zone = each.value.zone

tags = {
Name = "public-subnet-${var.component}-${var.deployment_identifier}-${element(var.availability_zones, count.index)}"
Component = var.component
Name = "public-subnet-${var.component}-${var.deployment_identifier}-${each.value.zone}"
Component = var.component
DeploymentIdentifier = var.deployment_identifier
Tier = "public"
Tier = "public"
}
}

resource "aws_route_table" "public" {
for_each = local.az_map

vpc_id = aws_vpc.base.id
count = length(var.availability_zones)

tags = {
Name = "public-routetable-${var.component}-${var.deployment_identifier}-${element(var.availability_zones, count.index)}"
Component = var.component
Name = "public-routetable-${var.component}-${var.deployment_identifier}-${each.value.zone}"
Component = var.component
DeploymentIdentifier = var.deployment_identifier
Tier = "public"
Tier = "public"
}
}

resource "aws_route" "public_internet" {
count = length(var.availability_zones)
route_table_id = element(aws_route_table.public.*.id, count.index)
gateway_id = aws_internet_gateway.base_igw.id
for_each = local.az_map

route_table_id = aws_route_table.public[each.key].id
gateway_id = aws_internet_gateway.base_igw.id
destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "public" {
count = length(var.availability_zones)
subnet_id = element(aws_subnet.public.*.id, count.index)
route_table_id = element(aws_route_table.public.*.id, count.index)
for_each = local.az_map

subnet_id = aws_subnet.public[each.key].id
route_table_id = aws_route_table.public[each.key].id
}
Loading