Exam Guide: Developer - Associate
ποΈ Domain 3: Deployment
π Task 1: Prepare Application Artifacts To Be Deployed To AWS
Before you can deploy anything to AWS, you need to package it properly. This task covers Lambda deployment packaging (zip vs container), managing dependencies, structuring projects for multi-environment deployment, and using AWS AppConfig for runtime configuration.
πConcepts
Lambda Deployment Packaging Options
| Option | Max Size | Build Complexity | Cold Start | Best For |
|---|---|---|---|---|
| Zip Package (inline editor) | 3 MB (editor limit) | None | Fastest | Simple functions, no dependencies |
| Zip Package (upload) | 50 MB compressed / 250 MB uncompressed | Low | Fast | Most Lambda functions |
| Zip + Lambda Layers | 250 MB total (function + all layers) | Medium | Fast | Shared dependencies across functions |
| Container Image | 10 GB | Higher | Slower (first invoke) | ML libraries, large dependencies, custom runtimes |
π‘If a scenario is about a deployment package exceeding 250 MB, the answer is container images. If it mentions sharing dependencies across multiple functions, the answer is Lambda Layers. Zip is the default for most workloads.
Lambda Layers
| Aspect | Detail |
|---|---|
| What They Are | Zip archives containing libraries, custom runtimes, or other dependencies |
| Max Layers Per Function | 5 |
| Size Limit | 250 MB total (function code + all layers uncompressed) |
| Versioning | Each publish creates an immutable version |
| Sharing | Can be shared across functions, accounts, or made public |
| Path | Contents extracted to /opt in the execution environment |
Dependency Management Strategies
| Strategy | How It Works | Pros | Cons |
|---|---|---|---|
| Bundle In Zip | Install deps into package directory, zip together | Simple, self-contained | Larger package, duplicated across functions |
| Lambda Layers | Package deps as a layer, attach to functions | Shared across functions, smaller deploys | Layer version management, 5-layer limit |
| Container Image | Install deps in Dockerfile | Full control, large deps supported | Slower cold starts, ECR management |
| sam build | SAM resolves deps from requirements.txt automatically | Easiest, handles everything | Requires SAM CLI |
Environment-Specific Configuration Approaches
| Approach | When Config Is Resolved | Best For |
|---|---|---|
| SAM Parameters + samconfig.toml | Deploy time | Resource names, table names, stage-specific infra |
| Lambda Environment Variables | Deploy time (baked into function config) | Simple key-value config per environment |
| SSM Parameter Store | Runtime (fetched by function) | Config that changes without redeployment |
| AWS AppConfig | Runtime (with gradual rollout) | Feature flags, config that needs validation and rollback |
| Secrets Manager | Runtime (fetched by function) | Credentials that rotate |
π‘ If the question is change configuration without redeploying, the answer is Parameter Store or AppConfig. If it says gradual rollout or validate configuration before applying, the answer is AppConfig.
AWS AppConfig Overview
| Feature | Detail |
|---|---|
| What It Does | Deploy configuration changes independently from code, with validation and rollout strategies |
| Deployment Strategies | AllAtOnce, Linear, Exponential |
| Validators | JSON Schema or Lambda function to validate config before deployment |
| Rollback | Automatic rollback if CloudWatch alarm triggers during deployment |
| Integration | Lambda extension for caching, or direct API calls |
| Cost | Free for configuration retrieval. Charges for feature flag evaluations |
Project Structure Best Practices
A well-structured SAM project separates handlers, shared code, tests, and configuration:
my-app/
βββ template.yaml # SAM/CloudFormation template
βββ samconfig.toml # Per-environment deployment config
βββ src/
β βββ handlers/ # One file per Lambda function
β β βββ create_order.py
β β βββ get_order.py
β β βββ process_payment.py
β βββ shared/ # Shared utilities across handlers
β βββ models.py
β βββ utils.py
βββ tests/
β βββ unit/ # Fast, no AWS calls
β β βββ test_create_order.py
β β βββ test_get_order.py
β βββ integration/ # Against deployed resources
β βββ test_api.py
βββ events/ # Sample event payloads for testing
β βββ create_order.json
β βββ get_order.json
βββ requirements.txt # Python dependencies
ποΈ Build A Multi-Environment Serverless App
Build a Multi-Environment Serverless App that demonstrates real-world packaging and deployment patterns:
- A Lambda function packaged with dependencies via zip upload in the console
- A container image built and pushed to ECR
- AWS AppConfig set up with feature flags and a deployment strategy
- A SAM project structured for multi-environment deployment
- A
samconfig.tomlconfigured for dev, staging, and prod
Prerequisites
Part I
Package a Lambda Function with Dependencies (Zip Upload)
Create the Deployment Package Locally
Create a zip file on your local machine and upload it through the console.
β οΈ Common Windows mistake: Don't create the zip with Explorer (right-click β Send to β Compressed folder). That wraps everything inside a subfolder, so your handler ends up at
package/lambda_function.pyand Lambda throwsNo module named 'lambda_function'. Rather build the zip in CloudShell.
Step 01: Create a project directory and install dependencies
mkdir lambda-package && cd lambda-package
# Create requirements.txt
cat > requirements.txt << 'EOF'
requests==2.31.0
boto3>=1.28.0
EOF
# Install dependencies into a package directory
pip install -r requirements.txt -t package/
# Create your function code
cat > package/lambda_function.py << 'EOF'
import json
import requests
import boto3
def lambda_handler(event, context):
"""
Lambda function packaged with external dependencies.
The 'requests' library is bundled in the zip package.
boto3 is included in the Lambda runtime, but pinning
a version in requirements.txt ensures consistency.
"""
# Use the bundled 'requests' library
response = requests.get('https://httpbin.org/json')
return {
'statusCode': 200,
'body': json.dumps({
'message': 'Function with bundled dependencies',
'externalApiStatus': response.status_code,
'requestsVersion': requests.__version__
})
}
EOF
# Create the zip package
cd package && zip -r ../deployment.zip . && cd ..
Upload via the Console
Step 02: Open the Lambda console β Create function
-
Function name:
PackagedFunction -
Runtime:
Python 3.12
Click Create function
Step 03: In the Code source section, click Update βΌ β Update from a .zip file
Step 04: Upload, select your deployment.zip file
Step 05: Click Update
Test the Function
Step 06: Go to the Test tab β create a test event with {}
Click Test
β οΈ Verify the response shows the
requestslibrary version and a successful API callπ‘The zip upload limit is 50 MB compressed. If your package exceeds this, upload to S3 first and reference the S3 location. The uncompressed limit is 250 MB.
sam buildhandles all of this automatically. It installs dependencies, creates the zip, and uploads to S3.
Part II
Build And Push A Container Image To ECR
Create an ECR Repository
Step 01: Open the ECR console
Step 02: Click Create repository
-
Repository name:
lambda-container-demo -
Image Tag immutability:
Mutable -
βΌ Image scanning settings - deprecated: Scan on push β
Enabled
Click Create
β οΈ Click into the repository Summary and note the URI (e.g.,
123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo)
Build and Push the Image Locally
Step 03: Create a Dockerfile and function code
# Use the official AWS Lambda Python base image
FROM public.ecr.aws/lambda/python:3.12
# Install dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt
# Copy function code
COPY app.py ${LAMBDA_TASK_ROOT}/
# Set the handler
CMD ["app.lambda_handler"]
Step 04: Create app.py
# app.py
import json
import requests
def lambda_handler(event, context):
"""
Lambda function running as a container image.
Container images support up to 10 GB β useful for
ML libraries, large datasets, or custom runtimes.
"""
return {
'statusCode': 200,
'body': json.dumps({
'message': 'Hello from a container-based Lambda!',
'runtime': 'Container image (Python 3.12)',
'maxPackageSize': '10 GB (vs 250 MB for zip)'
})
}
Step 05: Build and push
# Authenticate Docker with ECR
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
# Build the image
docker build -t lambda-container-demo .
# Tag for ECR
docker tag lambda-container-demo:latest \
123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest
# Push to ECR
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest
Alternatively
# Authenticate Docker with ECR
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
# Build the image β disable attestations so Lambda accepts the manifest
docker buildx build --provenance=false --sbom=false \
-t 123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest \
--push .
β οΈ Manifest Format Gotcha: If you build with a plain
docker buildon a recent Docker Desktop, BuildKit adds provenance and SBOM attestations that produce an OCI image index. Lambda rejects this with: "The image manifest, config or layer media type for the source image ... is not supported." Lambda only accepts Docker Image Manifest V2 Schema 2. The--provenance=false --sbom=falseflags above force the compatible format. Alternatively, setDOCKER_BUILDKIT=0before building to use the legacy builder, which always produces the compatible manifest.
If you prefer the separate tag-and-push steps (with BuildKit disabled):
# PowerShell: $env:DOCKER_BUILDKIT=0 | CMD: set DOCKER_BUILDKIT=0
docker build -t lambda-container-demo .
docker tag lambda-container-demo:latest \
123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest
Verify in the Console
Step 06: Go back to the ECR console β click into lambda-container-demo
You should see your image with the
latesttag
Create a Lambda Function from the Container Image
Step 07: Open the Lambda console β Create function
Step 08: Select Container image
-
Function name:
ContainerLambdaDemo -
Container image URI: Click Browse images β select
lambda-container-demoβ select thelatesttag
Click Create function
Step 09: Test with an empty event {}
π‘ Container-based Lambda functions use ECR for image storage. The image must implement the Lambda Runtime API. AWS provides base images for all supported runtimes (
public.ecr.aws/lambda/python:3.12). You can also use arbitrary base images with the Runtime Interface Client.
Part III
Set Up AWS AppConfig with Feature Flags
Create an AppConfig Application
Step 01: Open the AppConfig console
Step 02: Click Get started
Step 03: Select configuration type
-
Configuration options:
Feature flag -
Configuration profile name:
feauture-flags
Click Next
Step 04: Specify configuration data
-
Flag name:
New Checkout Flow -
Flag key:
new-checkout-flow -
Flag description - Optional:
Enable the redesigned checkout experience -
Variants:
Basic flag -
Enabled value: Toggle
**ON**
Click Next
Step 05: Review and save
Save to application
-
Application name:
orders-service -
Description:
Feature flags and configuration for the orders service
Click Save and continue to deploy
Step 06: Start deployment
Environment: Click Create environment
Step 07: Create environment
-
Name:
production -
Description:
Production environment
Click Create environment
Add A Feature Flag
Step 08: Click Add flag
-
Flag name:
Dark Mode -
Flag key:
dark-mode -
Description:
Enable dark mode UI -
Variants:
Basic flag -
Enabled value: Toggle
**OFF**
Click Save new version
Create a Deployment Strategy
Step 09: Deployment strategies β Create deployment strategy
-
Name:
GradualRollout -
Description:
Deploy over 10 minutes with bake time -
Deployment type:
Linear βΌ -
Step percentage:
20 -
Deployment time:
10 minutes -
Bake time:
5 minutes
Click Create deployment strategy
Deploy the Configuration
Step 10: Go to your orders-service application β production environment
Click Start deployment
Step 11: Start deployment
-
Configuration profile:
feature-flags -
Hosted configuration version:
(latest) -
Deployment strategy:
GradualRollout
Click Start deployment
β οΈ Watch the deployment progress. It rolls out 20% at a time over 10 minutes
π‘AppConfig deployment strategies control how fast configuration changes roll out. If a CloudWatch alarm fires during deployment, AppConfig automatically rolls back. This is safer than changing a Parameter Store value directly, which takes effect immediately for all callers.
Part IV
Structure a SAM Project for Multi-Environment Deployment
Step 01: Initialize the Project
sam init --runtime python3.12 --name multi-env-app --app-template hello-world
cd multi-env-app
Step 02: Create the Handler Files
β οΈ
sam initcreates ahello_world/folder, but our template uses asrc/handlers/structure with two functions. Create these files (delete the generatedhello_world/folder afterward. It's no longer referenced):
Step 03: Create src/handlers/requirements.txt:
boto3
Step 04: Create src/handlers/create_order.py:
import json
import os
import boto3
import uuid
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['TABLE_NAME'])
def lambda_handler(event, context):
body = json.loads(event.get('body', '{}'))
order_id = str(uuid.uuid4())[:8].upper()
table.put_item(Item={
'PK': f'ORDER#{order_id}',
'SK': 'METADATA',
'customerId': body.get('customerId', 'unknown'),
'status': 'created'
})
return {
'statusCode': 201,
'body': json.dumps({'orderId': f'ORD-{order_id}', 'status': 'created'})
}
Step 05: Create src/handlers/get_order.py:
import json
import os
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['TABLE_NAME'])
def lambda_handler(event, context):
order_id = event['pathParameters']['orderId']
response = table.get_item(Key={'PK': f'ORDER#{order_id}', 'SK': 'METADATA'})
item = response.get('Item')
if not item:
return {'statusCode': 404, 'body': json.dumps({'error': 'Order not found'})}
return {'statusCode': 200, 'body': json.dumps(item, default=str)}
β οΈ The template's
CodeUri: src/handlers/andHandler: create_order.lambda_handlermust point to real files. Ifsrc/handlers/doesn't exist or is missingrequirements.txt,sam buildfails with "source ... does not exist" or "requirements.txt file not found." TheCodeUriis the folder SAM packages; theHandlerisfilename.function_namerelative to that folder.
Step 06: Configure the SAM Template with Parameters
Replace the contents of template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Multi-environment serverless application
Parameters:
Stage:
Type: String
Default: dev
AllowedValues: [dev, staging, prod]
TableName:
Type: String
Default: orders
Globals:
Function:
Runtime: python3.12
Timeout: 30
Environment:
Variables:
STAGE: !Ref Stage
TABLE_NAME: !Sub "${Stage}-${TableName}"
Resources:
OrdersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${Stage}-${TableName}"
BillingMode: PAY_PER_REQUEST
KeySchema:
- AttributeName: PK
KeyType: HASH
- AttributeName: SK
KeyType: RANGE
AttributeDefinitions:
- AttributeName: PK
AttributeType: S
- AttributeName: SK
AttributeType: S
CreateOrderFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/handlers/
Handler: create_order.lambda_handler
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref OrdersTable
Events:
CreateOrder:
Type: Api
Properties:
Path: /orders
Method: post
GetOrderFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/handlers/
Handler: get_order.lambda_handler
Policies:
- DynamoDBReadPolicy:
TableName: !Ref OrdersTable
Events:
GetOrder:
Type: Api
Properties:
Path: /orders/{orderId}
Method: get
Outputs:
ApiUrl:
Description: API Gateway endpoint URL
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/${Stage}/"
Step 07: Configure samconfig.toml for Multiple Environments
# samconfig.toml β one section per environment
# The version key is REQUIRED β SAM CLI won't parse the file without it
version = 0.1
[default.deploy.parameters]
stack_name = "multi-env-app-dev"
resolve_s3 = true
capabilities = "CAPABILITY_IAM"
parameter_overrides = "Stage=dev"
confirm_changeset = false
[staging.deploy.parameters]
stack_name = "multi-env-app-staging"
resolve_s3 = true
capabilities = "CAPABILITY_IAM"
parameter_overrides = "Stage=staging"
confirm_changeset = false
[prod.deploy.parameters]
stack_name = "multi-env-app-prod"
resolve_s3 = true
capabilities = "CAPABILITY_IAM"
parameter_overrides = "Stage=prod"
confirm_changeset = true
Step 08: Deploy to Different Environments
# Build once β use --use-container to build inside a Lambda-compatible
# Docker image (needs Docker running, but no local Python required)
sam build --use-container
# Or, if you have Python 3.12 installed and on your PATH:
# sam build
# Deploy to dev (default config)
sam deploy
# Deploy to staging
sam deploy --config-env staging
# Deploy to prod (will prompt for changeset confirmation)
sam deploy --config-env prod
β οΈ
sam buildcan't find Python? If you see "Binary validation failed for python ... did you have python for runtime: python3.12 on your PATH?", it means Python 3.12 isn't installed locally (theWindowsApps\python.EXEentries are Microsoft Store stubs, not a real install). Usesam build --use-containerto build inside a Docker container that already has the correct runtime, or install Python 3.12 from python.org and check "Add python.exe to PATH" during setup. CloudShell also has Python 3.12 and SAM pre-installed.
Each deployment creates isolated resources: dev-orders table, staging-orders table, prod-orders table.
π‘
samconfig.tomluses config environments (sections like[staging.deploy.parameters]) to manage per-environment settings. The--config-envflag selects which section to use. Settingconfirm_changeset = truefor production forces you to review changes before applying them.
ποΈ What You Built | π Exam Concepts Recap
| What You Built | Exam Concept |
|---|---|
Bundled requests into a zip and uploaded via console |
Zip packaging with dependencies, 50/250 MB limits |
| Built a Dockerfile and pushed to ECR | Container image packaging (up to 10 GB) |
| Created a Lambda from a container image | When to use containers vs zip |
| Created an AppConfig application and environment | Runtime configuration management |
| Added feature flags with enabled/disabled toggles | Feature flag pattern for decoupling release from deploy |
| Created a Linear deployment strategy with bake time | Gradual config rollout with automatic rollback |
Used SAM parameters and !Sub for resource names |
Environment-specific infrastructure |
Configured samconfig.toml for dev/staging/prod |
Multi-environment deployment with --config-env |
Set confirm_changeset = true for prod |
Forcing review before production changes |
β οΈ Clean Up Protocol
-
Lambda β Delete
PackagedFunctionandContainerLambdaDemo -
ECR β Delete the
lambda-container-demorepository (and all images) - AppConfig β Delete the deployment, then the configuration profile, environment, and application (in that order)
-
CloudFormation β Delete any SAM-deployed stacks (
multi-env-app-dev, etc.) - IAM β Delete Lambda execution roles
- CloudWatch β Delete log groups
-
S3 β Delete any SAM deployment buckets (prefixed with
aws-sam-cli-managed-default)
Key Takeaways
- Zip packages: 50 MB compressed / 250 MB uncompressed. Container images: up to 10 GB. Use containers for large dependencies.
- Lambda Layers share dependencies across functions. Up to 5 layers, 250 MB total uncompressed.
- sam build resolves dependencies automatically. sam deploy packages and deploys. sam deploy --guided for first-time setup.
- AppConfig deploys configuration independently from code, with validation, gradual rollout, and automatic rollback.
-
samconfig.toml manages multi-environment deployment configs. Use
--config-envto select the environment. -
ECR stores container images. Use
public.ecr.aws/lambda/base images for Lambda containers. - Use CloudFormation parameters and
!Subfor environment-specific resource names. -
SAM policy templates (
DynamoDBCrudPolicy,S3ReadPolicy) enforce least privilege without writing full IAM policies.
Additional Resources
- Deploying Lambda functions as .zip file archives
- Managing Lambda dependencies with layers
- What is the AWS Serverless Application Model (AWS SAM)?
- What is AWS AppConfig?
- Create a Lambda function using a container image
- AWS SAM CLI configuration file
ποΈ
Top comments (1)
This is a solid breakdown of something that often gets underestimated in serverless architectures: artifact preparation across multiple environments is where most deployment complexity actually lives.
The βbuild once, deploy everywhereβ idea sounds simple, but in practice multi-environment serverless setups introduce a lot of hidden variables:
environment-specific configuration drift (dev vs staging vs prod)
IAM and permission boundaries per stage
dependency/version consistency across builds
and subtle differences in event sources and triggers
What I like about this approach is the emphasis on treating artifacts as immutable deployment units, rather than rebuilding logic per environment. That alone reduces a lot of inconsistencies.
In real-world AWS setups, Iβve found the biggest win comes from standardizing:
build pipeline (single source of truth for artifacts)
parameterized deployment templates (instead of branching logic)
and strict separation of state vs configuration
One thing Iβd be curious about is how you handle rollback strategies across environmentsβespecially when multiple services depend on the same artifact version.
Overall, this is exactly the kind of discipline that separates βworks in devβ serverless apps from production-grade systems.