Lambda functions should be in a VPC
Lambda functions outside your VPC cannot directly reach private resources like RDS instances, ElastiCache clusters, or internal APIs without either exposing those resources publicly or adding intermediary access patterns. VPC placement lets you apply security group rules and network ACLs to control inbound and outbound traffic, shrinking the attack surface.
Without VPC attachment, VPC Flow Logs won't capture Lambda network traffic, which makes incident investigation harder. For workloads processing regulated data (cardholder, PHI, or federal records), VPC placement is typically a baseline expectation, not an optional hardening step.
Retrofit consideration
Moving an existing Lambda function into a VPC changes its network path. Functions that previously reached public AWS APIs (S3, DynamoDB, SQS) directly will lose connectivity unless you provision NAT gateways or VPC endpoints. This can cause immediate runtime failures. Test thoroughly in a staging environment before applying.
Implementation
Choose the approach that matches how you manage Terraform.
Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.
module "lambda" {
source = "soc2.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "pcidss.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "hipaa.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "nist80053.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "fedrampmoderate.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "cisv80ig1.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "nist800171.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "cisacyberessentials.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "nydfs23.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "ffiec.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "cfrpart11.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "rbicybersecurity.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "rbiitfnbfc.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "fedramplow.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "hipaasecurity2003.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "nistcsfv11.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "nist80053rev4.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
module "lambda" {
source = "pcidssv321.compliance.tf/terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
If you use terraform-aws-modules/lambda/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.
module "lambda" {
source = "terraform-aws-modules/lambda/aws"
version = ">=8.0.0"
create_package = false
function_name = "abc123"
handler = "index.lambda_handler"
local_existing_package = "lambda_function.zip"
runtime = "python3.12"
vpc_security_group_ids = ["sg-abc12345", "sg-def67890"]
vpc_subnet_ids = ["subnet-abc123", "subnet-def456"]
}
Use AWS provider resources directly. See docs for the resources involved: aws_lambda_function.
resource "aws_lambda_function" "this" {
filename = "lambda_function.zip"
function_name = "pofix-abc123"
handler = "index.handler"
reserved_concurrent_executions = 100
role = "arn:aws:iam::123456789012:role/example-role"
runtime = "python3.12"
source_code_hash = "base64encodedhashabcdef1234567890=="
vpc_config {
security_group_ids = ["sg-abc12345"]
subnet_ids = ["subnet-abc123", "subnet-def456"]
}
}
What this control checks
On the aws_lambda_function resource, a vpc_config block must be present and populated. Within that block, subnet_ids must contain at least one subnet ID and security_group_ids must contain at least one security group ID. A function that omits vpc_config entirely, or provides empty lists for either argument, fails this control.
The subnets should be private subnets to maximize network isolation, and security groups should restrict egress to only the destinations the function actually needs. The execution role also requires ec2:CreateNetworkInterface, ec2:DescribeNetworkInterfaces, and ec2:DeleteNetworkInterface; attaching the AWSLambdaVPCAccessExecutionRole managed policy covers these. Without them, VPC attachment fails at deploy time.
Common pitfalls
Loss of internet connectivity after VPC placement
Any public endpoint call, including AWS service APIs like S3 or DynamoDB, will time out once the function is inside a VPC. Private subnets need a route to a NAT gateway, or you need VPC endpoints for each service the function calls. This catches teams off guard when they move functions into VPCs without auditing outbound dependencies first.
Insufficient ENI/IP capacity in subnets
Small subnets cause invocation failures under load. Lambda's Hyperplane ENIs reduce IP consumption compared to the old model, but subnet IP capacity still gets consumed. If your subnets use /28 or /27 ranges, you can hit exhaustion errors during burst traffic with ENI or subnet IP exhaustion related errors in the logs. Size subnets generously and spread across multiple AZs via
subnet_ids.Missing IAM permissions for ENI management
Attach
arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRoleto the execution role before deploying. Withoutec2:CreateNetworkInterface,ec2:DescribeNetworkInterfaces, andec2:DeleteNetworkInterface, function creation or updates will fail with an opaque permissions error that doesn't clearly point back to the missing grants.Cold start latency increase
VPC-attached functions still carry marginally higher cold start times than non-VPC functions. Hyperplane ENI improvements reduced the gap significantly, but it hasn't disappeared entirely. For latency-sensitive workloads, account for this in your SLOs before committing to VPC placement across the board.
Using default VPC or public subnets
Placing a function in the default VPC or a public subnet technically passes this control, but you've gained almost nothing. The default VPC is flat and broadly accessible; public subnets expose the function to internet-originating traffic. Use purpose-built private subnets in a non-default VPC with security groups scoped to actual traffic requirements.
Audit evidence
An auditor expects AWS Config evaluation results from a custom rule or conformance pack control confirming VPC attachment across all Lambda functions. Supporting evidence includes the Lambda console's Configuration > VPC tab showing assigned subnets and security groups, or aws lambda get-function-configuration output with a populated VpcConfig object containing non-empty SubnetIds and SecurityGroupIds arrays.
VPC Flow Logs capturing Lambda ENI traffic provide additional proof that functions operate within the expected network boundary. Prowler or Security Hub scan reports flagging non-VPC functions round out the evidence package.
Framework-specific interpretation
SOC 2: CC6.1 expects logical access controls at system boundaries. Running Lambda outside a VPC puts it in a flat, uncontrolled network context with no boundary auditors can point to. VPC attachment gives you a defined perimeter and security group rules you can present as evidence of boundary enforcement.
PCI DSS v4.0: For cardholder data environments in AWS, Requirement 1 means Lambda functions handling card data belong in a VPC segment where security group rules restrict ingress and egress to known, authorized paths. Running those functions outside a VPC leaves no firewall-equivalent control between them and untrusted network space.
HIPAA Omnibus Rule 2013: 45 CFR 164.312(e)(1) requires measures to guard ePHI in transit. VPC placement routes Lambda traffic over private network paths, with security groups restricting access to authorized endpoints only. Functions processing PHI that run outside a VPC lack that enforceable network-layer boundary.
NIST SP 800-53 Rev 5: Maps to SC-7 (Boundary Protection) and AC-4 (Information Flow Enforcement). VPC attachment puts the function inside a defined perimeter, and security groups act as the enforcement point for approved traffic flows between the function and other system components.
FedRAMP Moderate Baseline Rev 4: SC-7 requires a managed boundary between Lambda and other system components, with controlled interfaces for all function communications. Security groups and NACLs on VPC-attached functions act as those boundary devices, which is what FedRAMP Moderate boundary protection controls are looking for.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
lambda_function_in_vpcAWS Config Managed Rule:
LAMBDA_INSIDE_VPCCheckov Check:
CKV_AWS_117Powerpipe Control:
aws_compliance.control.lambda_function_in_vpcProwler Check:
awslambda_function_inside_vpcAWS Security Hub Control:
Lambda.3
Last reviewed: 2026-03-09