From b60dd440bd58b58752d8b61677d1a4ae0299d474 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Tue, 21 Nov 2023 14:32:11 +0000 Subject: [PATCH] Create public and private networks * This calculates the IP range for the 'private' and 'public' networks within the VPC. It does this by initially splitting the given vpc CIDR block into 2, and then calcalting the 'nebits' so that individual '/24' ranges can be calulated for subnets. * A subnet for each given availability zone will be created, in both the 'private' and 'public' ranges. * A public route table will be created, along with an Internet Gateway, and a 0/0 route created for the gateway. The subnets within the 'public' range will then be associated with this route table, allowing any resource launched within them to access the internet. * A private route table will also be created. If 'public' networking is enabled, a NAT gateway will be created in one of the public subnets, and a route created to allow resources within the 'private' subnets access to the internet. * Example subnets that will be created: ``` infrastructure_vpc_cidr_block = "10.0.0.0/16" infrastructure_vpc_network_enable_public = true infrastructure_vpc_network_enable_private = true infrastructure_vpc_network_availability_zones = ["a", "b", "c"] public subnets (Allocated "10.0.0.0/17"): [ "10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24" ] private subnets (Allocated "10.0.128.0/17"): [ "10.0.128.0/24", "10.0.129.0/24", "10.0.130.0/24" ] ``` --- README.md | 14 +++++ locals.tf | 9 ++++ variables.tf | 15 ++++++ vpc-infrastructure-flow-logs-cloudwatch.tf | 2 +- vpc-infrastructure-network-private.tf | 59 ++++++++++++++++++++++ vpc-infrastructure-network-public.tf | 51 +++++++++++++++++++ 6 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 vpc-infrastructure-network-private.tf create mode 100644 vpc-infrastructure-network-public.tf diff --git a/README.md b/README.md index 350fdcd..42bf14e 100644 --- a/README.md +++ b/README.md @@ -25,20 +25,31 @@ This project creates and manages resources within an AWS account for infrastruct |------|------| | [aws_athena_workgroup.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_workgroup) | resource | | [aws_cloudwatch_log_group.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_eip.infrastructure_nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource | | [aws_flow_log.infrastructure_vpc_flow_logs_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource | | [aws_flow_log.infrastructure_vpc_flow_logs_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource | | [aws_glue_catalog_database.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/glue_catalog_database) | resource | | [aws_glue_catalog_table.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/glue_catalog_table) | resource | | [aws_iam_role.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy.infrastructure_vpc_flow_logs_allow_cloudwatch_rw](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_internet_gateway.infrastructure_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource | | [aws_kms_alias.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | | [aws_kms_key.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | +| [aws_nat_gateway.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource | +| [aws_route.infrustructure_public_internet_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route.private_nat_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource | +| [aws_route_table.infrastructure_private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | +| [aws_route_table.infrastructure_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource | +| [aws_route_table_association.infrastructure_private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | +| [aws_route_table_association.infrastructure_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource | | [aws_s3_bucket.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket_lifecycle_configuration.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration) | resource | | [aws_s3_bucket_policy.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | | [aws_s3_bucket_public_access_block.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_s3_bucket_server_side_encryption_configuration.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | | [aws_s3_bucket_versioning.infrastructure_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [aws_subnet.infrastructure_private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | +| [aws_subnet.infrastructure_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource | | [aws_vpc.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | @@ -63,6 +74,9 @@ This project creates and manages resources within an AWS account for infrastruct | [infrastructure\_vpc\_flow\_logs\_s3\_with\_athena](#input\_infrastructure\_vpc\_flow\_logs\_s3\_with\_athena) | Enable VPC flow logs in infrastructure VPC to the S3 logs bucket. A compatible Glue table/database and Athena workgroup will also be created to allow querying the logs. | `bool` | n/a | yes | | [infrastructure\_vpc\_flow\_logs\_traffic\_type](#input\_infrastructure\_vpc\_flow\_logs\_traffic\_type) | Infrastructure VPC flow logs traffic type | `string` | n/a | yes | | [infrastructure\_vpc\_instance\_tenancy](#input\_infrastructure\_vpc\_instance\_tenancy) | Infrastructure VPC instance tenancy | `string` | n/a | yes | +| [infrastructure\_vpc\_network\_availability\_zones](#input\_infrastructure\_vpc\_network\_availability\_zones) | A list of availability zone characters (eg. ["a", "b", "c"]) | `list(string)` | n/a | yes | +| [infrastructure\_vpc\_network\_enable\_private](#input\_infrastructure\_vpc\_network\_enable\_private) | Enable private networking on Infrastructure VPC. This will create subnets with a route to a NAT Gateway (If Public networking has been enabled) | `bool` | n/a | yes | +| [infrastructure\_vpc\_network\_enable\_public](#input\_infrastructure\_vpc\_network\_enable\_public) | Enable public networking on Infrastructure VPC. This will create subnets with a route to an Internet Gateway | `bool` | n/a | yes | | [project\_name](#input\_project\_name) | Project name to be used as a prefix for all resources | `string` | n/a | yes | ## Outputs diff --git a/locals.tf b/locals.tf index 41d4f13..b92b596 100644 --- a/locals.tf +++ b/locals.tf @@ -25,6 +25,15 @@ locals { infrastructure_vpc_instance_tenancy = var.infrastructure_vpc_instance_tenancy infrastructure_vpc_enable_network_address_usage_metrics = var.infrastructure_vpc_enable_network_address_usage_metrics infrastructure_vpc_assign_generated_ipv6_cidr_block = var.infrastructure_vpc_assign_generated_ipv6_cidr_block + infrastructure_vpc_network_enable_public = local.infrastructure_vpc && var.infrastructure_vpc_network_enable_public + infrastructure_vpc_network_enable_private = local.infrastructure_vpc && var.infrastructure_vpc_network_enable_private + infrastructure_vpc_network_availability_zones = toset(sort(var.infrastructure_vpc_network_availability_zones)) + infrastructure_vpc_network_public_cidr = cidrsubnet(local.infrastructure_vpc_cidr_block, 1, 0) + infrastructure_vpc_network_public_cidr_prefix = basename(local.infrastructure_vpc_network_public_cidr) + infrastructure_vpc_network_public_cidr_newbits = 24 - local.infrastructure_vpc_network_public_cidr_prefix + infrastructure_vpc_network_private_cidr = cidrsubnet(local.infrastructure_vpc_cidr_block, 1, 1) + infrastructure_vpc_network_private_cidr_prefix = basename(local.infrastructure_vpc_network_private_cidr) + infrastructure_vpc_network_private_cidr_newbits = 24 - local.infrastructure_vpc_network_private_cidr_prefix infrastructure_vpc_flow_logs_cloudwatch_logs = var.infrastructure_vpc_flow_logs_cloudwatch_logs && local.infrastructure_vpc infrastructure_vpc_flow_logs_s3_with_athena = var.infrastructure_vpc_flow_logs_s3_with_athena && local.infrastructure_vpc infrastructure_vpc_flow_logs_s3_key_prefix = trim(var.infrastructure_vpc_flow_logs_s3_key_prefix, "/") diff --git a/variables.tf b/variables.tf index cd214fc..ecc1141 100644 --- a/variables.tf +++ b/variables.tf @@ -87,3 +87,18 @@ variable "infrastructure_vpc_flow_logs_s3_key_prefix" { description = "Flow Logs by default will go into the infrastructure S3 logs bucket. This is the key prefix used to isolate them from other logs" type = string } + +variable "infrastructure_vpc_network_enable_public" { + description = "Enable public networking on Infrastructure VPC. This will create subnets with a route to an Internet Gateway" + type = bool +} + +variable "infrastructure_vpc_network_enable_private" { + description = "Enable private networking on Infrastructure VPC. This will create subnets with a route to a NAT Gateway (If Public networking has been enabled)" + type = bool +} + +variable "infrastructure_vpc_network_availability_zones" { + description = "A list of availability zone characters (eg. [\"a\", \"b\", \"c\"])" + type = list(string) +} diff --git a/vpc-infrastructure-flow-logs-cloudwatch.tf b/vpc-infrastructure-flow-logs-cloudwatch.tf index db2764d..23f0aef 100644 --- a/vpc-infrastructure-flow-logs-cloudwatch.tf +++ b/vpc-infrastructure-flow-logs-cloudwatch.tf @@ -19,7 +19,7 @@ resource "aws_iam_role" "infrastructure_vpc_flow_logs" { resource "aws_iam_role_policy" "infrastructure_vpc_flow_logs_allow_cloudwatch_rw" { count = local.infrastructure_vpc_flow_logs_cloudwatch_logs ? 1 : 0 - name = "${local.resource_prefix}-ecs-vpc-flow-logs-cloudwatch-logs-rw" + name = "${local.resource_prefix}-vpc-flow-logs-cloudwatch-logs-rw" role = aws_iam_role.infrastructure_vpc_flow_logs[0].id policy = templatefile("${path.root}/policies/cloudwatch-logs-rw.json.tpl", {}) } diff --git a/vpc-infrastructure-network-private.tf b/vpc-infrastructure-network-private.tf new file mode 100644 index 0000000..4dfdbfe --- /dev/null +++ b/vpc-infrastructure-network-private.tf @@ -0,0 +1,59 @@ +resource "aws_route_table" "infrastructure_private" { + count = local.infrastructure_vpc_network_enable_private ? 1 : 0 + + vpc_id = aws_vpc.infrastructure[0].id + + tags = { + Name = "${local.resource_prefix}-infrastructure-private" + } +} + +resource "aws_subnet" "infrastructure_private" { + for_each = local.infrastructure_vpc_network_enable_private ? local.infrastructure_vpc_network_availability_zones : [] + + vpc_id = aws_vpc.infrastructure[0].id + availability_zone = "${local.aws_region}${each.value}" + + cidr_block = cidrsubnet( + local.infrastructure_vpc_network_private_cidr, + local.infrastructure_vpc_network_private_cidr_newbits, + index(tolist(local.infrastructure_vpc_network_availability_zones), each.value) + ) + + tags = { + Name = "${local.resource_prefix}-infrastructure-private-${each.value}" + } +} + +resource "aws_route_table_association" "infrastructure_private" { + for_each = local.infrastructure_vpc_network_enable_private ? local.infrastructure_vpc_network_availability_zones : [] + + subnet_id = aws_subnet.infrastructure_private[each.value].id + route_table_id = aws_route_table.infrastructure_private[0].id +} + +resource "aws_eip" "infrastructure_nat" { + count = local.infrastructure_vpc_network_enable_private && local.infrastructure_vpc_network_enable_public ? 1 : 0 + + domain = "vpc" + + tags = { + Name = "${local.resource_prefix}-infrastructure-nat" + } +} + +resource "aws_nat_gateway" "infrastructure" { + count = local.infrastructure_vpc_network_enable_private && local.infrastructure_vpc_network_enable_public ? 1 : 0 + + allocation_id = aws_eip.infrastructure_nat[0].id + subnet_id = aws_subnet.infrastructure_public[0].id + depends_on = [aws_internet_gateway.infrastructure_public] +} + +resource "aws_route" "private_nat_gateway" { + count = local.infrastructure_vpc_network_enable_private && local.infrastructure_vpc_network_enable_public ? 1 : 0 + + route_table_id = aws_route_table.infrastructure_private[0] + destination_cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.infrastructure[0].id +} diff --git a/vpc-infrastructure-network-public.tf b/vpc-infrastructure-network-public.tf new file mode 100644 index 0000000..617779b --- /dev/null +++ b/vpc-infrastructure-network-public.tf @@ -0,0 +1,51 @@ +resource "aws_route_table" "infrastructure_public" { + count = local.infrastructure_vpc_network_enable_public ? 1 : 0 + + vpc_id = aws_vpc.infrastructure[0].id + + tags = { + Name = "${local.resource_prefix}-infrastructure-public" + } +} + +resource "aws_internet_gateway" "infrastructure_public" { + count = local.infrastructure_vpc_network_enable_public ? 1 : 0 + + vpc_id = aws_vpc.infrastructure[0].id + + tags = { + Name = "${local.resource_prefix}-infrastructure-public" + } +} + +resource "aws_route" "infrustructure_public_internet_gateway" { + count = local.infrastructure_vpc_network_enable_public ? 1 : 0 + + route_table_id = aws_route_table.infrastructure_public[0].id + destination_cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.infrastructure_public[0].id +} + +resource "aws_subnet" "infrastructure_public" { + for_each = local.infrastructure_vpc_network_enable_public ? local.infrastructure_vpc_network_availability_zones : [] + + vpc_id = aws_vpc.infrastructure[0].id + availability_zone = "${local.aws_region}${each.value}" + + cidr_block = cidrsubnet( + local.infrastructure_vpc_network_public_cidr, + local.infrastructure_vpc_network_public_cidr_newbits, + index(tolist(local.infrastructure_vpc_network_availability_zones), each.value) + ) + + tags = { + Name = "${local.resource_prefix}-infrastructure-public-${each.value}" + } +} + +resource "aws_route_table_association" "infrastructure_public" { + for_each = local.infrastructure_vpc_network_enable_public ? local.infrastructure_vpc_network_availability_zones : [] + + subnet_id = aws_subnet.infrastructure_public[each.value].id + route_table_id = aws_route_table.infrastructure_public[0].id +}