Skip to main content
INS // Insights

AWS Lambda in Government: Compliance Patterns

Updated April 2026 · 7 min read

AWS Lambda is fully authorized in AWS GovCloud under FedRAMP High, and it's heavily used in government cloud programs for event-driven processing, API backends, and automation. But Lambda in a compliance environment has specific configuration requirements that differ from commercial Lambda deployments.

Here's how Rutagon configures Lambda for FedRAMP and CMMC-adjacent government programs — with the specific patterns that satisfy compliance controls and avoid the production failures that catch teams off guard.

Lambda in GovCloud: What's Different

AWS Lambda in GovCloud (us-gov-west-1 and us-gov-east-1) is functionally identical to commercial Lambda in most respects. The differences:

  • ARN format: arn:aws-us-gov:lambda:us-gov-west-1:ACCOUNT_ID:function:function-name
  • IAM policy conditions: Some condition keys behave slightly differently; verify government-specific behavior in testing
  • Service availability: Most Lambda features (provisioned concurrency, Lambda Extensions, container images, ARM64) are available in GovCloud, but verify current availability for features added recently
  • Endpoint URLs: If using Lambda Function URLs, these are GovCloud-specific endpoints

In our Terraform configuration:

# Lambda function in GovCloud — production configuration
resource "aws_lambda_function" "api_handler" {
  filename         = data.archive_file.api_handler.output_path
  source_code_hash = data.archive_file.api_handler.output_base64sha256
  function_name    = "${var.app_name}-${var.environment}-api-handler"
  role             = aws_iam_role.lambda_execution.arn
  handler          = "handler.lambda_handler"
  runtime          = "python3.12"
  
  # VPC configuration — required for CUI environment isolation
  vpc_config {
    subnet_ids         = var.private_subnet_ids
    security_group_ids = [aws_security_group.lambda_sg.id]
  }
  
  environment {
    variables = {
      ENVIRONMENT     = var.environment
      SECRET_ARN      = aws_secretsmanager_secret.db_credentials.arn
      # No plaintext secrets in environment variables
    }
  }
  
  # KMS encryption for environment variables — SC-28
  kms_key_arn = aws_kms_key.lambda_env.arn
  
  # Tracing for audit — AU-12
  tracing_config {
    mode = "Active"
  }
  
  # Dead letter queue for error audit trail — AU-6
  dead_letter_config {
    target_arn = aws_sqs_queue.lambda_dlq.arn
  }
  
  reserved_concurrent_executions = var.max_concurrency
  
  tags = local.compliance_tags
}

VPC Placement: Required for CUI Workloads

Lambda functions that process CUI must run inside your VPC — not the default Lambda execution environment, which has internet access. VPC placement:

  • Network isolation: Lambda in your VPC is subject to your security groups and NACLs, can reach resources in your private subnets (RDS, ElastiCache, internal APIs), and doesn't have direct internet egress
  • SC-7 compliance: VPC placement supports boundary protection — the Lambda function is inside your authorization boundary, not executing in a shared AWS-managed network
  • Traffic control: Outbound traffic from Lambda goes through your NAT gateway (auditable, can be restricted via security groups), not directly to the internet

The tradeoff: VPC cold starts take longer (ENI attachment), and you consume IP addresses from your VPC subnets. For production government workloads, provisioned concurrency addresses cold start; CIDR planning addresses IP address consumption.

resource "aws_security_group" "lambda_sg" {
  name_prefix = "${var.app_name}-lambda-"
  vpc_id      = var.vpc_id
  description = "Lambda execution SG — no inbound, controlled egress"
  
  # No inbound rules — Lambda is invoked by events, not direct connections
  
  # Egress: only to internal VPC resources + Secrets Manager endpoint
  egress {
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    prefix_list_ids = [data.aws_prefix_list.secrets_manager.id]
    description     = "Secrets Manager VPC endpoint"
  }
  
  egress {
    from_port   = 5432
    to_port     = 5432
    protocol    = "tcp"
    cidr_blocks = [var.db_subnet_cidr]
    description = "PostgreSQL in private subnet"
  }
  
  # Block all other egress by default
  lifecycle {
    create_before_destroy = true
  }
}

IAM Execution Role: Least Privilege

Lambda's execution role is the most common over-provisioning point. Teams grant wide IAM policies because it's easier than scoping precisely — creating a compliance finding (AC-6 Least Privilege) and an unnecessary blast radius.

The pattern: scope the execution role to exactly the resources and actions the function actually uses, identified by analyzing the function's code.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SecretsManagerRead",
      "Effect": "Allow",
      "Action": ["secretsmanager:GetSecretValue"],
      "Resource": "arn:aws-us-gov:secretsmanager:us-gov-west-1:ACCOUNT_ID:secret:app/db-credentials-*"
    },
    {
      "Sid": "S3ReadReports",
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": [
        "arn:aws-us-gov:s3:::reports-bucket",
        "arn:aws-us-gov:s3:::reports-bucket/*"
      ]
    },
    {
      "Sid": "XRayTracing",
      "Effect": "Allow",
      "Action": ["xray:PutTraceSegments", "xray:PutTelemetryRecords"],
      "Resource": "*"
    },
    {
      "Sid": "VPCNetworking",
      "Effect": "Allow",
      "Action": [
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface"
      ],
      "Resource": "*"
    }
  ]
}

The Resource ARN scoping on Secrets Manager and S3 is critical — it limits the function to specific secrets and buckets, not all secrets or all buckets in the account.

Secrets Management: Never in Environment Variables as Plaintext

A common Lambda anti-pattern in non-compliance environments: storing database passwords or API keys directly in Lambda environment variables. Even with KMS encryption, this means the secret value flows through Lambda's configuration API and is visible to anyone with lambda:GetFunctionConfiguration access.

The compliant pattern: store secrets in Secrets Manager, retrieve them at function startup, cache for the function lifetime.

import boto3
import json
import os
from functools import lru_cache

@lru_cache(maxsize=None)
def get_secret(secret_arn: str) -> dict:
    """
    Retrieve and cache secret at Lambda init time.
    Cache persists across warm invocations — cold starts re-fetch.
    """
    client = boto3.client('secretsmanager', 
                          region_name=os.environ['AWS_REGION'])
    response = client.get_secret_value(SecretId=secret_arn)
    return json.loads(response['SecretString'])

# At module load time (cold start) — not per invocation
DB_CREDENTIALS = get_secret(os.environ['SECRET_ARN'])
DB_HOST = DB_CREDENTIALS['host']
DB_PASSWORD = DB_CREDENTIALS['password']

This pattern fetches the secret during Lambda's init phase (cold start), not on every invocation, keeping latency overhead minimal for warm invocations.

Cold Start Management for Government APIs

Cold starts in VPC Lambda can be significant — 500ms to 2+ seconds for VPC ENI attachment. For government APIs with response SLAs, this is unacceptable for user-facing endpoints.

Provisioned concurrency is the solution:

resource "aws_lambda_provisioned_concurrency_config" "api_handler" {
  function_name                  = aws_lambda_function.api_handler.function_name
  qualifier                      = aws_lambda_alias.production.name
  provisioned_concurrent_executions = var.min_provisioned_concurrency
}

# Auto-scaling for variable load
resource "aws_appautoscaling_target" "lambda_concurrency" {
  max_capacity       = var.max_concurrency
  min_capacity       = var.min_provisioned_concurrency
  resource_id        = "function:${aws_lambda_function.api_handler.function_name}:${aws_lambda_alias.production.name}"
  scalable_dimension = "lambda:function:ProvisionedConcurrency"
  service_namespace  = "lambda"
}

Provisioned concurrency keeps Lambda instances warm, eliminates cold starts for provisioned capacity, and scales based on actual demand patterns. For government APIs with predictable usage windows (business hours, batch processing), scheduled scaling is often sufficient and more cost-efficient than always-on minimum concurrency.

Discuss your serverless government architecture →

Frequently Asked Questions

Is AWS Lambda FedRAMP authorized in GovCloud?

Yes. AWS Lambda is FedRAMP High authorized in both AWS GovCloud US-East (us-gov-east-1) and US-West (us-gov-west-1) regions. You can verify current FedRAMP authorization status for specific Lambda features via AWS Artifact in GovCloud. The Lambda service itself is authorized; the workload running in Lambda must implement controls appropriate to the data sensitivity.

What NIST 800-53 controls does Lambda VPC placement address?

VPC placement for Lambda directly supports SC-7 (Boundary Protection) by placing Lambda execution within your controlled network boundary, subject to your security groups and NACLs. It also supports SC-3 (Security Function Isolation) for sensitive processing, and SC-28 (Protection of Information at Rest) when combined with KMS encryption on the execution environment. Network-level auditing of Lambda egress supports AU-2 and AU-12 (Audit Events and Audit Record Generation).

How do we handle Lambda timeouts in compliance logging?

Lambda invocations that hit the configured timeout generate an error event in CloudWatch Logs. For compliance purposes: ensure your Lambda timeout is configured appropriately (never 15 minutes for APIs — use 30s for user-facing), configure a Dead Letter Queue (DLQ) on SQS or SNS to capture failed/timed-out invocations for audit review, and include invocation request IDs in your log entries to trace individual invocations in the audit trail. AU-3 (Content of Audit Records) requires time stamps, event types, and source identity — structure your logging accordingly.

Can Lambda container images run in GovCloud?

Yes. Lambda container images (using the Lambda container runtime) are supported in GovCloud. For CMMC and FedRAMP programs, container images should be sourced from Iron Bank (Platform One's hardened container registry) or your own hardened image pipeline with Trivy scanning. Using public Docker Hub images directly is an SI-3 (Malicious Code Protection) finding — images must be scanned and verified before use in authorized environments.

What's the performance difference between Lambda and ECS in government APIs?

Lambda provides automatic scaling, zero server management, and per-invocation pricing — better for variable, bursty workloads. ECS Fargate provides consistent performance, more control over the runtime environment, and better economics for sustained high-throughput workloads. For government APIs with predictable load, ECS Fargate is often more cost-effective; for event-driven processing and low-to-moderate volume APIs, Lambda is excellent. Many government architectures use both — Lambda for event processing and automation, ECS Fargate for user-facing API services.

Discuss your project with Rutagon

Contact Us →

Ready to discuss your project?

We deliver production-grade software for government, defense, and commercial clients. Let's talk about what you need.

Initiate Contact