Skip to content

Commit

Permalink
feat: Allow creation/passing secondary EIPs for NAT gateways.
Browse files Browse the repository at this point in the history
  • Loading branch information
tristal committed Jul 4, 2024
1 parent 4a2809c commit b4fbdad
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 1 deletion.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ No modules.
| [aws_default_vpc.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_vpc) | resource |
| [aws_egress_only_internet_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/egress_only_internet_gateway) | resource |
| [aws_eip.nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_eip.nat_secondary](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_elasticache_subnet_group.elasticache](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_subnet_group) | resource |
| [aws_flow_log.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_iam_policy.vpc_flow_log_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
Expand Down Expand Up @@ -446,6 +447,9 @@ No modules.
| <a name="input_enable_vpn_gateway"></a> [enable\_vpn\_gateway](#input\_enable\_vpn\_gateway) | Should be true if you want to create a new VPN Gateway resource and attach it to the VPC | `bool` | `false` | no |
| <a name="input_external_nat_ip_ids"></a> [external\_nat\_ip\_ids](#input\_external\_nat\_ip\_ids) | List of EIP IDs to be assigned to the NAT Gateways (used in combination with reuse\_nat\_ips) | `list(string)` | `[]` | no |
| <a name="input_external_nat_ips"></a> [external\_nat\_ips](#input\_external\_nat\_ips) | List of EIPs to be used for `nat_public_ips` output (used in combination with reuse\_nat\_ips and external\_nat\_ip\_ids) | `list(string)` | `[]` | no |
| <a name="input_external_nat_secondary_ip_count"></a> [external\_nat\_secondary\_ip\_count](#input\_external\_nat\_secondary\_ip\_count) | Number of secondary IPs to assign to the NAT Gateway | `list(number)` | <pre>[<br> 0<br>]</pre> | no |
| <a name="input_external_nat_secondary_ip_ids"></a> [external\_nat\_secondary\_ip\_ids](#input\_external\_nat\_secondary\_ip\_ids) | List of EIP IDs to be assigned to the NAT Gateways as secondary IPs (used in combination with reuse\_external\_nat\_secondary\_ips) | `list(list(string))` | `[]` | no |
| <a name="input_external_nat_secondary_ips"></a> [external\_nat\_secondary\_ips](#input\_external\_nat\_secondary\_ips) | List of EIPs to be used for `nat_public_secondary_ips` output (used in combination with reuse\_external\_nat\_secondary\_ips and external\_nat\_secondary\_ip\_ids) | `list(string)` | `[]` | no |
| <a name="input_flow_log_cloudwatch_iam_role_arn"></a> [flow\_log\_cloudwatch\_iam\_role\_arn](#input\_flow\_log\_cloudwatch\_iam\_role\_arn) | The ARN for the IAM role that's used to post flow logs to a CloudWatch Logs log group. When flow\_log\_destination\_arn is set to ARN of Cloudwatch Logs, this argument needs to be provided | `string` | `""` | no |
| <a name="input_flow_log_cloudwatch_log_group_class"></a> [flow\_log\_cloudwatch\_log\_group\_class](#input\_flow\_log\_cloudwatch\_log\_group\_class) | Specified the log class of the log group. Possible values are: STANDARD or INFREQUENT\_ACCESS | `string` | `null` | no |
| <a name="input_flow_log_cloudwatch_log_group_kms_key_id"></a> [flow\_log\_cloudwatch\_log\_group\_kms\_key\_id](#input\_flow\_log\_cloudwatch\_log\_group\_kms\_key\_id) | The ARN of the KMS Key to use when encrypting log data for VPC flow logs | `string` | `null` | no |
Expand Down Expand Up @@ -496,6 +500,7 @@ No modules.
| <a name="input_nat_eip_tags"></a> [nat\_eip\_tags](#input\_nat\_eip\_tags) | Additional tags for the NAT EIP | `map(string)` | `{}` | no |
| <a name="input_nat_gateway_destination_cidr_block"></a> [nat\_gateway\_destination\_cidr\_block](#input\_nat\_gateway\_destination\_cidr\_block) | Used to pass a custom destination route for private NAT Gateway. If not specified, the default 0.0.0.0/0 is used as a destination route | `string` | `"0.0.0.0/0"` | no |
| <a name="input_nat_gateway_tags"></a> [nat\_gateway\_tags](#input\_nat\_gateway\_tags) | Additional tags for the NAT gateways | `map(string)` | `{}` | no |
| <a name="input_nat_secondary_eip_tags"></a> [nat\_secondary\_eip\_tags](#input\_nat\_secondary\_eip\_tags) | Additional tags for the NAT secondary EIP's | `map(string)` | `{}` | no |
| <a name="input_one_nat_gateway_per_az"></a> [one\_nat\_gateway\_per\_az](#input\_one\_nat\_gateway\_per\_az) | Should be true if you want only one NAT Gateway per availability zone. Requires `var.azs` to be set, and the number of `public_subnets` created to be greater than or equal to the number of availability zones specified in `var.azs` | `bool` | `false` | no |
| <a name="input_outpost_acl_tags"></a> [outpost\_acl\_tags](#input\_outpost\_acl\_tags) | Additional tags for the outpost subnets network ACL | `map(string)` | `{}` | no |
| <a name="input_outpost_arn"></a> [outpost\_arn](#input\_outpost\_arn) | ARN of Outpost you want to create a subnet in | `string` | `null` | no |
Expand Down Expand Up @@ -570,6 +575,7 @@ No modules.
| <a name="input_redshift_subnet_suffix"></a> [redshift\_subnet\_suffix](#input\_redshift\_subnet\_suffix) | Suffix to append to redshift subnets name | `string` | `"redshift"` | no |
| <a name="input_redshift_subnet_tags"></a> [redshift\_subnet\_tags](#input\_redshift\_subnet\_tags) | Additional tags for the redshift subnets | `map(string)` | `{}` | no |
| <a name="input_redshift_subnets"></a> [redshift\_subnets](#input\_redshift\_subnets) | A list of redshift subnets inside the VPC | `list(string)` | `[]` | no |
| <a name="input_reuse_external_nat_secondary_ips"></a> [reuse\_external\_nat\_secondary\_ips](#input\_reuse\_external\_nat\_secondary\_ips) | Should be true if you don't want secondary IPs to be created for your NAT Gateways and will instead pass them in via the 'external\_nat\_secondary\_ip\_ids' variable | `bool` | `false` | no |
| <a name="input_reuse_nat_ips"></a> [reuse\_nat\_ips](#input\_reuse\_nat\_ips) | Should be true if you don't want EIPs to be created for your NAT Gateways and will instead pass them in via the 'external\_nat\_ip\_ids' variable | `bool` | `false` | no |
| <a name="input_secondary_cidr_blocks"></a> [secondary\_cidr\_blocks](#input\_secondary\_cidr\_blocks) | List of secondary CIDR blocks to associate with the VPC to extend the IP Address pool | `list(string)` | `[]` | no |
| <a name="input_single_nat_gateway"></a> [single\_nat\_gateway](#input\_single\_nat\_gateway) | Should be true if you want to provision a single shared NAT Gateway across all of your private networks | `bool` | `false` | no |
Expand Down Expand Up @@ -640,6 +646,7 @@ No modules.
| <a name="output_name"></a> [name](#output\_name) | The name of the VPC specified as argument to this module |
| <a name="output_nat_ids"></a> [nat\_ids](#output\_nat\_ids) | List of allocation ID of Elastic IPs created for AWS NAT Gateway |
| <a name="output_nat_public_ips"></a> [nat\_public\_ips](#output\_nat\_public\_ips) | List of public Elastic IPs created for AWS NAT Gateway |
| <a name="output_nat_public_secondary_ips"></a> [nat\_public\_secondary\_ips](#output\_nat\_public\_secondary\_ips) | List of secondary public Elastic IPs created for AWS NAT Gateway |
| <a name="output_natgw_ids"></a> [natgw\_ids](#output\_natgw\_ids) | List of NAT Gateway IDs |
| <a name="output_natgw_interface_ids"></a> [natgw\_interface\_ids](#output\_natgw\_interface\_ids) | List of Network Interface IDs assigned to NAT Gateways |
| <a name="output_outpost_network_acl_arn"></a> [outpost\_network\_acl\_arn](#output\_outpost\_network\_acl\_arn) | ARN of the outpost network ACL |
Expand Down
51 changes: 51 additions & 0 deletions examples/nat-secondary-eips/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Simple VPC with secondary CIDR blocks

Configuration in this directory creates set of VPC resources across multiple CIDR blocks.

There is a public and private subnet created per availability zone in addition to single NAT Gateway shared between all 3 availability zones.

## Usage

To run this example you need to execute:

```bash
$ terraform init
$ terraform plan
$ terraform apply
```

Note that this example may create resources which can cost money (AWS Elastic IP, for example). Run `terraform destroy` when you don't need these resources.

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.30 |

## Providers

No providers.

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_vpc"></a> [vpc](#module\_vpc) | ../../ | n/a |

## Resources

No resources.

## Inputs

No inputs.

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_nat_public_secondary_ips"></a> [nat\_public\_secondary\_ips](#output\_nat\_public\_secondary\_ips) | The secondary public IPs of the NAT gateways |
| <a name="output_vpc_id"></a> [vpc\_id](#output\_vpc\_id) | The ID of the VPC |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
40 changes: 40 additions & 0 deletions examples/nat-secondary-eips/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
provider "aws" {
region = local.region
}

locals {
name = "ex-${basename(path.cwd)}"
region = "eu-west-1"

vpc_cidr = "10.0.0.0/16"

tags = {
Example = local.name
GithubRepo = "terraform-aws-vpc"
GithubOrg = "terraform-aws-modules"
}
}

################################################################################
# VPC Module
################################################################################

module "vpc" {
source = "../../"

name = local.name
cidr = local.vpc_cidr

enable_nat_gateway = true

# create two secondary EIPs for the NAT gateway
external_nat_secondary_ip_count = [2]

# or, if you want to specify the EIPs to use
# reuse_external_nat_secondary_ips = true
# external_nat_secondary_ip_ids = [
# ["eip-12345678", "eip-87654321"]
# ]

tags = local.tags
}
9 changes: 9 additions & 0 deletions examples/nat-secondary-eips/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "vpc_id" {
description = "The ID of the VPC"
value = module.vpc.vpc_id
}

output "nat_public_secondary_ips" {
description = "The secondary public IPs of the NAT gateways"
value = module.vpc.nat_public_secondary_ips
}
Empty file.
10 changes: 10 additions & 0 deletions examples/nat-secondary-eips/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.30"
}
}
}
38 changes: 37 additions & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,19 @@ resource "aws_route" "private_ipv6_egress" {
locals {
nat_gateway_count = var.single_nat_gateway ? 1 : var.one_nat_gateway_per_az ? length(var.azs) : local.max_subnet_length
nat_gateway_ips = var.reuse_nat_ips ? var.external_nat_ip_ids : aws_eip.nat[*].id
nat_gateway_secondary_eip_specs = toset(flatten([
for nat_idx, counts in var.external_nat_secondary_ip_count : [
for eip_idx in range(counts) : {
nat_idx = nat_idx
eip_idx = eip_idx
}
]
]))
nat_gateway_secondary_eip_specs_map = {
for spec in local.nat_gateway_secondary_eip_specs :
"${spec.nat_idx}-${spec.eip_idx}" => spec
}
nat_gateway_secondary_allocation_ids = var.reuse_external_nat_secondary_ips && length(var.external_nat_secondary_ip_ids) > 0 ? var.external_nat_secondary_ip_ids : [[for eip in aws_eip.nat_secondary : eip.allocation_id]]
}

resource "aws_eip" "nat" {
Expand All @@ -1074,6 +1087,27 @@ resource "aws_eip" "nat" {
depends_on = [aws_internet_gateway.this]
}

resource "aws_eip" "nat_secondary" {
for_each = local.create_vpc && var.enable_nat_gateway && !var.reuse_external_nat_secondary_ips ? local.nat_gateway_secondary_eip_specs_map : {}

domain = "vpc"

tags = merge(
{
"Name" = format(
"${var.name}-secondary-nat-%s-eip-%s",
each.value.nat_idx,
each.value.eip_idx
),
"NATGatewayIndex" = each.value.nat_idx,
},
var.tags,
var.nat_secondary_eip_tags,
)

depends_on = [aws_internet_gateway.this]
}

resource "aws_nat_gateway" "this" {
count = local.create_vpc && var.enable_nat_gateway ? local.nat_gateway_count : 0

Expand All @@ -1086,6 +1120,8 @@ resource "aws_nat_gateway" "this" {
var.single_nat_gateway ? 0 : count.index,
)

secondary_allocation_ids = length(local.nat_gateway_secondary_allocation_ids) > 0 ? element(local.nat_gateway_secondary_allocation_ids, count.index) : []

tags = merge(
{
"Name" = format(
Expand All @@ -1097,7 +1133,7 @@ resource "aws_nat_gateway" "this" {
var.nat_gateway_tags,
)

depends_on = [aws_internet_gateway.this]
depends_on = [aws_internet_gateway.this, aws_eip.nat, aws_eip.nat_secondary]
}

resource "aws_route" "private_nat_gateway" {
Expand Down
5 changes: 5 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,11 @@ output "nat_public_ips" {
value = var.reuse_nat_ips ? var.external_nat_ips : aws_eip.nat[*].public_ip
}

output "nat_public_secondary_ips" {
description = "List of secondary public Elastic IPs created for AWS NAT Gateway"
value = var.reuse_external_nat_secondary_ips ? var.external_nat_secondary_ips : length(local.nat_gateway_secondary_eip_specs) > 0 ? [for eip in aws_eip.nat_secondary : eip.public_ip] : []
}

output "natgw_ids" {
description = "List of NAT Gateway IDs"
value = aws_nat_gateway.this[*].id
Expand Down
30 changes: 30 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,30 @@ variable "external_nat_ips" {
default = []
}

variable "external_nat_secondary_ip_count" {
description = "Number of secondary IPs to assign to the NAT Gateway"
type = list(number)
default = [0]
}

variable "reuse_external_nat_secondary_ips" {
description = "Should be true if you don't want secondary IPs to be created for your NAT Gateways and will instead pass them in via the 'external_nat_secondary_ip_ids' variable"
type = bool
default = false
}

variable "external_nat_secondary_ip_ids" {
description = "List of EIP IDs to be assigned to the NAT Gateways as secondary IPs (used in combination with reuse_external_nat_secondary_ips)"
type = list(list(string))
default = []
}

variable "external_nat_secondary_ips" {
description = "List of EIPs to be used for `nat_public_secondary_ips` output (used in combination with reuse_external_nat_secondary_ips and external_nat_secondary_ip_ids)"
type = list(string)
default = []
}

variable "nat_gateway_tags" {
description = "Additional tags for the NAT gateways"
type = map(string)
Expand All @@ -1234,6 +1258,12 @@ variable "nat_eip_tags" {
default = {}
}

variable "nat_secondary_eip_tags" {
description = "Additional tags for the NAT secondary EIP's"
type = map(string)
default = {}
}

################################################################################
# Customer Gateways
################################################################################
Expand Down

0 comments on commit b4fbdad

Please sign in to comment.