Lambda deployment covers packaging code, uploading artifacts, creating/updating functions, managing layers, and configuring event sources. Data engineering teams typically deploy via CI/CD (GitHub Actions, CodePipeline) using S3-based packages or container images for heavier dependencies (Pandas, PyArrow).
Common use cases:
- Deploy S3-triggered validation Lambdas on every Git merge
- Ship shared dependency layers (boto3, pandas, awswrangler) across pipeline functions
- Container-based Lambda for ML inference or large Python dependencies
- Blue/green deployments using versions and aliases
When to use it: As part of automated pipeline releases — not for one-off Console edits in production accounts.
Purpose: Zip handler and dependencies before upload.
Command:
cd lambda/s3-landing-validator
pip install -r requirements.txt -t package/
cp handler.py package/
cd package && zip -r ../deployment.zip . && cd ..Purpose: Deploy a new Lambda from a local zip artifact.
Command:
aws lambda create-function \
--function-name s3-landing-validator \
--runtime python3.12 \
--role arn:aws:iam::123456789012:role/LambdaS3ValidatorRole \
--handler handler.lambda_handler \
--zip-file fileb://deployment.zip \
--timeout 120 \
--memory-size 512 \
--environment 'Variables={LOG_LEVEL=INFO,GLUE_JOB_NAME=orders-daily-etl}' \
--tags Environment=prod,Team=data-platformExample Output (abbreviated):
{
"FunctionName": "s3-landing-validator",
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:s3-landing-validator",
"Runtime": "python3.12",
"State": "Pending",
"LastUpdateStatus": "InProgress"
}Purpose: Push a new zip without changing configuration.
Command:
aws lambda update-function-code \
--function-name s3-landing-validator \
--zip-file fileb://deployment.zipExample Output:
{
"FunctionName": "s3-landing-validator",
"LastUpdateStatus": "InProgress",
"CodeSha256": "abc123...",
"CodeSize": 15234567
}Purpose: Upload zip to S3 first, then reference from Lambda (required for large artifacts).
Command:
aws s3 cp deployment.zip s3://etl-artifacts/lambda/s3-landing-validator/deployment.zip
aws lambda update-function-code \
--function-name s3-landing-validator \
--s3-bucket etl-artifacts \
--s3-key lambda/s3-landing-validator/deployment.zipExample Output:
{
"FunctionName": "s3-landing-validator",
"CodeSize": 52428800,
"LastUpdateStatus": "InProgress"
}Purpose: Block CI/CD until the function is ready.
Command:
aws lambda wait function-updated --function-name s3-landing-validator
aws lambda get-function --function-name s3-landing-validator \
--query 'Configuration.{State:State,LastUpdateStatus:LastUpdateStatus}' \
--output tableExample Output:
---------------------------------
| GetFunction |
+------------------+------------+
| LastUpdateStatus | State |
+------------------+------------+
| Successful | Active |
+------------------+------------+
Purpose: Share common dependencies across multiple functions.
Command:
mkdir -p layer/python
pip install awswrangler pandas -t layer/python/
cd layer && zip -r ../awswrangler-layer.zip python && cd ..
aws lambda publish-layer-version \
--layer-name data-engineering-deps \
--description "awswrangler + pandas for data pipelines" \
--zip-file fileb://awswrangler-layer.zip \
--compatible-runtimes python3.11 python3.12Example Output:
{
"LayerArn": "arn:aws:lambda:us-east-1:123456789012:layer:data-engineering-deps",
"LayerVersionArn": "arn:aws:lambda:us-east-1:123456789012:layer:data-engineering-deps:3",
"Version": 3
}Purpose: Reference a published layer version in a function.
Command:
aws lambda update-function-configuration \
--function-name s3-landing-validator \
--layers arn:aws:lambda:us-east-1:123456789012:layer:data-engineering-deps:3Purpose: Deploy Lambda from ECR (for heavy dependencies).
Command:
aws lambda create-function \
--function-name parquet-compactor \
--package-type Image \
--code ImageUri=123456789012.dkr.ecr.us-east-1.amazonaws.com/parquet-compactor:latest \
--role arn:aws:iam::123456789012:role/LambdaCompactorRole \
--timeout 900 \
--memory-size 2048 \
--architectures arm64Purpose: Wire an SQS queue as a Lambda trigger.
Command:
aws lambda create-event-source-mapping \
--function-name s3-landing-validator \
--event-source-arn arn:aws:sqs:us-east-1:123456789012:landing-file-queue \
--batch-size 10 \
--maximum-batching-window-in-seconds 5 \
--function-response-types ReportBatchItemFailuresExample Output:
{
"UUID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"State": "Creating",
"BatchSize": 10
}VERSION=$(aws lambda publish-version --function-name s3-landing-validator --query Version --output text)
aws lambda update-alias \
--function-name s3-landing-validator \
--name prod \
--function-version "$VERSION"aws lambda update-function-code-signing-config \
--function-name s3-landing-validator \
--code-signing-config-arn arn:aws:lambda:us-east-1:123456789012:code-signing-config:prodaws lambda list-versions-by-function --function-name s3-landing-validator \
--query 'Versions[].{Version:Version,Sha:CodeSha256,Modified:LastModified}' \
--output tablesam build && sam deploy --guided
# Or
aws cloudformation deploy \
--template-file template.yaml \
--stack-name orders-ingest-lambda \
--capabilities CAPABILITY_IAMfrom pathlib import Path
import boto3
zip_bytes = Path("deployment.zip").read_bytes()
client = boto3.client("lambda")
response = client.update_function_code(
FunctionName="s3-landing-validator",
ZipFile=zip_bytes,
)
print(response["LastUpdateStatus"])import logging
import time
from pathlib import Path
import boto3
from botocore.exceptions import ClientError
logger = logging.getLogger(__name__)
def deploy_lambda_from_s3(
function_name: str,
local_zip: str,
artifact_bucket: str,
artifact_key: str,
) -> None:
s3 = boto3.client("s3")
lam = boto3.client("lambda")
zip_path = Path(local_zip)
if not zip_path.exists():
raise FileNotFoundError(local_zip)
logger.info("Uploading %s to s3://%s/%s", local_zip, artifact_bucket, artifact_key)
s3.upload_file(str(zip_path), artifact_bucket, artifact_key)
lam.update_function_code(
FunctionName=function_name,
S3Bucket=artifact_bucket,
S3Key=artifact_key,
)
waiter = lam.get_waiter("function_updated")
waiter.wait(FunctionName=function_name)
logger.info("Function %s updated successfully", function_name)import boto3
def publish_and_attach_layer(function_name: str, layer_zip: str, layer_name: str) -> str:
lam = boto3.client("lambda")
with open(layer_zip, "rb") as f:
layer_resp = lam.publish_layer_version(
LayerName=layer_name,
ZipFile=f.read(),
CompatibleRuntimes=["python3.12"],
)
layer_arn = layer_resp["LayerVersionArn"]
config = lam.get_function_configuration(FunctionName=function_name)
existing_layers = [l["Arn"] for l in config.get("Layers", [])]
existing_layers = [a for a in existing_layers if layer_name not in a]
existing_layers.append(layer_arn)
lam.update_function_configuration(FunctionName=function_name, Layers=existing_layers)
lam.get_waiter("function_updated").wait(FunctionName=function_name)
return layer_arn- Store deployment artifacts in a private S3 bucket with SSE-KMS; restrict
s3:GetObjectto CI/CD roles. - Use IAM deployment roles separate from runtime roles — CI can update code; runtime cannot.
- Enable code signing for production functions to verify package integrity.
- Scan container images with ECR image scanning before Lambda deploy.
- Never commit
.envor credentials into deployment zips. - Pin dependency versions in
requirements.txtto prevent supply-chain drift.
| Error / Symptom | Root Cause | Resolution |
|---|---|---|
RequestEntityTooLargeException |
Zip > 50 MB direct limit | Upload to S3; use --s3-bucket/--s3-key |
ResourceConflictException |
Update in progress | Wait with function-updated waiter |
InvalidParameterValueException |
Handler path wrong | Match --handler to module.function in zip root |
| Layer import fails | Wrong folder structure | Layer must be python/lib/python3.12/site-packages/ |
Unzipped size must be smaller than |
Too many deps | Split into layers or use container image |
| Stale code after deploy | Invoking $LATEST during update |
Publish version; route alias after successful deploy |
- Deploy via CI/CD with immutable S3 artifact paths (
s3://.../lambda/name/<git-sha>.zip). - Use
aws lambda wait function-updatedbefore running integration tests. - Split heavy deps into layers shared across pipeline functions.
- Prefer container images when dependencies exceed layer size limits or need OS packages.
- Implement blue/green with aliases — never point production traffic to
$LATEST. - Keep deployment packages minimal — exclude tests, docs, and
.pycfiles from zip. - Version layer ARNs explicitly in IaC; avoid
:latestlayer references. - Run
pip install --platform manylinux2014_x86_64 --only-binary=:all:when building on Mac for Lambda Linux.