Deploying AWS Lambda and S3 with CDK (Python)
This project walks through deploying a simple AWS Lambda function and S3 bucket using the AWS Cloud Development Kit (CDK) in Python. It’s designed for beginners exploring cloud infrastructure, DevOps, or Infrastructure as Code (IaC). Along the way, you'll learn Identity and Access Management (IAM) best practices, such as avoiding root credentials, and troubleshoot a real-world deployment issue using the command-line interface (CLI).
Skills Demonstrated
- Infrastructure as Code using AWS CDK (Python)
- Packaging and deploying AWS Lambda functions
- Secure IAM practices (avoiding root credentials)
- CloudFormation synthesis and asset management
- Command-line troubleshooting and CDK internals
Prerequisites
- AWS account (create one here)
- AWS CLI v2 installed and configured
- Python 3.8+ (tested with Python 3.12)
- Node.js v14+ (tested with Node.js 22.16.0 and npm 10.9.2)
- AWS CDK v2 installed:
npm install -g aws-cdk
Setting Up AWS Access Without Root Credentials
As a new AWS user, configure your environment securely without using your root account. It's unsafe and should be avoided except for initial account setup.
Option 1: IAM User
- Go to the IAM Console.
- Create a new IAM user with:
- Programmatic access
- AdministratorAccess (for development only)
- Run the AWS CLI configuration:
aws configure --profile cdk-sre-demo
Enter:
- Access key ID
- Secret access key
- Region (e.g., us-west-2)
- Output format (optional: json)
Use this profile for all CDK commands:
cdk bootstrap --profile cdk-sre-demo
Option 2: IAM Identity Center (SSO)
- Run:
aws configure sso
- Authenticate:
aws sso login --profile my-sso-profile
- Use it with CDK:
cdk bootstrap --profile my-sso-profile
See: AWS userguide to SSO CLI Configuration
Quick Setup Recap
- Never use root credentials for CLI/CDK access.
- Use an IAM user or SSO to configure access:
aws configure --profileoraws configure sso- Always pass the correct profile to CDK with
--profile
Project Setup
Initialize the CDK Project
mkdir cdk-sre-demo && cd cdk-sre-demo
cdk init app --language python
Note: The folder must be empty before running cdk init.
After initialization, your folder should look like this:
cdk-sre-demo/
├── app.py
├── cdk_sre_demo/
│ ├── __init__.py
│ └── cdk_sre_demo_stack.py
├── requirements.txt
├── lambda/
│ └── hello_lambda.py
├── .gitignore
├── README.md
└── cdk.json
Create Virtual Environment and Install Dependencies
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Confirm requirements.txt contains:
aws-cdk-lib==2.139.0
constructs>=10.0.0,<11.0.0
Write Your Lambda Function
Create lambda/hello_lambda.py with the following content:
def handler(event, context):
print("Hello from Lambda!")
return {
'statusCode': 200,
'body': 'Lambda function executed successfully.'
}
Define Your Infrastructure Stack
In cdk_sre_demo/cdk_sre_demo_stack.py, define the infrastructure:
from aws_cdk import (
Stack,
aws_s3 as s3,
aws_lambda as _lambda,
)
from constructs import Construct
class CdkSreDemoStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs):
super().__init__(scope, construct_id, **kwargs)
s3.Bucket(self, "SreDemoBucket", versioned=True)
_lambda.Function(
self, "HelloLambda",
runtime=_lambda.Runtime.PYTHON_3_10,
handler="hello_lambda.handler",
code=_lambda.Code.from_asset("lambda")
)
Verify Entry Point (app.py)
Make sure app.py contains:
#!/usr/bin/env python3
import aws_cdk as cdk
from cdk_sre_demo.cdk_sre_demo_stack import CdkSreDemoStack
app = cdk.App()
CdkSreDemoStack(app, "CdkSreDemoStack")
app.synth()
Deploy the Stack
cdk bootstrap --profile cdk-sre-demo
cdk deploy --profile cdk-sre-demo
Troubleshooting Deployment: Lambda Not Zipping Correctly
Error Description
Sometimes (or most of the time) things don’t go as smoothly as expected. In my case, every time I ran cdk deploy, I hit an error that indicated:
Uploaded file must be a non-empty zip (Service: Lambda, Status Code: 400…)
This hinted that CDK wasn’t zipping and staging my Lambda function properly. More accurately, CDK failed to detect the Lambda directory as a valid asset. As a result, it didn't bundle and upload the function code to S3, causing a Lambda deployment error.
I double-checked my functions and folder structure, but everything looked correct. To investigate further, I ran:
cdk synth --profile dev-user
This command synthesizes your CDK app into a CloudFormation template and outputs the resource definitions. Normally, under the Lambda section, you should see a ZIP file reference like this:
Resources:
HelloLambda…:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: cdk-hnb659fds-assets-<AccountId>-<Region>
S3Key: 12118b59e….zip
Which I did, but when I ran:
find cdk.out -type f -name '*.zip'
No .zip file was found! That’s when I realized: CDK wasn’t packaging the function directory at all. It was just copying the hello_lambda.py file directly into cdk.out/assets/.
Workaround: Manually Zip the Lambda Function
To keep the project moving, I used a simple workaround: manually zipped the Lambda file and referenced it explicitly in the CDK stack.
cd lambda
zip -j ../hello_lambda.zip hello_lambda.py
cd ..
This will result in the .zip file in your project root directory.
Then in cdk_sre_demo_stack.py, I updated the Lambda block:
_lambda.Function(
self, "HelloLambda",
runtime=_lambda.Runtime.PYTHON_3_12,
handler="hello_lambda.handler",
code=_lambda.Code.from_asset("hello_lambda.zip")
)
Code.from_asset(path) tells CDK to package a local file or folder and upload it to S3 as a Lambda asset.
Clean and Re-Synthesize
To clean your directory of old deployment files, remove previously synthesized outputs before re-synthesizing:
rm -rf cdk.out
cdk synth --profile cdk-sre-demo
Double-check that the zipped folder is in the cdk.out directory by running:
find cdk.out -type f -name '*.zip'
Unzip:
unzip -l cdk.out/assets/<that-hash>.zip
If it lists hello_lambda.py, you're good to go!
Final Deployment
cdk bootstrap --profile dev-user
cdk deploy --profile dev-user
Verifying Resources
You can confirm your resources are up and running in two ways, AWS UI or via terminal:
Option 1: AWS Console
S3 Bucket
- Go to the S3 Console.
- Look for a bucket named something like
CdkSreDemoStack-SreDemoBucketXXXXX. - Click into it to confirm it exists and versioning is enabled.
Lambda Function
- Go to the Lambda Console.
- Find the function named
CdkSreDemoStack-HelloLambdaXXXXX. - Click it, and then:
- In the Monitor tab → check the Log streams to ensure the function executed successfully without errors.
- In the Test tab, create a test event, run the function, and verify the output.
- You should see: “Lambda function executed successfully.”
Option 2: AWS CLI
Verify the S3 bucket
If you prefer to confirm your resources through the terminal, run the below command:
aws s3 ls --profile cdk-sre-demo
Check if versioning is enabled:
aws s3api get-bucket-versioning \
--bucket your-bucket-name-here \
--profile cdk-sre-demo
You should see something like:
{
"Status": "Enabled"
}
This confirms the bucket was created with versioning, as specified in your CDK stack.
Verify the Lambda function
List Lambda Functions:
aws lambda list-functions --profile dev-user
Invoke the Function:
aws lambda invoke \
--function-name <your-function-name> \
--profile dev-user \
response.json
cat response.json
You should see something like:
{
"statusCode": 200,
"body": "Lambda function executed successfully."
}
Note: You can also see this function invocation in the AWS Lambda console.
Tearing Down
To delete everything you deployed:
cdk destroy --profile dev-user