Lambda Environment Variables: Inject Config Safely and Encrypt with KMS
Hardcoding a database endpoint inside your Lambda function is a deployment anti-pattern — it couples your code to infrastructure, breaks multi-environment workflows, and creates a security liability. Lambda Environment Variables solve this cleanly, and AWS KMS adds an encryption layer for sensitive values like connection strings or API keys.
TL;DR
| Concern | Solution | Key Detail |
|---|---|---|
| Inject DB endpoint without hardcoding | Lambda Environment Variable | Set DB_ENDPOINT at deploy time; read via os.environ |
| Protect sensitive values at rest | KMS Customer Managed Key (CMK) | Lambda encrypts env vars using your CMK; decrypts on cold start |
| Minimum IAM for KMS decryption | kms:Decrypt on the CMK ARN |
Attach to the Lambda execution role only |
| Avoid secret sprawl | Combine with SSM Parameter Store or Secrets Manager | For rotation needs, env vars alone are insufficient |
How Lambda Environment Variables Work Internally
Lambda stores environment variables as key-value pairs attached to a function version. At the infrastructure level, AWS encrypts all environment variables at rest by default using an AWS-managed key (aws/lambda). When you specify a Customer Managed Key (CMK), Lambda uses that key instead, giving you full audit control via AWS CloudTrail.
The analogy: Think of environment variables as a sealed envelope handed to your function when it wakes up. By default, AWS seals it with their master lock. When you bring your own KMS key, you're using your own padlock — only your Lambda execution role has the key to open it.
The decryption happens before your handler code runs, during the Lambda execution environment initialization (cold start). Your code reads plain-text values from os.environ — it never sees the encrypted form.
Data Flow: From Deployment to Runtime
Step-by-Step Implementation
Phase 1: Create a KMS Customer Managed Key
- Create a CMK and note its ARN — this will be referenced in both the Lambda config and the IAM policy.
aws kms create-key \
--description "Lambda env var encryption key" \
--key-usage ENCRYPT_DECRYPT \
--query 'KeyMetadata.Arn' \
--output text
# Optionally create a human-readable alias
aws kms create-alias \
--alias-name alias/lambda-env-key \
--target-key-id <KEY_ID>
Phase 2: Grant the Lambda Execution Role kms:Decrypt
- Apply least-privilege: the execution role needs only
kms:Decrypton this specific CMK ARN. - Do not grant
kms:Encryptorkms:GenerateDataKeyto the Lambda role — those are only needed by the Lambda service during deployment.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowLambdaDecryptEnvVars",
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:us-east-1:123456789012:key/<KEY_ID>"
}
]
}
aws iam put-role-policy \
--role-name my-lambda-execution-role \
--policy-name LambdaKMSDecrypt \
--policy-document file://kms-decrypt-policy.json
Phase 3: Deploy the Lambda with Environment Variables and CMK
- Pass the
--kms-key-arnflag to instruct Lambda to use your CMK instead of the default AWS-managed key. - The
DB_ENDPOINTvalue is encrypted in transit (TLS) and at rest (KMS) automatically.
aws lambda update-function-configuration \
--function-name my-function \
--kms-key-arn arn:aws:kms:us-east-1:123456789012:key/<KEY_ID> \
--environment "Variables={DB_ENDPOINT=mydb.cluster-xyz.us-east-1.rds.amazonaws.com,DB_PORT=5432}"
Phase 4: Read the Variable in Your Handler
- Lambda decrypts the values before your handler is invoked — your code reads plain text directly from the process environment.
- No SDK calls to KMS are needed in your application code; the Lambda service handles decryption transparently.
import os
import psycopg2
DB_ENDPOINT = os.environ['DB_ENDPOINT']
DB_PORT = int(os.environ.get('DB_PORT', 5432))
def handler(event, context):
conn = psycopg2.connect(
host=DB_ENDPOINT,
port=DB_PORT,
database="mydb",
user=os.environ['DB_USER'],
password=os.environ['DB_PASSWORD']
)
# ... query logic
return {"statusCode": 200}
Terraform Alternative
- Infrastructure-as-code is the preferred approach for reproducible deployments across environments.
resource "aws_lambda_function" "my_function" {
function_name = "my-function"
role = aws_iam_role.lambda_exec.arn
handler = "app.handler"
runtime = "python3.12"
filename = "function.zip"
kms_key_arn = aws_kms_key.lambda_env_key.arn
environment {
variables = {
DB_ENDPOINT = var.db_endpoint
DB_PORT = "5432"
}
}
}
When Environment Variables Are Not Enough
- Secret rotation: Env vars are static per deployment. If your DB password rotates, you must redeploy. Use AWS Secrets Manager with the SDK for automatic rotation support.
- Secrets larger than 4 KB: Lambda env var total size is capped at 4 KB. Use SSM Parameter Store (SecureString) or Secrets Manager for larger payloads.
- Cross-function sharing: Env vars are scoped to a single function. SSM Parameter Store is better for shared config across multiple Lambdas.
Cost Impact
- Default AWS-managed key (
aws/lambda): Free. No additional KMS charges. - Customer Managed Key (CMK): $1.00/month per key + $0.03 per 10,000 API calls. Each Lambda cold start triggers one
kms:DecryptAPI call. At moderate invocation rates with warm containers, actual API call costs are minimal. - Secrets Manager: $0.40/secret/month + $0.05 per 10,000 API calls — justified only when rotation is required.
IAM Permissions Summary
| Principal | Required Permission | Reason |
|---|---|---|
| Lambda Execution Role | kms:Decrypt |
Decrypt env vars at cold start |
| Deploying IAM User/Role (CI/CD) | kms:Encrypt, kms:DescribeKey |
Encrypt values when setting function config |
| Lambda Service Principal | Granted via KMS Key Policy | Allow Lambda service to use the key on behalf of the function |
Glossary
- Customer Managed Key (CMK): A KMS encryption key you create and control, enabling custom key policies, rotation schedules, and CloudTrail audit logs.
- Lambda Execution Role: The IAM role assumed by the Lambda service when running your function, defining what AWS resources the function can access.
- Cold Start: The initialization phase of a new Lambda execution environment, where the runtime is bootstrapped and environment variables are decrypted before your handler runs.
- Least Privilege: The IAM principle of granting only the minimum permissions required for a task — nothing more.
- SSM Parameter Store: An AWS Systems Manager feature for storing configuration data and secrets as key-value pairs, with optional KMS encryption via SecureString type.
Comments
Post a Comment