`private_subnet_attributes_by_az` includes isolated subnets – causes compute workloads to deploy into non-routable networks

We are using the module:

aws-ia/terraform-aws-vpc  (version >= 4.4.4)

While working with this module, we observed that the output:

private_subnet_attributes_by_az

includes all non-public subnets, such as:

  • Private subnets (NAT-connected)
  • Isolated subnets (no outbound access)

This behavior is technically correct from an AWS networking perspective (public vs non-public). However, the output name is misleading. Many users naturally assume that private_subnet_attributes_by_az represents only NAT-enabled private subnets suitable for compute workloads.

In our environment, this misunderstanding caused AWS Lambda functions to be deployed into isolated subnets. Since isolated subnets have no outbound connectivity, Lambda was unable to reach AWS services (e.g., DynamoDB, STS, CloudWatch), resulting in timeouts and API Gateway 504 errors.


Actual Behavior

  • private_subnet_attributes_by_az aggregates all non-public subnets without distinguishing:
    • NAT-enabled private subnets
    • Isolated / non-routable subnets
  • Consumers must manually filter this output to safely deploy compute resources.

My Terraform Scripts:

VPC.tf

module “main_vpc” {
source = “aws-ia/vpc/aws”
version = “>= 4.4.4”

name = “{var.organization}-{var.environment}-vpc”
cidr_block = var.cidr_block
az_count = 3

Enable NAT gateway in all AZs for prod, single AZ for non-prod

subnets = {
# Public subnet with IGW
public = {
netmask = 24
# Deploy NAT gateway in all AZs for prod, single AZ for non-prod
nat_gateway_configuration = var.is_environment_prod ? “all_azs” : “single_az”
}

# Private subnet with NAT gateway for outbound
private = {
  netmask                 = 24
  connect_to_public_natgw = true
}

Isolated private subnet with no internet connectivity

isolated = {
  netmask = 24
}

}
}

OUTPUT.tf

output “vpc_id” {
value = module.main_vpc.vpc_attributes.id
description = “VPC ID”
}

output “vpc_arn” {
value = module.main_vpc.vpc_attributes.arn
description = “VPC ARN”
}

output “private_subnet_ids” {
description = “List of private subnet IDs (with NAT gateway).”
value = [for _, value in module.main_vpc.private_subnet_attributes_by_az : value.id]
}

user-service.tf

data “aws_s3_object” “user_service” {
bucket = var.artifacts_bucket_name
key = “apps/user-service/latest/index.js.zip”
}

resource “aws_lambda_function” “user_service” {
function_name = “user-service”
role = aws_iam_role.lambda_primary_execution_role.arn
handler = “index.handler”
runtime = “nodejs24.x”
memory_size = 512
timeout = 60
publish = true

s3_bucket = var.artifacts_bucket_name
s3_key = “apps/user-service/latest/index.js.zip”
source_code_hash = data.aws_s3_object.user_service.etag

vpc_config {
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.lambda_primary_sg.id]
}

logging_config {
log_format = “JSON”
application_log_level = “TRACE”
system_log_level = “DEBUG”
}

environment {
variables = {
NODE_ENV = “production”
ENVIRONMENT = var.environment
LOG_LEVEL = “INFO”
}
}
}

locals {
user_service_endpoints = {
“users_get” = {
method = “GET”
path = “/v1/users”
}
“users_post” = {
method = “POST”
path = “/v1/users”
}
“users_id_get” = {
method = “GET”
path = “/v1/users/{userId}”
}
“users_id_patch” = {
method = “PATCH”
path = “/v1/users/{userId}”
}
“users_id_delete” = {
method = “DELETE”
path = “/v1/users/{userId}”
}
}
}

Expected Behavior

One of the following improvements would help prevent this confusion:

Option A

  • Provide a dedicated output for NAT-enabled private subnets, e.g.:

    nat_private_subnet_attributes_by_az
    
    

Option B

  • Clarify documentation that private_subnet_attributes_by_az means all non-public subnets, not necessarily NAT-connected.

Option C

  • Add metadata or flags to distinguish isolated vs NAT-connected subnets more explicitly in outputs.

@malyadrireddykypu Chances are the module’s maintainers are not actively monitoring HashiCorp Discuss. For any module issues, you should first check the module page in the Terraform Registry which typically has a link to the respective GitHub repo to report issues.

1 Like