From 7b55fd5f53f08fff0119844d448247e0bad36ebf Mon Sep 17 00:00:00 2001 From: "Byungjin Park (Claud)" Date: Tue, 24 Oct 2023 00:43:40 +0900 Subject: [PATCH] Improve nat-gateway (#52) --- README.md | 6 ++ .../main.tf | 84 ++++++++++++++++ .../outputs.tf | 7 ++ .../versions.tf | 10 ++ examples/nat-gateway-private/main.tf | 41 ++++++++ examples/nat-gateway-private/outputs.tf | 4 + examples/nat-gateway-private/versions.tf | 10 ++ examples/nat-gateway-public/main.tf | 57 +++++++++++ examples/nat-gateway-public/outputs.tf | 9 ++ examples/nat-gateway-public/versions.tf | 10 ++ modules/nat-gateway/README.md | 45 +++++---- modules/nat-gateway/main.tf | 59 +++++++++--- modules/nat-gateway/outputs.tf | 53 +++++++++-- modules/nat-gateway/variables.tf | 95 +++++++++++++++---- modules/nat-gateway/versions.tf | 4 +- 15 files changed, 436 insertions(+), 58 deletions(-) create mode 100644 examples/nat-gateway-private-secondary-ip-addresses/main.tf create mode 100644 examples/nat-gateway-private-secondary-ip-addresses/outputs.tf create mode 100644 examples/nat-gateway-private-secondary-ip-addresses/versions.tf create mode 100644 examples/nat-gateway-private/main.tf create mode 100644 examples/nat-gateway-private/outputs.tf create mode 100644 examples/nat-gateway-private/versions.tf create mode 100644 examples/nat-gateway-public/main.tf create mode 100644 examples/nat-gateway-public/outputs.tf create mode 100644 examples/nat-gateway-public/versions.tf diff --git a/README.md b/README.md index f6b4c09..43c4c91 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,12 @@ Terraform Modules from [this package](https://github.com/tedilabs/terraform-aws- - [vpc-simple](./examples/vpc-simple) - [vpc-with-ipam](./examples/vpc-with-ipam) +### NAT Gateway + +- [nat-gateway-public](./examples/nat-gateway-public/) +- [nat-gateway-private](./examples/nat-gateway-private/) +- [nat-gateway-private-secondary-ip-addresses](./examples/nat-gateway-private-secondary-ip-addresses) + ## Other Terraform Modules from Tedilabs diff --git a/examples/nat-gateway-private-secondary-ip-addresses/main.tf b/examples/nat-gateway-private-secondary-ip-addresses/main.tf new file mode 100644 index 0000000..3a5add1 --- /dev/null +++ b/examples/nat-gateway-private-secondary-ip-addresses/main.tf @@ -0,0 +1,84 @@ +provider "aws" { + region = "us-east-1" +} + +data "aws_vpc" "default" { + default = true +} + +data "aws_subnets" "default" { + filter { + name = "vpc-id" + values = [data.aws_vpc.default.id] + } +} + + +################################################### +# Public NAT Gateway +################################################### + +module "nat_gateway" { + source = "../../modules/nat-gateway" + # source = "tedilabs/network/aws//modules/nat-gateway" + # version = "~> 0.2.0" + + name = "test-count" + is_private = true + subnet = data.aws_subnets.default.ids[0] + + + ## Primary IP Address + primary_ip_assignment = { + # Automatically assign a public IP address to the NAT Gateway + private_ip = null + } + + + ## Secondary IP Addresses + secondary_ip_count = 7 + + + tags = { + "project" = "terraform-aws-network-examples" + } +} + +module "nat_gateway_2" { + source = "../../modules/nat-gateway" + # source = "tedilabs/network/aws//modules/nat-gateway" + # version = "~> 0.2.0" + + name = "test-assingments" + is_private = true + subnet = data.aws_subnets.default.ids[0] + + + ## Primary IP Address + primary_ip_assignment = { + # Automatically assign a public IP address to the NAT Gateway + private_ip = "172.31.51.100" + } + + + ## Secondary IP Addresses + secondary_ip_assignments = [ + { + private_ip = "172.31.51.101" + }, + { + private_ip = "172.31.51.102" + }, + { + private_ip = "172.31.51.103" + }, + { + private_ip = "172.31.51.104" + }, + ] + + + tags = { + "project" = "terraform-aws-network-examples" + } +} diff --git a/examples/nat-gateway-private-secondary-ip-addresses/outputs.tf b/examples/nat-gateway-private-secondary-ip-addresses/outputs.tf new file mode 100644 index 0000000..c0ba79e --- /dev/null +++ b/examples/nat-gateway-private-secondary-ip-addresses/outputs.tf @@ -0,0 +1,7 @@ +output "nat_gateway" { + description = "The NAT Gateways." + value = { + count = module.nat_gateway + assignments = module.nat_gateway_2 + } +} diff --git a/examples/nat-gateway-private-secondary-ip-addresses/versions.tf b/examples/nat-gateway-private-secondary-ip-addresses/versions.tf new file mode 100644 index 0000000..59c42e8 --- /dev/null +++ b/examples/nat-gateway-private-secondary-ip-addresses/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 1.5" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} diff --git a/examples/nat-gateway-private/main.tf b/examples/nat-gateway-private/main.tf new file mode 100644 index 0000000..0dee5c9 --- /dev/null +++ b/examples/nat-gateway-private/main.tf @@ -0,0 +1,41 @@ +provider "aws" { + region = "us-east-1" +} + +data "aws_vpc" "default" { + default = true +} + +data "aws_subnets" "default" { + filter { + name = "vpc-id" + values = [data.aws_vpc.default.id] + } +} + + +################################################### +# Public NAT Gateway +################################################### + +module "nat_gateway" { + source = "../../modules/nat-gateway" + # source = "tedilabs/network/aws//modules/nat-gateway" + # version = "~> 0.2.0" + + name = "test/az1" + is_private = true + subnet = data.aws_subnets.default.ids[0] + + + ## Primary IP Address + primary_ip_assignment = { + # Automatically assign a public IP address to the NAT Gateway + private_ip = null + } + + + tags = { + "project" = "terraform-aws-network-examples" + } +} diff --git a/examples/nat-gateway-private/outputs.tf b/examples/nat-gateway-private/outputs.tf new file mode 100644 index 0000000..1e20c3a --- /dev/null +++ b/examples/nat-gateway-private/outputs.tf @@ -0,0 +1,4 @@ +output "nat_gateway" { + description = "The NAT Gateway." + value = module.nat_gateway +} diff --git a/examples/nat-gateway-private/versions.tf b/examples/nat-gateway-private/versions.tf new file mode 100644 index 0000000..59c42e8 --- /dev/null +++ b/examples/nat-gateway-private/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 1.5" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} diff --git a/examples/nat-gateway-public/main.tf b/examples/nat-gateway-public/main.tf new file mode 100644 index 0000000..5970f38 --- /dev/null +++ b/examples/nat-gateway-public/main.tf @@ -0,0 +1,57 @@ +provider "aws" { + region = "us-east-1" +} + +data "aws_vpc" "default" { + default = true +} + +data "aws_subnets" "default" { + filter { + name = "vpc-id" + values = [data.aws_vpc.default.id] + } +} + + +################################################### +# Elastic IP +################################################### + +module "elastic_ip" { + source = "tedilabs/ipam/aws//modules/elastic-ip" + version = "~> 0.3.0" + + name = "nat-gw-public" + type = "AMAZON" + + tags = { + "project" = "terraform-aws-network-examples" + } +} + + +################################################### +# Public NAT Gateway +################################################### + +module "nat_gateway" { + source = "../../modules/nat-gateway" + # source = "tedilabs/network/aws//modules/nat-gateway" + # version = "~> 0.2.0" + + name = "test/az1" + is_private = false + subnet = data.aws_subnets.default.ids[0] + + + ## Primary IP Address + primary_ip_assignment = { + elastic_ip = module.elastic_ip.id + } + + + tags = { + "project" = "terraform-aws-network-examples" + } +} diff --git a/examples/nat-gateway-public/outputs.tf b/examples/nat-gateway-public/outputs.tf new file mode 100644 index 0000000..57010c1 --- /dev/null +++ b/examples/nat-gateway-public/outputs.tf @@ -0,0 +1,9 @@ +output "elastic_ip" { + description = "The Elastic IP." + value = module.elastic_ip +} + +output "nat_gateway" { + description = "The NAT Gateway." + value = module.nat_gateway +} diff --git a/examples/nat-gateway-public/versions.tf b/examples/nat-gateway-public/versions.tf new file mode 100644 index 0000000..59c42e8 --- /dev/null +++ b/examples/nat-gateway-public/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 1.5" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} diff --git a/modules/nat-gateway/README.md b/modules/nat-gateway/README.md index 4d29ad7..96d3248 100644 --- a/modules/nat-gateway/README.md +++ b/modules/nat-gateway/README.md @@ -10,14 +10,14 @@ This module creates following resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.5 | -| [aws](#requirement\_aws) | >= 3.45 | +| [terraform](#requirement\_terraform) | >= 1.6 | +| [aws](#requirement\_aws) | >= 5.10 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | 5.19.0 | +| [aws](#provider\_aws) | 5.22.0 | ## Modules @@ -29,32 +29,39 @@ This module creates following resources. | Name | Type | |------|------| -| [aws_eip.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource | | [aws_nat_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource | +| [aws_subnet.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [name](#input\_name) | Desired name for the NAT Gateway resources. | `string` | n/a | yes | -| [subnet\_id](#input\_subnet\_id) | The ID of the subnet which the NAT Gateway belongs to. | `string` | n/a | yes | -| [assign\_eip\_on\_create](#input\_assign\_eip\_on\_create) | Assign a new Elastic IP to NAT Gateway on create. Set false if you want to provide existing Elastic IP. | `bool` | `false` | no | -| [eip\_id](#input\_eip\_id) | The Allocation ID of the Elastic IP address for the gateway. Create a new Elastic IP if not provided. | `string` | `""` | no | -| [is\_private](#input\_is\_private) | Whether to create the gateway as private or public connectivity type. Defaults to public(false). | `bool` | `false` | no | -| [module\_tags\_enabled](#input\_module\_tags\_enabled) | Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | -| [resource\_group\_description](#input\_resource\_group\_description) | The description of Resource Group. | `string` | `"Managed by Terraform."` | no | -| [resource\_group\_enabled](#input\_resource\_group\_enabled) | Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | -| [resource\_group\_name](#input\_resource\_group\_name) | The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | -| [tags](#input\_tags) | A map of tags to add to all resources. | `map(string)` | `{}` | no | +| [name](#input\_name) | (Required) Desired name for the NAT Gateway resources. | `string` | n/a | yes | +| [subnet](#input\_subnet) | (Required) The Subnet ID of the subnet in which to place the NAT Gateway. | `string` | n/a | yes | +| [is\_private](#input\_is\_private) | (Optional) Whether to create the NAT gateway as private or public connectivity type. Defaults to `false` (public). | `bool` | `false` | no | +| [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | +| [primary\_ip\_assignment](#input\_primary\_ip\_assignment) | (Optional) A configuration to assign primary ip address with the NAT Gateway. `primary_ip_assignment` as defined below.
(Optional) `elastic_ip` - The allocation ID of Elastic IP address to associate with the NAT Gateway.
(Optional) `private_ip` - The private IP address to associate with the NAT Gateway. If you dont't provide an address, a private IPv4 address will be automatically assigned. |
object({
elastic_ip = optional(string)
private_ip = optional(string)
})
| `{}` | no | +| [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no | +| [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no | +| [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no | +| [secondary\_ip\_assignments](#input\_secondary\_ip\_assignments) | (Optional) A configuration to assign secondary ip addresses with the NAT Gateway. Each block of `secondary_ip_assignments` as defined below.
(Optional) `elastic_ip` - The allocation ID of Elastic IP address to associate with the NAT Gateway.
(Optional) `private_ip` - The private IP address to associate with the NAT Gateway. If you dont't provide an address, a private IPv4 address will be automatically assigned. |
list(object({
elastic_ip = optional(string)
private_ip = optional(string)
}))
| `[]` | no | +| [secondary\_ip\_count](#input\_secondary\_ip\_count) | (Optional) The number of secondary private IPv4 addresses to assign to the NAT Gateway. Only used with private NAT Gateway. | `number` | `null` | no | +| [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | +| [timeouts](#input\_timeouts) | (Optional) How long to wait for the NAT Gateway to be created/updated/deleted. |
object({
create = optional(string, "10m")
update = optional(string, "10m")
delete = optional(string, "30m")
})
| `{}` | no | ## Outputs | Name | Description | |------|-------------| -| [connectivity\_type](#output\_connectivity\_type) | Connectivity type for the gateway. Valid values are private and public. | -| [eip\_id](#output\_eip\_id) | The Allocation ID of the Elastic IP address for the gateway. | -| [eni\_id](#output\_eni\_id) | The ENI ID of the network interface created by the NAT gateway. | +| [availability\_zone](#output\_availability\_zone) | The availability zone of the NAT Gateway.
`id` - The ID of the availability zone.
`name` - The name of the availability zone. | +| [elastic\_ip](#output\_elastic\_ip) | The Allocation ID of the Elastic IP address for the gateway. | | [id](#output\_id) | The ID of the NAT Gateway. | -| [private\_ip](#output\_private\_ip) | The private IP address of the NAT Gateway. | -| [public\_ip](#output\_public\_ip) | The public IP address of the NAT Gateway. | +| [is\_private](#output\_is\_private) | Whether the NAT Gateway supports public or private connectivity. | +| [name](#output\_name) | The name of the NAT Gateway. | +| [netework\_interface](#output\_netework\_interface) | The ENI ID of the network interface created by the NAT gateway. | +| [primary\_private\_ip](#output\_primary\_private\_ip) | The private IP address of the NAT Gateway. | +| [primary\_public\_ip](#output\_primary\_public\_ip) | The public IP address of the NAT Gateway. | +| [secondary\_private\_ips](#output\_secondary\_private\_ips) | The secondary private IP addresses of the NAT Gateway. | +| [subnet](#output\_subnet) | The subnet which the NAT Gateway belongs to.
`id` - The ID of the subnet.
`arn` - The ARN of the subnet. | +| [vpc\_id](#output\_vpc\_id) | The VPC ID of the NAT Gateway. | diff --git a/modules/nat-gateway/main.tf b/modules/nat-gateway/main.tf index 9e9756a..6c1b144 100644 --- a/modules/nat-gateway/main.tf +++ b/modules/nat-gateway/main.tf @@ -14,24 +14,47 @@ locals { } : {} } -resource "aws_eip" "this" { - count = !var.is_private && var.assign_eip_on_create ? 1 : 0 +data "aws_subnet" "this" { + id = var.subnet +} - vpc = true - tags = merge( - { - "Name" = local.metadata.name - }, - local.module_tags, - var.tags, - ) -} +################################################### +# NAT Gateway +################################################### resource "aws_nat_gateway" "this" { connectivity_type = var.is_private ? "private" : "public" - subnet_id = var.subnet_id - allocation_id = length(aws_eip.this) > 0 ? aws_eip.this[0].id : var.eip_id + subnet_id = var.subnet + + + ## Primary IP Addresse + allocation_id = var.primary_ip_assignment.elastic_ip + private_ip = var.primary_ip_assignment.private_ip + + + ## Secondary IP Addresses + secondary_allocation_ids = (!var.is_private + ? [ + for assignment in var.secondary_ip_assignments : + assignment.elastic_ip + ] + : null + ) + secondary_private_ip_addresses = (var.secondary_ip_count == null + ? [ + for assignment in var.secondary_ip_assignments : + assignment.private_ip + ] + : null + ) + secondary_private_ip_address_count = var.secondary_ip_count + + timeouts { + create = var.timeouts.create + update = var.timeouts.update + delete = var.timeouts.delete + } tags = merge( { @@ -40,4 +63,14 @@ resource "aws_nat_gateway" "this" { local.module_tags, var.tags, ) + + lifecycle { + precondition { + condition = anytrue([ + var.secondary_ip_count == null, + var.secondary_ip_count != null && var.is_private == true, + ]) + error_message = "`secondary_ip_count` variable is only supported with private NAT Gateway." + } + } } diff --git a/modules/nat-gateway/outputs.tf b/modules/nat-gateway/outputs.tf index 2891a6b..eb4cf21 100644 --- a/modules/nat-gateway/outputs.tf +++ b/modules/nat-gateway/outputs.tf @@ -3,27 +3,66 @@ output "id" { value = aws_nat_gateway.this.id } -output "connectivity_type" { - description = "Connectivity type for the gateway. Valid values are private and public." - value = aws_nat_gateway.this.connectivity_type +output "name" { + description = "The name of the NAT Gateway." + value = var.name } -output "eip_id" { +output "is_private" { + description = "Whether the NAT Gateway supports public or private connectivity." + value = aws_nat_gateway.this.connectivity_type == "private" +} + +output "availability_zone" { + description = < 0 && var.secondary_ip_count < 32 + : true + ) + error_message = "`secondary_ip_count` must be greater than 0 and less than 32." + } +} + +variable "timeouts" { + description = "(Optional) How long to wait for the NAT Gateway to be created/updated/deleted." + type = object({ + create = optional(string, "10m") + update = optional(string, "10m") + delete = optional(string, "30m") + }) + default = {} + nullable = false } variable "tags" { - description = "A map of tags to add to all resources." + description = "(Optional) A map of tags to add to all resources." type = map(string) default = {} + nullable = false } variable "module_tags_enabled" { - description = "Whether to create AWS Resource Tags for the module informations." + description = "(Optional) Whether to create AWS Resource Tags for the module informations." type = bool default = true + nullable = false } @@ -44,19 +102,22 @@ variable "module_tags_enabled" { ################################################### variable "resource_group_enabled" { - description = "Whether to create Resource Group to find and group AWS resources which are created by this module." + description = "(Optional) Whether to create Resource Group to find and group AWS resources which are created by this module." type = bool default = true + nullable = false } variable "resource_group_name" { - description = "The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`." + description = "(Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`." type = string default = "" + nullable = false } variable "resource_group_description" { - description = "The description of Resource Group." + description = "(Optional) The description of Resource Group." type = string default = "Managed by Terraform." + nullable = false } diff --git a/modules/nat-gateway/versions.tf b/modules/nat-gateway/versions.tf index 9ae3d78..c46e4aa 100644 --- a/modules/nat-gateway/versions.tf +++ b/modules/nat-gateway/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.5" + required_version = ">= 1.6" required_providers { aws = { source = "hashicorp/aws" - version = ">= 3.45" + version = ">= 5.10" } } }