diff --git a/.github/workflows/ci-cd-production.yaml b/.github/workflows/ci-cd-production.yaml index 8d6bea8f36..ea8ba05313 100644 --- a/.github/workflows/ci-cd-production.yaml +++ b/.github/workflows/ci-cd-production.yaml @@ -62,7 +62,6 @@ jobs: django_settings_module: settings.production enable_additional_parameters: true enable_cron_tasks: true - enable_nat_gateway: true enable_rds_proxy: true environment: production frontend_desired_count: 2 diff --git a/.github/workflows/ci-cd-staging.yaml b/.github/workflows/ci-cd-staging.yaml index a6ad2bb667..4324d8cf97 100644 --- a/.github/workflows/ci-cd-staging.yaml +++ b/.github/workflows/ci-cd-staging.yaml @@ -61,7 +61,6 @@ jobs: django_configuration: Staging django_settings_module: settings.staging enable_cron_tasks: false - enable_nat_gateway: false enable_rds_proxy: false environment: staging frontend_desired_count: 1 diff --git a/.github/workflows/run-ci-cd.yaml b/.github/workflows/run-ci-cd.yaml index d4ff79b455..e57d4d5bc4 100644 --- a/.github/workflows/run-ci-cd.yaml +++ b/.github/workflows/run-ci-cd.yaml @@ -69,10 +69,6 @@ on: description: Enable cron tasks required: true type: boolean - enable_nat_gateway: - description: Enable NAT gateway - required: true - type: boolean enable_rds_proxy: description: Enable RDS proxy required: true @@ -310,7 +306,6 @@ jobs: django_settings_module: ${{ inputs.django_settings_module }} enable_additional_parameters: ${{ inputs.enable_additional_parameters }} enable_cron_tasks: ${{ inputs.enable_cron_tasks }} - enable_nat_gateway: ${{ inputs.enable_nat_gateway }} enable_rds_proxy: ${{ inputs.enable_rds_proxy }} environment: ${{ inputs.environment }} frontend_desired_count: ${{ inputs.frontend_desired_count }} diff --git a/.github/workflows/run-deploy.yaml b/.github/workflows/run-deploy.yaml index 5c9c7a50dd..ff4adf5721 100644 --- a/.github/workflows/run-deploy.yaml +++ b/.github/workflows/run-deploy.yaml @@ -65,10 +65,6 @@ on: description: Enable cron tasks required: true type: boolean - enable_nat_gateway: - description: Enable NAT gateway - required: true - type: boolean enable_rds_proxy: description: Enable RDS proxy required: true @@ -217,7 +213,6 @@ jobs: django_settings_module="${{ inputs.django_settings_module }}" enable_additional_parameters=${{ inputs.enable_additional_parameters }} enable_cron_tasks=${{ inputs.enable_cron_tasks }} - enable_nat_gateway=${{ inputs.enable_nat_gateway }} enable_rds_proxy=${{ inputs.enable_rds_proxy }} environment="${{ inputs.environment }}" frontend_desired_count=${{ inputs.frontend_desired_count }} @@ -238,7 +233,6 @@ jobs: echo "backend_service_name=$(terraform output -raw backend_service_name)" echo "frontend_cluster_name=$(terraform output -raw frontend_cluster_name)" echo "frontend_service_name=$(terraform output -raw frontend_service_name)" - echo "nat_gateway_enabled=$(terraform output -raw nat_gateway_enabled)" echo "tasks_cluster_name=$(terraform output -raw tasks_cluster_name)" echo "tasks_security_group_id=$(terraform output -raw tasks_security_group_id)" echo "tasks_subnet_ids=$(terraform output -json tasks_subnet_ids | jq -r 'join(",")')" @@ -249,12 +243,11 @@ jobs: id: migrate-task env: CLUSTER_NAME: ${{ steps.tf-outputs.outputs.tasks_cluster_name }} - NAT_GATEWAY_ENABLED: ${{ steps.tf-outputs.outputs.nat_gateway_enabled }} TASKS_SECURITY_GROUP_ID: ${{ steps.tf-outputs.outputs.tasks_security_group_id }} TASK_DEFINITION: ${{ inputs.migrate_task_definition }} TASKS_SUBNET_IDS: ${{ steps.tf-outputs.outputs.tasks_subnet_ids }} run: | - ASSIGN_PUBLIC_IP=$([ "$NAT_GATEWAY_ENABLED" = "true" ] && echo "DISABLED" || echo "ENABLED") + ASSIGN_PUBLIC_IP=DISABLED TASK_ARN=$(aws ecs run-task \ --cluster "$CLUSTER_NAME" \ --task-definition "$TASK_DEFINITION" \ @@ -291,12 +284,11 @@ jobs: - name: Run ECS index-data task env: CLUSTER_NAME: ${{ steps.tf-outputs.outputs.tasks_cluster_name }} - NAT_GATEWAY_ENABLED: ${{ steps.tf-outputs.outputs.nat_gateway_enabled }} TASK_DEFINITION: ${{ inputs.index_task_definition }} TASKS_SECURITY_GROUP_ID: ${{ steps.tf-outputs.outputs.tasks_security_group_id }} TASKS_SUBNET_IDS: ${{ steps.tf-outputs.outputs.tasks_subnet_ids }} run: | - ASSIGN_PUBLIC_IP=$([ "$NAT_GATEWAY_ENABLED" = "true" ] && echo "DISABLED" || echo "ENABLED") + ASSIGN_PUBLIC_IP=DISABLED RESPONSE=$(aws ecs run-task \ --cluster "$CLUSTER_NAME" \ --task-definition "$TASK_DEFINITION" \ diff --git a/infrastructure/README.md b/infrastructure/README.md index afb1eecbb2..fa91165675 100644 --- a/infrastructure/README.md +++ b/infrastructure/README.md @@ -260,8 +260,7 @@ Run the following commands to execute ECS tasks with the correct network configu CLUSTER=$(terraform output -raw tasks_cluster_name) SECURITY_GROUP=$(terraform output -raw tasks_security_group_id) SUBNETS=$(terraform output -json tasks_subnet_ids | jq -r 'join(",")') -NAT_ENABLED=$(terraform output -raw nat_gateway_enabled) -ASSIGN_PUBLIC_IP=$([ "$NAT_ENABLED" = "true" ] && echo "DISABLED" || echo "ENABLED") +ASSIGN_PUBLIC_IP=DISABLED ``` > [!NOTE] @@ -313,9 +312,9 @@ aws ecs run-task \ - Environment: Cluster: `nest-staging-tasks-cluster` - Networking: - VPC: `nest-staging-vpc` - - Subnets: Choose a private subnet if NAT Gateway is enabled, public otherwise (default). + - Subnets: Choose a private subnet. - Security group name: select the ECS security group (e.g. `nest-staging-tasks-sg`). - - Public IP: Turned off if NAT Gateway is enabled, Turned on otherwise (default). + - Public IP: Turned off. - Click "Create" - The task is now running... Click on the task ID to view Logs, Status, etc. - Follow the same steps for `nest-staging-load-data` and `nest-staging-index-data`. diff --git a/infrastructure/live/README.md b/infrastructure/live/README.md index 9a43be941b..360862d3b6 100644 --- a/infrastructure/live/README.md +++ b/infrastructure/live/README.md @@ -102,7 +102,6 @@ No resources. | [domain\_name](#input\_domain\_name) | The domain name for the site. | `string` | n/a | yes | | [enable\_additional\_parameters](#input\_enable\_additional\_parameters) | Whether to enable additional parameters (e.g. for production). | `bool` | `false` | no | | [enable\_cron\_tasks](#input\_enable\_cron\_tasks) | Whether to enable scheduled cron tasks. | `bool` | n/a | yes | -| [enable\_nat\_gateway](#input\_enable\_nat\_gateway) | Whether to enable a NAT Gateway. | `bool` | `true` | no | | [enable\_rds\_proxy](#input\_enable\_rds\_proxy) | Whether to create an RDS proxy. | `bool` | `false` | no | | [enable\_vpc\_cloudwatch\_logs\_endpoint](#input\_enable\_vpc\_cloudwatch\_logs\_endpoint) | Whether to create CloudWatch Logs VPC endpoint. | `bool` | `false` | no | | [enable\_vpc\_ecr\_api\_endpoint](#input\_enable\_vpc\_ecr\_api\_endpoint) | Whether to create ECR API VPC endpoint. | `bool` | `false` | no | @@ -145,10 +144,9 @@ No resources. | [frontend\_ecr\_repository\_url](#output\_frontend\_ecr\_repository\_url) | The URL of the frontend ECR repository. | | [frontend\_service\_name](#output\_frontend\_service\_name) | The name of the ECS frontend service. | | [frontend\_url](#output\_frontend\_url) | The URL to access the frontend. | -| [nat\_gateway\_enabled](#output\_nat\_gateway\_enabled) | Whether a NAT Gateway is enabled. | | [private\_subnet\_ids](#output\_private\_subnet\_ids) | A list of private subnet IDs. | | [shared\_data\_bucket\_name](#output\_shared\_data\_bucket\_name) | S3 bucket for shared public data (e.g. nest.dump). | | [tasks\_cluster\_name](#output\_tasks\_cluster\_name) | The name of the ECS tasks cluster. | | [tasks\_security\_group\_id](#output\_tasks\_security\_group\_id) | The ID of the security group for ECS tasks. | -| [tasks\_subnet\_ids](#output\_tasks\_subnet\_ids) | A list of public or private subnet IDs for ECS tasks. | +| [tasks\_subnet\_ids](#output\_tasks\_subnet\_ids) | A list of private subnet IDs for ECS tasks. | diff --git a/infrastructure/live/main.tf b/infrastructure/live/main.tf index 74168e6196..f71ee61212 100644 --- a/infrastructure/live/main.tf +++ b/infrastructure/live/main.tf @@ -15,7 +15,6 @@ terraform { } locals { - assign_public_ip = var.enable_nat_gateway ? false : true common_tags = { Environment = var.environment ManagedBy = "Terraform" @@ -41,7 +40,7 @@ module "alb" { module "backend" { source = "../modules/service" - assign_public_ip = local.assign_public_ip + assign_public_ip = false auto_scaling_cpu_target = var.auto_scaling_cpu_target auto_scaling_scale_in_cooldown = var.auto_scaling_scale_in_cooldown auto_scaling_scale_out_cooldown = var.auto_scaling_scale_out_cooldown @@ -63,7 +62,7 @@ module "backend" { project_name = var.project_name security_group_id = module.security.backend_sg_id service_name = "backend" - subnet_ids = var.enable_nat_gateway ? module.networking.private_subnet_ids : module.networking.public_subnet_ids + subnet_ids = module.networking.private_subnet_ids target_group_arn = module.alb.backend_target_group_arn task_role_policy_arns = [module.storage.static_read_write_policy_arn] use_fargate_spot = var.backend_use_fargate_spot @@ -111,7 +110,7 @@ module "database" { module "frontend" { source = "../modules/service" - assign_public_ip = local.assign_public_ip + assign_public_ip = false auto_scaling_cpu_target = var.auto_scaling_cpu_target auto_scaling_scale_in_cooldown = var.auto_scaling_scale_in_cooldown auto_scaling_scale_out_cooldown = var.auto_scaling_scale_out_cooldown @@ -130,7 +129,7 @@ module "frontend" { project_name = var.project_name security_group_id = module.security.frontend_sg_id service_name = "frontend" - subnet_ids = var.enable_nat_gateway ? module.networking.private_subnet_ids : module.networking.public_subnet_ids + subnet_ids = module.networking.private_subnet_ids target_group_arn = module.alb.frontend_target_group_arn use_fargate_spot = var.frontend_use_fargate_spot } @@ -164,7 +163,6 @@ module "networking" { availability_zones = var.availability_zones aws_region = var.aws_region common_tags = local.common_tags - enable_nat_gateway = var.enable_nat_gateway enable_vpc_cloudwatch_logs_endpoint = var.enable_vpc_cloudwatch_logs_endpoint enable_vpc_ecr_api_endpoint = var.enable_vpc_ecr_api_endpoint enable_vpc_ecr_dkr_endpoint = var.enable_vpc_ecr_dkr_endpoint @@ -233,7 +231,7 @@ module "storage" { module "tasks" { source = "../modules/tasks" - assign_public_ip = local.assign_public_ip + assign_public_ip = false aws_region = var.aws_region common_tags = local.common_tags container_parameters_arns = module.parameters.django_ssm_parameter_arns @@ -247,6 +245,6 @@ module "tasks" { image_tag = var.backend_image_tag kms_key_arn = module.kms.key_arn project_name = var.project_name - subnet_ids = var.enable_nat_gateway ? module.networking.private_subnet_ids : module.networking.public_subnet_ids + subnet_ids = module.networking.private_subnet_ids use_fargate_spot = var.tasks_use_fargate_spot } diff --git a/infrastructure/live/outputs.tf b/infrastructure/live/outputs.tf index 29047c7057..ef6024dc24 100644 --- a/infrastructure/live/outputs.tf +++ b/infrastructure/live/outputs.tf @@ -53,11 +53,6 @@ output "frontend_url" { value = "https://${var.domain_name}" } -output "nat_gateway_enabled" { - description = "Whether a NAT Gateway is enabled." - value = var.enable_nat_gateway -} - output "private_subnet_ids" { description = "A list of private subnet IDs." value = module.networking.private_subnet_ids @@ -79,6 +74,6 @@ output "tasks_security_group_id" { } output "tasks_subnet_ids" { - description = "A list of public or private subnet IDs for ECS tasks." - value = var.enable_nat_gateway ? module.networking.private_subnet_ids : module.networking.public_subnet_ids + description = "A list of private subnet IDs for ECS tasks." + value = module.networking.private_subnet_ids } diff --git a/infrastructure/live/terraform.production.tfvars.example b/infrastructure/live/terraform.production.tfvars.example index 8d84d8d685..77dc2c3328 100644 --- a/infrastructure/live/terraform.production.tfvars.example +++ b/infrastructure/live/terraform.production.tfvars.example @@ -16,7 +16,6 @@ django_release_version = "0.0.0" django_settings_module = "settings.production" enable_additional_parameters = true enable_cron_tasks = true -enable_nat_gateway = true enable_rds_proxy = true environment = "production" frontend_desired_count = 2 diff --git a/infrastructure/live/terraform.staging.tfvars.example b/infrastructure/live/terraform.staging.tfvars.example index 3a5c1c3b91..1367b9500b 100644 --- a/infrastructure/live/terraform.staging.tfvars.example +++ b/infrastructure/live/terraform.staging.tfvars.example @@ -14,7 +14,6 @@ domain_name = "nest.owasp.dev" django_configuration = "Staging" django_settings_module = "settings.staging" enable_cron_tasks = false -enable_nat_gateway = false enable_rds_proxy = false environment = "staging" frontend_desired_count = 1 diff --git a/infrastructure/live/variables.tf b/infrastructure/live/variables.tf index 86181dc88e..ed7af5ff60 100644 --- a/infrastructure/live/variables.tf +++ b/infrastructure/live/variables.tf @@ -193,12 +193,6 @@ variable "enable_cron_tasks" { type = bool } -variable "enable_nat_gateway" { - description = "Whether to enable a NAT Gateway." - type = bool - default = true -} - variable "enable_rds_proxy" { description = "Whether to create an RDS proxy." type = bool diff --git a/infrastructure/modules/networking/README.md b/infrastructure/modules/networking/README.md index 9c3c4a653d..9a40db0478 100644 --- a/infrastructure/modules/networking/README.md +++ b/infrastructure/modules/networking/README.md @@ -46,7 +46,6 @@ | [availability\_zones](#input\_availability\_zones) | A list of availability zones for the VPC. | `list(string)` | n/a | yes | | [aws\_region](#input\_aws\_region) | The AWS region. | `string` | n/a | yes | | [common\_tags](#input\_common\_tags) | A map of common tags to apply to all resources. | `map(string)` | `{}` | no | -| [enable\_nat\_gateway](#input\_enable\_nat\_gateway) | Whether to enable a NAT Gateway. | `bool` | `true` | no | | [enable\_vpc\_cloudwatch\_logs\_endpoint](#input\_enable\_vpc\_cloudwatch\_logs\_endpoint) | Whether to create CloudWatch Logs VPC endpoint. | `bool` | `false` | no | | [enable\_vpc\_ecr\_api\_endpoint](#input\_enable\_vpc\_ecr\_api\_endpoint) | Whether to create ECR API VPC endpoint. | `bool` | `false` | no | | [enable\_vpc\_ecr\_dkr\_endpoint](#input\_enable\_vpc\_ecr\_dkr\_endpoint) | Whether to create ECR DKR VPC endpoint. | `bool` | `false` | no | diff --git a/infrastructure/modules/networking/main.tf b/infrastructure/modules/networking/main.tf index 55138ffcd9..d977246eb2 100644 --- a/infrastructure/modules/networking/main.tf +++ b/infrastructure/modules/networking/main.tf @@ -49,7 +49,6 @@ resource "aws_internet_gateway" "main" { } resource "aws_eip" "nat" { - count = var.enable_nat_gateway ? 1 : 0 depends_on = [aws_internet_gateway.main] domain = "vpc" tags = merge(var.common_tags, { @@ -58,8 +57,7 @@ resource "aws_eip" "nat" { } resource "aws_nat_gateway" "main" { - count = var.enable_nat_gateway ? 1 : 0 - allocation_id = aws_eip.nat[0].id + allocation_id = aws_eip.nat.id depends_on = [aws_internet_gateway.main] subnet_id = aws_subnet.public[0].id tags = merge(var.common_tags, { @@ -67,6 +65,16 @@ resource "aws_nat_gateway" "main" { }) } +moved { + from = aws_eip.nat[0] + to = aws_eip.nat +} + +moved { + from = aws_nat_gateway.main[0] + to = aws_nat_gateway.main +} + resource "aws_route_table" "public" { tags = merge(var.common_tags, { Name = "${var.project_name}-${var.environment}-public-rt" @@ -85,12 +93,9 @@ resource "aws_route_table" "private" { }) vpc_id = aws_vpc.main.id - dynamic "route" { - for_each = var.enable_nat_gateway ? [1] : [] - content { - cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.main[0].id - } + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.main.id } } diff --git a/infrastructure/modules/networking/tests/unit.tftest.hcl b/infrastructure/modules/networking/tests/unit.tftest.hcl index 520aa57c48..333f960013 100644 --- a/infrastructure/modules/networking/tests/unit.tftest.hcl +++ b/infrastructure/modules/networking/tests/unit.tftest.hcl @@ -1,10 +1,17 @@ -mock_provider "aws" {} +mock_provider "aws" { + override_during = plan + + mock_resource "aws_nat_gateway" { + defaults = { + id = "nat-000000000000test" + } + } +} variables { availability_zones = ["us-east-2a", "us-east-2b", "us-east-2c"] aws_region = "us-east-2" common_tags = { Environment = "test", Project = "nest" } - enable_nat_gateway = true enable_vpc_cloudwatch_logs_endpoint = false enable_vpc_ecr_api_endpoint = false enable_vpc_ecr_dkr_endpoint = false @@ -143,28 +150,11 @@ run "test_internet_gateway_name_format" { } } -run "test_nat_eip_created_when_enabled" { - command = plan - - variables { - enable_nat_gateway = true - } - - assert { - condition = length(aws_eip.nat) == 1 - error_message = "NAT EIP must be created when enable_nat_gateway is true." - } -} - run "test_nat_eip_name_format" { command = plan - variables { - enable_nat_gateway = true - } - assert { - condition = aws_eip.nat[0].tags["Name"] == "${var.project_name}-${var.environment}-nat-eip" + condition = aws_eip.nat.tags["Name"] == "${var.project_name}-${var.environment}-nat-eip" error_message = "NAT EIP name must follow format: {project}-{environment}-nat-eip." } } @@ -172,65 +162,27 @@ run "test_nat_eip_name_format" { run "test_nat_eip_domain_is_vpc" { command = plan - variables { - enable_nat_gateway = true - } - assert { - condition = aws_eip.nat[0].domain == "vpc" + condition = aws_eip.nat.domain == "vpc" error_message = "NAT EIP domain must be VPC." } } -run "test_nat_eip_not_created_when_disabled" { - command = plan - - variables { - enable_nat_gateway = false - } - - assert { - condition = length(aws_eip.nat) == 0 - error_message = "NAT EIP must not be created when enable_nat_gateway is false." - } -} - -run "test_nat_gateway_created_when_enabled" { - command = plan - - variables { - enable_nat_gateway = true - } - - assert { - condition = length(aws_nat_gateway.main) == 1 - error_message = "NAT Gateway must be created when enable_nat_gateway is true." - } -} - run "test_nat_gateway_name_format" { command = plan - variables { - enable_nat_gateway = true - } - assert { - condition = aws_nat_gateway.main[0].tags["Name"] == "${var.project_name}-${var.environment}-nat" + condition = aws_nat_gateway.main.tags["Name"] == "${var.project_name}-${var.environment}-nat" error_message = "NAT gateway name must follow format: {project}-{environment}-nat." } } -run "test_nat_gateway_not_created_when_disabled" { +run "test_private_route_targets_nat_gateway" { command = plan - variables { - enable_nat_gateway = false - } - assert { - condition = length(aws_nat_gateway.main) == 0 - error_message = "NAT Gateway must not be created when enable_nat_gateway is false." + condition = one([for route in aws_route_table.private.route : route if route.cidr_block == "0.0.0.0/0"]).nat_gateway_id == aws_nat_gateway.main.id + error_message = "Private route table must route 0.0.0.0/0 through the NAT gateway." } } diff --git a/infrastructure/modules/networking/variables.tf b/infrastructure/modules/networking/variables.tf index 7d3856585f..c5bafb45e4 100644 --- a/infrastructure/modules/networking/variables.tf +++ b/infrastructure/modules/networking/variables.tf @@ -14,12 +14,6 @@ variable "common_tags" { default = {} } -variable "enable_nat_gateway" { - description = "Whether to enable a NAT Gateway." - type = bool - default = true -} - variable "enable_vpc_cloudwatch_logs_endpoint" { description = "Whether to create CloudWatch Logs VPC endpoint." type = bool