## Running AWS SAM Lambdas in Private Subnets (with defaults and optional VPC endpoints)

This guide standardizes how we place all SAM Lambda functions in private subnets and avoid deploy prompts by using parameter defaults. It also includes optional VPC Interface Endpoints to reduce NAT traffic.

### Prerequisites
- VPC with:
  - 2 private subnets in different AZs, routed to a NAT Gateway
  - 1 or more public subnets with an Internet Gateway (for the NAT)
- Lambda needs outbound HTTPS to:
  - AWS services (SSM, CloudWatch Logs, KMS, Secrets Manager)
  - External services (e.g., Supabase, Gmail)
- Recommended: Enable VPC DNS hostnames and resolution.

### Template changes (add once per SAM template)
Add parameters with defaults, a Lambda egress-only security group, and wire `Globals.Function.VpcConfig` so every function inherits the VPC config.

```yaml
Parameters:
  VpcId:
    Type: AWS::EC2::VPC::Id
    Default: vpc-<your-vpc-id>
    Description: VPC where Lambdas will run
  PrivateSubnet1Id:
    Type: AWS::EC2::Subnet::Id
    Default: subnet-<private-a>
    Description: First private subnet ID (with NAT egress)
  PrivateSubnet2Id:
    Type: AWS::EC2::Subnet::Id
    Default: subnet-<private-b>
    Description: Second private subnet ID (with NAT egress)

Resources:
  LambdaSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Egress-only security group for Lambda ENIs
      VpcId: !Ref VpcId
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0

Globals:
  Function:
    VpcConfig:
      SecurityGroupIds:
        - !Ref LambdaSecurityGroup
      SubnetIds:
        - !Ref PrivateSubnet1Id
        - !Ref PrivateSubnet2Id
```

Notes:
- With defaults present, `sam deploy` won’t prompt for these values.
- Override per environment via `samconfig.toml` or `--parameter-overrides` when needed.

### Optional: VPC Interface Endpoints (reduce NAT traffic for AWS services)
Add interface endpoints for AWS APIs used by Lambdas (traffic stays inside VPC). Keep NAT for any public internet services (e.g., Supabase/Gmail).

```yaml
Parameters:
  VpcCidr:
    Type: String
    Default: 10.0.0.0/16

Resources:
  VpcEndpointSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTPS from private subnets to interface endpoints
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: !Ref VpcCidr
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0

  SsmEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VpcId
      VpcEndpointType: Interface
      ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
      PrivateDnsEnabled: true
      SubnetIds:
        - !Ref PrivateSubnet1Id
        - !Ref PrivateSubnet2Id
      SecurityGroupIds:
        - !Ref VpcEndpointSecurityGroup

  LogsEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VpcId
      VpcEndpointType: Interface
      ServiceName: !Sub com.amazonaws.${AWS::Region}.logs
      PrivateDnsEnabled: true
      SubnetIds:
        - !Ref PrivateSubnet1Id
        - !Ref PrivateSubnet2Id
      SecurityGroupIds:
        - !Ref VpcEndpointSecurityGroup

  KmsEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VpcId
      VpcEndpointType: Interface
      ServiceName: !Sub com.amazonaws.${AWS::Region}.kms
      PrivateDnsEnabled: true
      SubnetIds:
        - !Ref PrivateSubnet1Id
        - !Ref PrivateSubnet2Id
      SecurityGroupIds:
        - !Ref VpcEndpointSecurityGroup

  SecretsManagerEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VpcId
      VpcEndpointType: Interface
      ServiceName: !Sub com.amazonaws.${AWS::Region}.secretsmanager
      PrivateDnsEnabled: true
      SubnetIds:
        - !Ref PrivateSubnet1Id
        - !Ref PrivateSubnet2Id
      SecurityGroupIds:
        - !Ref VpcEndpointSecurityGroup
```

Optional S3 Gateway endpoint for private S3 access (no NAT):

```yaml
Parameters:
  PrivateRouteTable1Id:
    Type: String
    Default: rtb-xxxxxxxxxxxxxxxxx
  PrivateRouteTable2Id:
    Type: String
    Default: rtb-yyyyyyyyyyyyyyyyy

Resources:
  S3GatewayEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VpcId
      VpcEndpointType: Gateway
      ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
      RouteTableIds:
        - !Ref PrivateRouteTable1Id
        - !Ref PrivateRouteTable2Id
```

### Deploy
1. `sam build`
2. `sam deploy` (no prompts if defaults are set)
3. In the AWS Console, verify each Lambda shows the correct VPC, subnets, and security group.

### Connectivity checks
- AWS services: SSM/Logs/KMS/SecretsManager work via endpoints (if added) or NAT.
- External services: Supabase/Gmail over NAT (ensure SG egress allows 443 and SMTP ports if used).

### Troubleshooting
- Timeouts: check private subnet route tables `0.0.0.0/0 → NAT`.
- DNS errors: enable VPC DNS hostnames and resolution.
- NAT cost: add VPC endpoints for AWS services to reduce usage.
- ENI/IP exhaustion: ensure private subnets have enough free IPs for your concurrency.


