mirror of
https://github.com/Sea-Haven-Industries/engineering-handbook.git
synced 2026-09-30 08:03:16 +00:00
Some checks failed
ci / ci / ci (push) Has been cancelled
Make HCP Terraform plus GitHub Actions content CD the default for new workloads, and keep SAM/CDK documented as the remaining path.
3.3 KiB
3.3 KiB
Lambda Starter Template
Remaining SAM scaffold for an existing Python Lambda stack. New functions belong in an HCP Terraform repo; see terraform-project-layout.md and aws-infrastructure.md.
Project Structure
my-stack/
├── template.yaml
├── samconfig.toml.example
├── src/
│ └── handler/
│ ├── app.py
│ └── requirements.txt
└── .gitignore
See sam-project-layout.md for the full directory convention.
template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: my-stack — one-line purpose
Globals:
Function:
Runtime: python3.12
Architecture: arm64
Timeout: 30
MemorySize: 256
LoggingConfig:
LogFormat: JSON
Resources:
HandlerFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: my-stack-handler
CodeUri: src/handler/
Handler: app.handler
Environment:
Variables:
CONFIG_SECRET: my-stack/config
Policies:
- AWSLambdaBasicExecutionRole
- Statement:
- Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:my-stack/*
HandlerLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${HandlerFunction}
RetentionInDays: 60
Outputs:
HandlerArn:
Description: Handler Lambda ARN
Value: !GetAtt HandlerFunction.Arn
Key points:
Globals.Functionsets runtime, architecture, and JSON logging once for the whole template.- The
LogGroupis declared explicitly withRetentionInDays: 60. Omit it and CloudWatch creates the log group on first invocation with no retention — logs accumulate forever. - IAM scopes
secretsmanager:GetSecretValueto the stack's secret prefix only. Add specific permissions as needed; never useAdministratorAccess.
src/handler/app.py
import json
import os
import boto3
_secrets_client = boto3.client("secretsmanager")
_config = None
def _get_config():
global _config
if _config is None:
resp = _secrets_client.get_secret_value(SecretId=os.environ["CONFIG_SECRET"])
_config = json.loads(resp["SecretString"])
return _config
def handler(event, context):
config = _get_config()
# ... your logic ...
return {"statusCode": 200, "body": json.dumps({"ok": True})}
The module-level _config global caches the secret across warm invocations. The first call per cold start hits Secrets Manager; subsequent calls reuse the cached value. See secrets-and-config.md for the rationale.
src/handler/requirements.txt
Keep this file in every function directory even when empty — SAM looks for it during sam build.
# Per-function dependencies. Leave empty if the function uses only boto3 and stdlib.
Naming Reminders
FunctionNamemust be kebab-case and start with the stack name (my-stack-handler).- Secret IDs use
stack-name/secret-name. - Stack name itself is set in
samconfig.toml, not the template — match the repo name.