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
SAM Project Layout
Remaining path for stacks that already use SAM. New deployables use terraform-project-layout.md and hcp-terraform.md.
Standard Directory Structure
project-name/
├── template.yaml # SAM template at root
├── samconfig.toml # Deploy config (gitignored)
├── samconfig.toml.example # Template for onboarding (committed)
├── src/
│ ├── function_name/
│ │ ├── app.py # Lambda handler
│ │ └── requirements.txt # Per-function dependencies
│ └── shared/ # Shared layer code (if needed)
└── .gitignore
File Purposes
template.yaml
The SAM/CloudFormation template. Lives at the project root. Defines all Lambda functions, IAM roles, API Gateway endpoints, DynamoDB tables, and other resources.
samconfig.toml
Contains real ARNs, S3 bucket names, and deploy parameters. Gitignored because it varies per environment and may contain account-specific values.
samconfig.toml.example
A committed template showing the expected structure and parameter names. New contributors copy this to samconfig.toml and fill in their values.
src/function_name/
One directory per Lambda function. Each contains its own handler (app.py) and dependencies (requirements.txt). This keeps functions independently deployable and avoids bloating one function with another's dependencies.
src/shared/
Optional. Used for code shared across multiple functions, typically deployed as a Lambda layer. See the next section for layout details.
Lambda Layers
When sharing code across functions via a layer, the source layout matters because SAM transforms ContentUri depending on whether BuildMethod is set.
Correct layout (with BuildMethod)
src/shared/
├── shared/ # Package goes here directly — SAM wraps in python/ at build time
│ ├── __init__.py
│ └── utils.py
└── requirements.txt # Layer-level pip dependencies
Template:
SharedLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: my-stack-shared
ContentUri: src/shared/
CompatibleRuntimes:
- python3.12
CompatibleArchitectures:
- arm64
Metadata:
BuildMethod: python3.12
BuildArchitecture: arm64
BuildMethod nesting gotcha
With BuildMethod: python3.12, SAM copies ContentUri into a python/ subdirectory during build, then pip-installs requirements.txt deps into that same python/ directory.
Do not include a python/ wrapper in your source — SAM adds it. The wrong layout:
src/shared/
├── python/ # SAM wraps this again → python/python/shared/ — module unreachable
│ └── shared/
└── requirements.txt
A real production incident (~22 hours of outage) traced to this exact pattern when a refactor moved layer code under an extra python/ directory.
Without BuildMethod (raw zip)
If the layer has no pip dependencies and you omit BuildMethod, SAM zips ContentUri as-is — you DO need the python/ wrapper. Reserve raw zip for layers that ship only Python source.
Standard .gitignore
.aws-sam/
__pycache__/
*.pyc
.env
samconfig.toml