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..e41f8e9 --- /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[element(tolist(local.infrastructure_vpc_network_availability_zones), 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].id + 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 +}