package/lambda_function.py and Lambda throws No 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 requests library 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 build handles 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 build on 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=false flags above force the compatible format. Alternatively, set DOCKER_BUILDKIT=0 before 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 latest tag
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 the latest tag
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 init creates a hello_world/ folder, but our template uses a src/handlers/ structure with two functions. Create these files (delete the generated hello_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/ and Handler: create_order.lambda_handler must point to real files. If src/handlers/ doesn't exist or is missing requirements.txt, sam build fails with "source ... does not exist" or "requirements.txt file not found." The CodeUri is the folder SAM packages; the Handler is filename.function_name relative 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 build can'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 (the WindowsApps\python.EXE entries are Microsoft Store stubs, not a real install). Use sam build --use-container to 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.toml uses config environments (sections like [staging.deploy.parameters]) to manage per-environment settings. The --config-env flag selects which section to use. Setting confirm_changeset = true for 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
PackagedFunction and ContainerLambdaDemo
-
ECR β Delete the
lambda-container-demo repository (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-env to select the environment.
-
ECR stores container images. Use
public.ecr.aws/lambda/ base images for Lambda containers.
- Use CloudFormation parameters and
!Sub for environment-specific resource names.
-
SAM policy templates (
DynamoDBCrudPolicy, S3ReadPolicy) enforce least privilege without writing full IAM policies.
Additional Resources
ποΈ