Quick Start: Build a Python function, run it on your laptop with the AWS Serverless Application Model (SAM) CLI, then deploy it and delete it again. The hands-on part takes about 30 minutes. The rest of the guide covers the few ideas that decide whether a Lambda function behaves in production.
A Lambda function looks like the easiest code you will ever ship: one function, no servers, a bill measured in milliseconds. Then a queue delivers the same message twice, a timeout you never set kills a request after 3 seconds, and the first call after lunch takes a full second longer than the rest. Every one of those surprises comes from the same short list of concepts, and this guide covers that list.
What You’ll Learn
- What is AWS Lambda, and what changed in 2026?
- What are the use cases for Lambda, and when is it the wrong tool?
- What are the core concepts you need to be productive: handlers, the execution environment, invocation types, concurrency, packaging, observability, and cost?
- How do you build, test, deploy, and delete a function with AWS SAM?
- Where can you go to learn more about Lambda?
The Basics
AWS Lambda runs your code when something happens, such as an HTTP request, a file landing in Amazon S3, or a message arriving on a queue. You pay per request plus the time that code runs. You hand AWS a function; AWS supplies the servers, the operating system, the scaling, and the patching.
AWS now calls the classic Lambda function this guide teaches an Event Function, although the API operations, SDKs, CLI, and the AWS::Lambda::Function resource type still say “function” (Lambda Event Functions). Every concept below applies to Event Functions.
Two newer options sit beside it. Lambda Web Functions run ordinary web servers, and Lambda MicroVMs provide isolated sandboxes for AI agents and build jobs. The end of this guide covers both, along with durable functions.
This guide teaches the 20% of Lambda that covers 80% of real work. That 20% is the handler, the execution environment and cold starts, memory sizing, the three invocation types, concurrency, packaging, logging, and the cost model. The long tail, including virtual private cloud (VPC) networking, extensions, custom runtimes, and Lambda@Edge, lives in the resources at the end.
Primary Use Cases
- APIs and webhooks behind Amazon API Gateway or a function URL.
- File processing when objects land in S3, such as resizing images or parsing uploads.
- Queue and stream consumers for Amazon Simple Queue Service (SQS), Amazon Kinesis, and DynamoDB Streams.
- Scheduled jobs and glue code between AWS services.
Less Suitable Use Cases
- Work that runs longer than 15 minutes in one go, since that is the timeout limit on default compute (Lambda quotas).
- Steady, high-volume traffic, where an always-on container or instance can cost less per request.
- Long-running stateful workflows written as one function. Use AWS Step Functions or Lambda durable functions instead (durable functions).
When to Use AWS Lambda
Reach for Lambda when work arrives in bursts, each piece finishes in seconds or minutes, and you would rather not run servers. Skip it when traffic is flat and heavy, when a job needs more than 15 minutes, or when the code needs a long-lived process holding state in memory.
The 20% That Does 80%
The handler, the event, and the context
Lambda calls one function in your code, the handler, and passes it two things (Python handler). The event is a JSON document whose shape depends on what invoked the function. The context object carries details such as the request ID and the time left before the timeout.
def handler(event, context):
name = event.get("name", "world")
return {"message": f"Hello, {name}!"}What this gives you: A function Lambda can call with any JSON event. The return value goes back to the caller on a synchronous invocation.
The execution environment and cold starts
Lambda runs your function inside an execution environment, a small isolated machine with your runtime and code loaded. Each environment moves through three phases: Init, Invoke, and Shutdown (execution environment lifecycle).
A cold start is an invocation that has to wait for Init. AWS says cold starts typically occur in under 1% of invocations and take from under 100 milliseconds to over a second, and you pay for Init time (execution environment lifecycle). On-demand Init is limited to 10 seconds; provisioned concurrency and SnapStart allow up to 15 minutes.
The practical rule follows from the diagram. Code at the top of your file runs once per environment and gets reused by every warm invocation, so put SDK clients, database connections, and configuration there (Lambda best practices). George Mao refines the rule in his Lambda development guide: lazy-load anything only some code paths need. A secret used by one route, for example, should not slow every cold start.
A Reddit thread asking what a well-made Python Lambda looks like lands in the same place, and adds a warning: /tmp survives between warm invocations too, so give each request its own directory.
Memory is the CPU dial
You set memory from 128 MB to 10,240 MB, and Lambda allocates CPU in proportion; at 1,769 MB a function gets the equivalent of one virtual CPU (memory configuration). A CPU-bound function that is slow at 128 MB may finish fast enough at 1,024 MB to cost less overall.
Measure instead of guessing. The AWS best practices page recommends the open source AWS Lambda Power Tuning tool. It runs your function at several memory settings and reports the cheapest, the fastest, or a balance of the two. Luca Mezzalira and Matt Diamond reported cutting a function’s cold start by 56% and its execution time by 95% in the Serverless Office Hours episode on Lambda performance tuning.
Three ways Lambda gets invoked
How an event reaches your function decides who retries a failure (retries):
- Synchronous. The caller waits for the response, as API Gateway and a default
Invokecall (RequestResponse) do. Lambda does not retry; the caller decides. - Asynchronous. Services such as S3 and Amazon Simple Notification Service (SNS) hand Lambda the event and move on. Lambda retries function errors twice (retries) and can send the final failure to a destination or a dead-letter queue (asynchronous invocation).
- Event source mapping. Lambda polls SQS, Kinesis, DynamoDB Streams, and Kafka for you and calls your function with batches. Records arrive at least once (event source mappings).
Asynchronous retries and at-least-once delivery mean the same event can run twice. Write the handler so a second run does no harm: check a processed-ID table before charging a card, for example. AWS says to “Write idempotent code” (Lambda best practices).
For SQS batches, turn on ReportBatchItemFailures and return only the failed message IDs, so one bad message does not send the whole batch back to the queue (SQS error handling).
Concurrency: reserved, provisioned, and SnapStart
On Lambda’s default compute, each execution environment handles one request at a time, so 50 simultaneous requests need 50 environments. The default quota is 1,000 concurrent executions per Region, though new accounts start lower and AWS raises the quota automatically as usage grows (Lambda quotas). Three settings shape that:
- Reserved concurrency caps one function and guarantees it a slice of the account limit. Setting it to 0 stops a runaway function (reserved concurrency).
- Provisioned concurrency keeps environments initialized ahead of traffic to remove cold starts. You pay for it while it is configured, whether or not requests arrive (provisioned concurrency).
- SnapStart snapshots an initialized environment and restores it on a cold start. It supports Java 11 and later, Python 3.12 and later, and .NET 8 and later, but not Node.js. Java SnapStart costs nothing extra; Python and .NET add charges for caching and restoring snapshots (SnapStart).
Packaging: .zip, container image, and layers
Ship code as a .zip archive (250 MB unzipped, including layers) or a container image (up to 10 GB) (Lambda quotas). Start with .zip; switch to an image when dependencies outgrow the limit or your team already builds containers. You cannot change an existing function’s package type later (container images).
Layers share dependencies across functions, but the Reddit thread’s advice holds: treat them as a last resort, and package shared code as a normal library first. Keep test and build tools out of the deployment package, and use current SDKs and runtimes (Mao). Mao’s 2024 list of “latest runtimes” has already aged, so check the supported runtimes page before you pick one.
Observability: logs, the REPORT line, and Powertools
Everything your function prints goes to Amazon CloudWatch Logs. Every invocation ends with a REPORT line showing duration, billed duration, memory size, and Max Memory Used, the number you size memory from (Lambda best practices).
Powertools for AWS Lambda adds structured JSON logging, tracing, custom metrics, idempotency, and batch processing for Python, TypeScript, Java, and .NET. Darryl Ruggles calls it “best practices by default” in his Powertools write-up, and warns that logging the full event can leak personal data in production. For tracing, plan on OpenTelemetry: the AWS X-Ray SDKs and daemon entered maintenance mode on February 25, 2026, and now get security fixes only (X-Ray SDK and daemon support timeline).
The limits worth memorizing
/tmp (ephemeral storage).How Lambda bills you
You pay per request and per GB-second of duration, billed by the millisecond. The free tier includes one million requests and 400,000 GB-seconds per month, and beyond it, requests cost $0.20 per million (Lambda pricing). Init time counts as duration. Arm functions on AWS Graviton cost less per GB-second than x86; AWS launched them in 2021 claiming up to 34% better price performance (Graviton2 launch).
Build Something: A Function You Run Locally, Deploy, and Delete
Prerequisites
Required:
- An AWS account and the AWS CLI configured with credentials (
aws configure). - Permission in that account to create CloudFormation stacks, AWS Identity and Access Management (IAM) roles, S3 buckets, and Lambda functions. An administrator user in a personal account has all four.
- The AWS SAM CLI. This guide used version 1.166.2.
- Docker, running, for local invocation.
- About 30 minutes.
Not required: Python on your machine. This function has no dependencies, so sam build only copies the code, and sam local invoke runs it in a container that matches the Lambda runtime. If you add a requirements.txt later, run sam build --use-container unless Python 3.14 is installed locally.
Step 1: Write the function
Create a project folder with a src folder inside it:
mkdir -p learn-lambda/src
cd learn-lambdaSave this as src/app.py:
import json
import logging
import os
logger = logging.getLogger()
logger.setLevel(os.environ.get("LOG_LEVEL", "INFO"))
# Code outside the handler runs once per execution environment, then gets reused.
GREETING = os.environ.get("GREETING", "Hello")
def handler(event, context):
name = event.get("name", "world")
logger.info(json.dumps({"event": "greeted", "name": name}))
return {
"message": f"{GREETING}, {name}!",
"remaining_ms": context.get_remaining_time_in_millis(),
}What you learned: Configuration loads once at the top; the handler does only per-request work and logs one JSON line per call.
Step 2: Describe the function in a SAM template
Save this as template.yaml in the project folder:
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: A first Lambda function
Resources:
GreeterFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: greeter
CodeUri: src/
Handler: app.handler
Runtime: python3.14
Architectures:
- arm64
MemorySize: 256
Timeout: 10
Environment:
Variables:
GREETING: HelloHandler: app.handler means “the handler function in app.py”. The template sets memory and timeout explicitly instead of accepting the 3-second default, and picks Arm.
Step 3: Build and invoke locally
Save a test event as event.json:
{"name": "Ada"}Then build and invoke:
sam build
sam local invoke GreeterFunction --event event.jsonExpected output (the last lines; the request ID and remaining_ms vary):
[INFO] 2026-10-14T02:44:43.202Z ee087417-... {"event": "greeted", "name": "Ada"}
END RequestId: ee087417-...
REPORT RequestId: ee087417-... Duration: 153.34 ms Billed Duration: 154 ms Memory Size: 256 MB ...
{"message": "Hello, Ada!", "remaining_ms": 9848}What you learned: The REPORT line you will read in CloudWatch shows up locally too. sam local invoke runs the function once in a Docker container (sam local invoke).
Step 4 (optional): Test a container image the same way
If you package as a container image, AWS base images include a runtime interface emulator for local testing (Python container images). Save this as Dockerfile in the project folder:
FROM public.ecr.aws/lambda/python:3.14
COPY src/app.py ${LAMBDA_TASK_ROOT}
CMD ["app.handler"]Build it, run it, and send it an event:
docker build -t greeter:test .
docker run -d --name greeter-test -p 9000:8080 greeter:test
curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"name": "Ada"}'
docker rm -f greeter-testExpected output from curl:
{"message": "Hello, Ada!", "remaining_ms": 299960}The emulator ignores your template’s timeout and uses its own default, which is why remaining_ms is larger here.
Step 5: Deploy
sam deploy --guidedName the stack learn-lambda, pick a Region, and accept the defaults for the rest. SAM saves your answers to samconfig.toml, so later deploys need only sam deploy.
Expected output: the last lines report Successfully created/updated stack - learn-lambda and the Region you picked. The stack creates an IAM role and the function, and Step 7 removes both. The first deploy in a Region also creates a separate aws-sam-cli-managed-default stack for SAM’s artifact bucket; Step 7 explains why it stays.
Step 6: Invoke it in the cloud and read the logs
sam remote invoke GreeterFunction --stack-name learn-lambda --event '{"name": "Ada"}'
sam logs -n GreeterFunction --stack-name learn-lambda --tailExpected output from sam remote invoke includes the same message as the local run; remaining_ms differs:
{"message": "Hello, Ada!", "remaining_ms": <varies>}Stop the log tail with Ctrl-C once you have seen the REPORT line, and note Max Memory Used against the 256 MB you set.
Step 7: Delete it
sam delete --stack-name learn-lambdaAnswer y to both prompts, one to delete the stack and one to delete the artifacts SAM uploaded for it. Both default to No, so pressing Enter keeps everything. To skip the prompts, add --no-prompts.
Expected output: the last line reads Deleted successfully. Two things stay:
- The
aws-sam-cli-managed-defaultstack. It holds the S3 bucket SAM uses for deployment artifacts and reuses for later deploys in that Region. - The
/aws/lambda/greeterlog group in CloudWatch Logs. Lambda created it when the function first ran, so it is not part of the stack (Lambda logs in CloudWatch). Delete it withaws logs delete-log-group --log-group-name /aws/lambda/greeterif you want it gone. Delete it before you follow the CDK section, whose stack creates a log group with that name.
What you learned: The whole loop, from a file on your laptop to a running function and back to nothing, takes six SAM commands.
Deploy the Same Function with the AWS CDK
The AWS Cloud Development Kit (CDK) describes infrastructure in a general-purpose language and deploys it through CloudFormation. The stack below deploys the function from Step 1 in TypeScript, with the same name, runtime, handler, architecture, memory, timeout, and GREETING as the SAM template, so any difference you notice comes from the tool, not the function. Every command in this section runs on your own machine.
Verified with: Node.js 26.9.0, AWS CDK CLI 2.1145.0, and aws-cdk-lib 2.273.0. With these versions, cdk init created the project below and cdk synth turned the stack into a CloudFormation template with one Lambda function.
Required:
- Node.js and the AWS CDK CLI, installed with
npm install -g aws-cdk(Getting started with the AWS CDK). - The same AWS credentials and permissions as the SAM steps.
- The SAM stack deleted (Step 7). Both deploys name the function
greeter, and Lambda rejects a new function whose name already exists in the account and Region (CreateFunction). - No
/aws/lambda/greeterlog group (Step 7 shows how to delete it). The stack creates a log group with that name, and the deploy fails if one already exists (CDK feature flaguseCdkManagedLogGroup).
Create the CDK project
From the learn-lambda folder, create a project folder next to src and generate a TypeScript app in it:
Runs on your machine, not in the practice shell
mkdir greeter-cdk
cd greeter-cdk
cdk init app --language typescriptDefine the function
Replace the contents of lib/greeter-cdk-stack.ts with:
Runs on your machine, not in the practice shell
import * as path from 'path';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class GreeterCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new lambda.Function(this, 'GreeterFunction', {
functionName: 'greeter',
code: lambda.Code.fromAsset(path.join(__dirname, '../../src')),
handler: 'app.handler',
runtime: lambda.Runtime.PYTHON_3_14,
architecture: lambda.Architecture.ARM_64,
memorySize: 256,
timeout: cdk.Duration.seconds(10),
environment: {
GREETING: 'Hello',
},
});
}
}Code.fromAsset points at the same src folder that CodeUri names in the SAM template. The stack sets no role, so the CDK generates an execution role for the function (Function construct).
Bootstrap once per account and Region
The CDK deploys into an environment, one account and one Region. Run cdk bootstrap once from the project folder before you deploy into an environment for the first time. It deploys a CloudFormation stack named CDKToolkit with an S3 bucket for assets such as your function code, an Amazon ECR repository, and IAM roles the CDK uses to deploy (AWS CDK bootstrapping). Running it again on an environment that is already bootstrapped upgrades the stack if needed and otherwise does nothing (Bootstrap your environment).
Runs on your machine, not in the practice shell
cdk bootstrapDeploy
Runs on your machine, not in the practice shell
cdk deployBecause the stack creates an IAM role, cdk deploy lists the IAM changes and asks you to confirm before it deploys. Answer y (cdk deploy).
cdk deploy reports its progress as it builds, publishes, and deploys, then prints the new stack’s ARN after Stack ARN:. In a run of this stack in us-east-1, the ARN looked like this, with your account ID in place of 123456789012:
arn:aws:cloudformation:us-east-1:123456789012:stack/GreeterCdkStack/bca1c440-c890-11f1-a12f-0e10bad081d1That run skipped the confirmation with --require-approval never; your run stops to ask first.
Invoke it
Runs on your machine, not in the practice shell
aws lambda invoke --function-name greeter --cli-binary-format raw-in-base64-out --payload '{"name": "Ada"}' response.json
cat response.json--cli-binary-format raw-in-base64-out lets AWS CLI version 2 accept the payload as plain JSON. aws lambda invoke prints the status of the call:
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}cat response.json then prints what the function returned, the same message as the SAM run:
{"message": "Hello, Ada!", "remaining_ms": 9999}Troubleshooting the CDK deploy
If cdk bootstrap or cdk deploy fails because it finds no AWS credentials, apply the fix from the Error: Unable to locate credentials entry in Troubleshooting, then run the command again.
Clean up
Runs on your machine, not in the practice shell
cdk destroycdk destroy asks you to confirm, then deletes the stack and the function and role it created (cdk destroy). The CDKToolkit bootstrap stack stays, with its S3 bucket, ECR repository, and IAM roles, because it is a separate stack that later deploys in the same account and Region reuse (AWS CDK bootstrapping). The CDK documentation advises against deleting and recreating a bootstrap stack; run cdk bootstrap again to upgrade it instead (Bootstrap your environment).
The /aws/lambda/greeter log group in CloudWatch Logs also stays. A project made with cdk init turns on the useCdkManagedLogGroup feature flag in cdk.json, so the stack creates the log group itself instead of leaving that to Lambda (Lambda log groups in the CDK), and marks it with a Retain deletion policy, which keeps a resource when its stack is deleted (DeletionPolicy attribute). Delete it with aws logs delete-log-group --log-group-name /aws/lambda/greeter if you want it gone, and before you run cdk deploy again.
Deploy the Same Function with Terraform
Terraform describes infrastructure in HashiCorp Configuration Language (HCL) and keeps track of what it created in its own state file instead of a CloudFormation stack. The configuration below deploys the function from Step 1 with the same name, runtime, handler, architecture, memory, timeout, and GREETING as the SAM template and the CDK stack. SAM and the CDK generated the execution role for you; here the configuration declares the role and attaches the basic execution policy that lets the function write its logs. Every command in this section runs on your own machine.
Verified with: Terraform 1.16.3, hashicorp/aws 6.69.0, and hashicorp/archive 2.8.1. With these versions, terraform init, terraform validate, and terraform fmt -check all succeeded on the configuration below.
Required:
- The Terraform CLI (Install Terraform).
- The same AWS credentials and permissions as the SAM steps.
- No other deployed
greeterfunction. If you followed the CDK section, runcdk destroyfrom its clean-up step first; otherwise delete the SAM stack (Step 7). All three deploys name the functiongreeter, and Lambda rejects a new function whose name already exists in the account and Region (CreateFunction).
Write the configuration
From the learn-lambda folder, create a project folder next to src:
Runs on your machine, not in the practice shell
mkdir greeter-terraform
cd greeter-terraformSave this as main.tf:
Runs on your machine, not in the practice shell
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.69.0"
}
archive = {
source = "hashicorp/archive"
version = "2.8.1"
}
}
}
provider "aws" {
region = "us-east-1"
}
data "archive_file" "greeter" {
type = "zip"
source_dir = "${path.module}/../src"
output_path = "${path.module}/greeter.zip"
}
resource "aws_iam_role" "greeter" {
name = "learn-lambda-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
},
]
})
}
resource "aws_iam_role_policy_attachment" "greeter_logs" {
role = aws_iam_role.greeter.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_lambda_function" "greeter" {
function_name = "greeter"
filename = data.archive_file.greeter.output_path
code_sha256 = data.archive_file.greeter.output_base64sha256
role = aws_iam_role.greeter.arn
handler = "app.handler"
runtime = "python3.14"
architectures = ["arm64"]
memory_size = 256
timeout = 10
environment {
variables = {
GREETING = "Hello"
}
}
}The archive_file data source zips the same src folder that CodeUri names in the SAM template (archive_file), and code_sha256 makes Terraform update the function when that zip changes (aws_lambda_function). The role trusts the Lambda service, and the AWSLambdaBasicExecutionRole managed policy grants the CloudWatch Logs permissions the function needs (Lambda execution role). Each version pins the exact provider release this guide was verified with. The AWS provider accepts the python3.14 runtime from 6.21.0 (AWS provider changelog); to move to a newer release, change the version and run terraform init -upgrade (terraform init).
The region in the provider block is the only place the configuration names a Region (AWS provider configuration). To deploy somewhere else, change us-east-1 there and nowhere else.
Deploy
Runs on your machine, not in the practice shell
terraform init
terraform applyterraform init installs the providers listed in required_providers (terraform init). terraform apply shows its plan and asks you to approve it before it creates the role, attaches the policy, and creates the function (terraform apply). Type yes to approve. When the apply finishes, the last line reports the result:
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.These lines come from a run that approved the plan with -auto-approve, so it never stopped to ask. Terraform records what it created in a terraform.tfstate file in the project folder, because the default local backend stores state on your disk (Backend block). Keep that file until you clean up.
Invoke it
Runs on your machine, not in the practice shell
aws lambda invoke --function-name greeter --cli-binary-format raw-in-base64-out --payload '{"name": "Ada"}' response.json
cat response.jsonresponse.json holds the same message as the SAM run:
{"message": "Hello, Ada!", "remaining_ms": 9999}If your AWS CLI’s default Region differs from the one in main.tf, add --region with the Region from main.tf.
Troubleshooting the Terraform deploy
If terraform apply fails because it finds no valid AWS credentials, apply the fix from the Error: Unable to locate credentials entry in Troubleshooting, then run the command again. The AWS provider looks for credentials in the same order as the AWS CLI, so the CLI fix applies here too (AWS provider authentication).
Clean up
Runs on your machine, not in the practice shell
terraform destroyterraform destroy shows what it will delete and asks you to approve, then removes everything the configuration created: the function, the policy attachment, and the role (terraform destroy). After you type yes, it ends with:
Destroy complete! Resources: 3 destroyed.No artifact bucket or bootstrap stack stays behind, because Terraform kept its records in the local state file. Two things do stay:
- The
/aws/lambda/greeterlog group in CloudWatch Logs. Lambda created it when the function first ran, unless an earlier deploy left one with that name, so it is not part of the configuration (Lambda logs in CloudWatch). Delete it withaws logs delete-log-group --log-group-name /aws/lambda/greeterif you want it gone. - The
greeter.zipfile and the state file in the project folder. Delete the folder when you are done.
Manage the Function with the AWS CLI
SAM, the CDK, and Terraform each wrap the same few Lambda API calls. The AWS CLI makes those calls one at a time, so you see what each one sends and what AWS answers. This section creates the same greeter function by hand, invokes it, and reads the answers. Every output below is what AWS CLI 2.38.0 printed against a real account, with the account ID replaced by 123456789012.
Before you start on your own account:
- The AWS CLI v2 installed and signed in to an account (install the AWS CLI).
- No deployed
greeterfunction. Run the clean-up step of any deploy section you followed first, because Lambda rejects a second function with the same name in the same Region.
Check which account the CLI uses
Runs on your machine, not in the practice shell
aws sts get-caller-identityThe answer names your account ID, your user or role, and its ARN, so no output appears here. Read it before you create anything: the next commands create a role and a function in that account.
Create the execution role
Every function runs as an IAM role, its execution role (Lambda execution role). On your own machine, make a folder for the function and copy src/app.py from Step 1 into it. Then package the code, save a test event, and write the trust policy that lets Lambda assume the role:
Runs on your machine, not in the practice shell
mkdir -p ~/greeter && cd ~/greeter
zip function.zip app.py
echo '{"name": "Ada"}' > event.json
echo '{"Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}' > trust.jsonThen create the role, attach the managed policy that lets the function write its logs, and wait until IAM reports the role:
Runs on your machine, not in the practice shell
aws iam create-role --role-name learn-lambda-role --assume-role-policy-document file://trust.json
aws iam attach-role-policy --role-name learn-lambda-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam wait role-exists --role-name learn-lambda-roleSave the role’s ARN in ROLE_ARN, so the create-function examples work in your shell as printed:
Runs on your machine, not in the practice shell
ROLE_ARN=$(aws iam get-role --role-name learn-lambda-role --query Role.Arn --output text)A new role can take a few seconds to reach Lambda. If create-function answers that the role cannot be assumed, wait ten seconds and run it again.
Start from an empty Region
aws --version
aws lambda list-functionsaws-cli/2.38.0 Python/3.14.6 Linux/7.0.12-linuxkit docker/x86_64.amzn.2023
{
"Functions": []
}aws --version names the CLI release, then the Python and platform it runs on. An empty Functions list means the Region holds no function yet.
Create and invoke the function
create-function uploads the zip and sets the runtime, handler, architecture, memory, timeout, and environment in one call (create-function):
cd ~/greeter
aws lambda create-function --function-name greeter --runtime python3.14 --role "$ROLE_ARN" --handler app.handler --architectures arm64 --memory-size 256 --timeout 10 --environment 'Variables={GREETING=Hello}' --zip-file fileb://function.zip
aws lambda get-function --function-name greeter --query Configuration.State --output text
aws lambda invoke --function-name greeter --payload fileb://event.json response.json
cat response.json; echo{
"FunctionName": "greeter",
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:greeter",
"Runtime": "python3.14",
"Role": "arn:aws:iam::123456789012:role/learn-lambda-role",
"Handler": "app.handler",
"CodeSize": 619,
"Description": "",
"Timeout": 10,
"MemorySize": 256,
"LastModified": "2026-10-17T01:43:01.782+0000",
"CodeSha256": "cgTW8vA3pzrPZ7VcRsVSuiaXKPmbY1IqoAr5MBWjHG8=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"GREETING": "Hello"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "7c2e1e5b-14b1-4451-908d-59a1fd3595b0",
"State": "Pending",
"StateReason": "The function is being created.",
"StateReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"arm64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:ab559f26fdef82cfdfbb2942d07b46f84b45b52d0d760d3d623001ab0218052e"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/greeter"
}
}
Active
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
{"message": "Hello, Ada!", "remaining_ms": 9999}The create-function answer says "State": "Pending": Lambda is still preparing the function. A moment later get-function reports Active, and invoke works. invoke prints the call’s status, and the function’s own response lands in response.json (invoke). cat response.json; echo prints that file and then a newline, because the response ends without one. Your own zip gives a different CodeSize and CodeSha256 unless it is byte for byte the recorded one.
On your own account, wait for Active before you invoke, instead of guessing how long it takes (function states):
Runs on your machine, not in the practice shell
aws lambda wait function-active-v2 --function-name greeterThe Command Reference below holds every recorded command, including updates, deletes, and the errors you meet when the function does not exist. When you are done on your own account, delete the function, the role, and the log group:
Runs on your machine, not in the practice shell
aws lambda delete-function --function-name greeter
aws iam detach-role-policy --role-name learn-lambda-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name learn-lambda-role
aws logs delete-log-group --log-group-name /aws/lambda/greeterPractice the AWS CLI
The practice shell below runs real zsh 5.9.2 in this page, with a simulated AWS CLI that answers from recordings of a real account. It answers only these recorded aws commands: lambda create-function, invoke, get-function, list-functions, update-function-code, update-function-configuration, delete-function, and aws --version. Every answer is what AWS CLI 2.38.0 printed in us-east-1, so you can practice the Lambda commands without an account, a bill, or a function left behind. Nothing downloads until you press Start, nothing you type leaves your browser, and Reset shell brings back a fresh shell with an empty Region.
The practice shell needs JavaScript; every command in the Command Reference also works with the AWS CLI on your own machine and account.
The shell’s home holds the greeter folder from Create the execution role, with function.zip, function-v2.zip, and event.json, and ROLE_ARN is already set. The practice shell differs from the AWS CLI on your own machine in the ways below. Run anything that cannot run here on your own machine and account:
awsis a simulation that answers only from recordings- Only the listed commands and recorded forms answer. Forms the real CLI accepts but the shell does not model get a note: abbreviated and repeated flags, optional-value flags, a list flag (
--architectures,--file-system-configs) given no value or more than one, values starting with-other than the negative numbers-1,-0.5, and-.5(so-5.,-1e3, and non-ASCII digits get a note, though the real CLI takes them as values),--, aninvokeoutfile that is-, starts with-, is.or.., or contains/, a missing~file, anyAWS_*environment variable, and any unrecorded value - No credentials are read or needed, so
aws configureand the other credential commands explain that none are used here - Values that vary on real AWS are frozen at the recorded values:
LastModified,RevisionId,RuntimeVersionArn, andremaining_ms - Asynchronous transitions complete at once: a new function shows
State: Pendingand an update showsLastUpdateStatus: InProgress, as recorded, and the next command already sees the finished state get-function’s presignedCode.Locationis the placeholderhttps://presigned-url.invalid/removedaws --versionprints the line recorded in the pinned Linux container,aws-cli/2.38.0 Python/3.14.6 Linux/7.0.12-linuxkit docker/x86_64.amzn.2023, which names the recording machine’s Docker VM kernel and the image’s architecture; your machine prints its own OS and install type- The practice home presets
ROLE_ARNand~/.aws/config - No network: the kernel has no IPv4 (
INET), sowget,ping,nc, andnslookupfail - The practice config turns off the CLI’s pager (
cli_pager =); your machine pages long output withlessunless you set the same line - A note exits with status 3, a status outside the CLI’s documented set (0, 1, 2, 130, 252 to 255)
- Forms the real CLI treats as the same request share one answer:
--flag=valueand--flag value, any option or global order, the same file bytes under another name or a~path, another bare outfile name,--versionadded to a listed command, and--output jsonor--no-cli-pagerspelled out - Recorded usage errors replay only on an 80-column terminal with the recorded color settings. With stdout piped or redirected, a terminal of another width, or other color settings (
NO_COLOR,FORCE_COLOR, orPYTHON_COLORSset, or anotherTERM), the shell prints the base note instead; the real CLI prints the usage uncolored, or wrapped to your width
sam, cdk, terraform, python3, pip, docker, curl, and zip are not installed, so zsh answers command not found. Run them on your own machine too.
Simulated AWS
Responses recorded from AWS CLI 2.38.0 in us-east-1 on 2026-10-16. No account, no bill, nothing is created.
Practice the commands on this page in real zsh with a simulated AWS CLI, right here. Nothing downloads until Start (about 6 MB).
reader@practice ~ % aws lambda list-functions
{
"Functions": []
}
reader@practice ~ %The practice shell needs JavaScript. Every command in the reference also works in zsh with a simulated AWS CLI on your own machine.
Run the drill below in the practice shell, in order, from a fresh shell. Type each command exactly as shown and press Enter. Each step shows what the shell prints, which is what real AWS answered. It takes about ten minutes:
Check the version:
aws --version. The shell names the recorded CLI release, then the Python and the Linux container it ran in. Your own machine prints its own platform.aws-cli/2.38.0 Python/3.14.6 Linux/7.0.12-linuxkit docker/x86_64.amzn.2023List the functions in the Region:
aws lambda list-functions. The list is empty: nothing exists yet.{ "Functions": [] }Move into the function’s folder:
cd greeter. It prints nothing, and the prompt changes toreader@practice ~/greeter %. The folder holdsfunction.zip,function-v2.zip, andevent.json.Create the function:
aws lambda create-function --function-name greeter --runtime python3.14 --role "$ROLE_ARN" --handler app.handler --architectures arm64 --memory-size 256 --timeout 10 --environment 'Variables={GREETING=Hello}' --zip-file fileb://function.zip.ROLE_ARNis already set in the practice shell. The answer says"State": "Pending", because Lambda is still preparing the function.{ "FunctionName": "greeter", "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:greeter", "Runtime": "python3.14", "Role": "arn:aws:iam::123456789012:role/learn-lambda-role", "Handler": "app.handler", "CodeSize": 619, "Description": "", "Timeout": 10, "MemorySize": 256, "LastModified": "2026-10-17T01:43:01.782+0000", "CodeSha256": "cgTW8vA3pzrPZ7VcRsVSuiaXKPmbY1IqoAr5MBWjHG8=", "Version": "$LATEST", "Environment": { "Variables": { "GREETING": "Hello" } }, "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "7c2e1e5b-14b1-4451-908d-59a1fd3595b0", "State": "Pending", "StateReason": "The function is being created.", "StateReasonCode": "Creating", "PackageType": "Zip", "Architectures": [ "arm64" ], "EphemeralStorage": { "Size": 512 }, "SnapStart": { "ApplyOn": "None", "OptimizationStatus": "Off" }, "RuntimeVersionConfig": { "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:ab559f26fdef82cfdfbb2942d07b46f84b45b52d0d760d3d623001ab0218052e" }, "LoggingConfig": { "LogFormat": "Text", "LogGroup": "/aws/lambda/greeter" } }Invoke it with the test event:
aws lambda invoke --function-name greeter --payload fileb://event.json response.json.invokeprints the call’s status. The function’s own response lands inresponse.json.{ "StatusCode": 200, "ExecutedVersion": "$LATEST" }Read the function’s response:
cat response.json; echo.echoadds the newline the response lacks.{"message": "Hello, Ada!", "remaining_ms": 9999}Change the greeting:
aws lambda update-function-configuration --function-name greeter --environment 'Variables={GREETING=Hi}'. The answer shows"GREETING": "Hi"and"LastUpdateStatus": "InProgress": the update is still rolling out.{ "FunctionName": "greeter", "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:greeter", "Runtime": "python3.14", "Role": "arn:aws:iam::123456789012:role/learn-lambda-role", "Handler": "app.handler", "CodeSize": 619, "Description": "", "Timeout": 10, "MemorySize": 256, "LastModified": "2026-10-17T01:43:35.000+0000", "CodeSha256": "cgTW8vA3pzrPZ7VcRsVSuiaXKPmbY1IqoAr5MBWjHG8=", "Version": "$LATEST", "Environment": { "Variables": { "GREETING": "Hi" } }, "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "b9c5af0f-baba-41b3-8fa7-bb902df27f63", "State": "Active", "LastUpdateStatus": "InProgress", "LastUpdateStatusReason": "The function is being created.", "LastUpdateStatusReasonCode": "Creating", "PackageType": "Zip", "Architectures": [ "arm64" ], "EphemeralStorage": { "Size": 512 }, "SnapStart": { "ApplyOn": "None", "OptimizationStatus": "Off" }, "RuntimeVersionConfig": { "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:ab559f26fdef82cfdfbb2942d07b46f84b45b52d0d760d3d623001ab0218052e" }, "LoggingConfig": { "LogFormat": "Text", "LogGroup": "/aws/lambda/greeter" } }Upload the second version of the code:
aws lambda update-function-code --function-name greeter --zip-file fileb://function-v2.zip.CodeSizeandCodeSha256change, and the greeting staysHi.{ "FunctionName": "greeter", "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:greeter", "Runtime": "python3.14", "Role": "arn:aws:iam::123456789012:role/learn-lambda-role", "Handler": "app.handler", "CodeSize": 641, "Description": "", "Timeout": 10, "MemorySize": 256, "LastModified": "2026-10-17T01:43:47.000+0000", "CodeSha256": "OhqqtQSrneSjRDt50w/0TXzs8X0gHT5DMfjK6n62fes=", "Version": "$LATEST", "Environment": { "Variables": { "GREETING": "Hi" } }, "TracingConfig": { "Mode": "PassThrough" }, "RevisionId": "7e7a5d68-fe32-415f-a32d-be5fae925fc4", "State": "Active", "LastUpdateStatus": "InProgress", "LastUpdateStatusReason": "The function is being created.", "LastUpdateStatusReasonCode": "Creating", "PackageType": "Zip", "Architectures": [ "arm64" ], "EphemeralStorage": { "Size": 512 }, "SnapStart": { "ApplyOn": "None", "OptimizationStatus": "Off" }, "RuntimeVersionConfig": { "RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:ab559f26fdef82cfdfbb2942d07b46f84b45b52d0d760d3d623001ab0218052e" }, "LoggingConfig": { "LogFormat": "Text", "LogGroup": "/aws/lambda/greeter" } }Invoke it again:
aws lambda invoke --function-name greeter --payload fileb://event.json response.json. The status is the same as in step 5.{ "StatusCode": 200, "ExecutedVersion": "$LATEST" }Read the new response:
cat response.json; echo. The greeting is nowHi, and the second version of the code adds"version": 2.{"message": "Hi, Ada!", "remaining_ms": 10000, "version": 2}Delete the function:
aws lambda delete-function --function-name greeter. Status code 204 means Lambda deleted the function and has nothing more to say.{ "StatusCode": 204 }List the functions again:
aws lambda list-functions. The Region is empty again, as it was in step 2.{ "Functions": [] }
Command Reference
Recorded with AWS CLI 2.38.0 in us-east-1 on 2026-10-16. The account ID in every answer is 123456789012. LastModified, RevisionId, RuntimeVersionArn, and remaining_ms change on every real run; the outputs show the values from the recording.
Each example starts from an empty Region and runs in ~/greeter, the folder that holds function.zip, function-v2.zip, and event.json. On your own machine, set ROLE_ARN first (see Create the execution role). Search the reference to find a command fast:
No example in the Command Reference matches.
Version and list
aws --version
aws lambda list-functions
aws lambda list-functions --query 'Functions[].FunctionName' --output textaws-cli/2.38.0 Python/3.14.6 Linux/7.0.12-linuxkit docker/x86_64.amzn.2023
{
"Functions": []
}--query filters the answer on your machine with a JMESPath expression, and --output text prints bare values (filter output). An empty list prints nothing.
Errors before the function exists
cd ~/greeter
aws lambda get-function --function-name greeter
aws lambda invoke --function-name greeter --payload fileb://event.json response.json
aws lambda update-function-configuration --function-name greeter --environment 'Variables={GREETING=Hi}'
aws lambda update-function-code --function-name greeter --zip-file fileb://function-v2.zip
aws lambda delete-function --function-name greeter
aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the GetFunction operation: Function not found: arn:aws:lambda:us-east-1:123456789012:function:greeter
Additional error details:
Type: User
aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the Invoke operation: Function not found: arn:aws:lambda:us-east-1:123456789012:function:greeter:$LATEST
Additional error details:
Type: User
aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the UpdateFunctionConfiguration operation: Function not found: arn:aws:lambda:us-east-1:123456789012:function:greeter
Additional error details:
Type: User
aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the UpdateFunctionCode operation: Function not found: arn:aws:lambda:us-east-1:123456789012:function:greeter
Additional error details:
Type: User
aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the DeleteFunction operation: Function not found: arn:aws:lambda:us-east-1:123456789012:function:greeter
Additional error details:
Type: UserEach command exits with status 254 and names the missing function by its ARN. invoke adds :$LATEST, the version it tried to call.
Create and inspect
cd ~/greeter
aws lambda create-function --function-name greeter --runtime python3.14 --role "$ROLE_ARN" --handler app.handler --architectures arm64 --memory-size 256 --timeout 10 --environment 'Variables={GREETING=Hello}' --zip-file fileb://function.zip
aws lambda create-function --function-name greeter --runtime python3.14 --role "$ROLE_ARN" --handler app.handler --architectures arm64 --memory-size 256 --timeout 10 --environment 'Variables={GREETING=Hello}' --zip-file fileb://function.zip
aws lambda get-function --function-name greeter --query Configuration.State --output text
aws lambda get-function --function-name greeter --query Configuration.Runtime --output text
aws lambda get-function --function-name greeter --query 'Configuration.[MemorySize,Timeout]' --output text
aws lambda get-function --function-name greeter --query Configuration.Environment.Variables.GREETING --output text
aws lambda list-functions --query 'Functions[].FunctionName' --output text
aws lambda list-functions --query 'Functions[].[FunctionName,Runtime]' --output text{
"FunctionName": "greeter",
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:greeter",
"Runtime": "python3.14",
"Role": "arn:aws:iam::123456789012:role/learn-lambda-role",
"Handler": "app.handler",
"CodeSize": 619,
"Description": "",
"Timeout": 10,
"MemorySize": 256,
"LastModified": "2026-10-17T01:43:01.782+0000",
"CodeSha256": "cgTW8vA3pzrPZ7VcRsVSuiaXKPmbY1IqoAr5MBWjHG8=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"GREETING": "Hello"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "7c2e1e5b-14b1-4451-908d-59a1fd3595b0",
"State": "Pending",
"StateReason": "The function is being created.",
"StateReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"arm64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:ab559f26fdef82cfdfbb2942d07b46f84b45b52d0d760d3d623001ab0218052e"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/greeter"
}
}
aws: [ERROR]: An error occurred (ResourceConflictException) when calling the CreateFunction operation: Function already exist: greeter
Additional error details:
Type: User
Active
python3.14
256 10
Hello
greeter
greeter python3.14A second create-function with the same name fails with ResourceConflictException. get-function with --query reads one setting; the text output separates several values with tabs.
Invoke with a file or an inline payload
cd ~/greeter
aws lambda create-function --function-name greeter --runtime python3.14 --role "$ROLE_ARN" --handler app.handler --architectures arm64 --memory-size 256 --timeout 10 --environment 'Variables={GREETING=Hello}' --zip-file fileb://function.zip
aws lambda invoke --function-name greeter --payload fileb://event.json response.json
cat response.json; echo
aws lambda invoke --function-name greeter --payload '{"name": "Ada"}' response.json
aws lambda invoke --function-name greeter --cli-binary-format raw-in-base64-out --payload '{"name": "Ada"}' response.json
cat response.json; echo{
"FunctionName": "greeter",
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:greeter",
"Runtime": "python3.14",
"Role": "arn:aws:iam::123456789012:role/learn-lambda-role",
"Handler": "app.handler",
"CodeSize": 619,
"Description": "",
"Timeout": 10,
"MemorySize": 256,
"LastModified": "2026-10-17T01:43:01.782+0000",
"CodeSha256": "cgTW8vA3pzrPZ7VcRsVSuiaXKPmbY1IqoAr5MBWjHG8=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"GREETING": "Hello"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "7c2e1e5b-14b1-4451-908d-59a1fd3595b0",
"State": "Pending",
"StateReason": "The function is being created.",
"StateReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"arm64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:ab559f26fdef82cfdfbb2942d07b46f84b45b52d0d760d3d623001ab0218052e"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/greeter"
}
}
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
{"message": "Hello, Ada!", "remaining_ms": 9999}
aws: [ERROR]: Invalid base64: "{"name": "Ada"}"
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
{"message": "Hello, Ada!", "remaining_ms": 9999}AWS CLI v2 reads --payload as base64 by default, so the inline JSON fails with Invalid base64 and exit status 255 before any request leaves the machine. --cli-binary-format raw-in-base64-out tells the CLI to send the text as it is (binary parameters in AWS CLI v2). fileb://event.json sends the file’s bytes unchanged, so it needs no extra flag.
Update the settings and the code, then delete
cd ~/greeter
aws lambda create-function --function-name greeter --runtime python3.14 --role "$ROLE_ARN" --handler app.handler --architectures arm64 --memory-size 256 --timeout 10 --environment 'Variables={GREETING=Hello}' --zip-file fileb://function.zip
aws lambda update-function-configuration --function-name greeter --environment 'Variables={GREETING=Hi}'
aws lambda get-function --function-name greeter --query Configuration.Environment.Variables.GREETING --output text
aws lambda invoke --function-name greeter --payload fileb://event.json response.json
cat response.json; echo
aws lambda update-function-code --function-name greeter --zip-file fileb://function-v2.zip
aws lambda get-function --function-name greeter --query Configuration.CodeSha256 --output text
aws lambda invoke --function-name greeter --payload fileb://event.json response.json
cat response.json; echo
aws lambda delete-function --function-name greeter
aws lambda delete-function --function-name greeter
aws lambda invoke --function-name greeter --payload fileb://event.json response.json{
"FunctionName": "greeter",
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:greeter",
"Runtime": "python3.14",
"Role": "arn:aws:iam::123456789012:role/learn-lambda-role",
"Handler": "app.handler",
"CodeSize": 619,
"Description": "",
"Timeout": 10,
"MemorySize": 256,
"LastModified": "2026-10-17T01:43:01.782+0000",
"CodeSha256": "cgTW8vA3pzrPZ7VcRsVSuiaXKPmbY1IqoAr5MBWjHG8=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"GREETING": "Hello"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "7c2e1e5b-14b1-4451-908d-59a1fd3595b0",
"State": "Pending",
"StateReason": "The function is being created.",
"StateReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"arm64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:ab559f26fdef82cfdfbb2942d07b46f84b45b52d0d760d3d623001ab0218052e"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/greeter"
}
}
{
"FunctionName": "greeter",
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:greeter",
"Runtime": "python3.14",
"Role": "arn:aws:iam::123456789012:role/learn-lambda-role",
"Handler": "app.handler",
"CodeSize": 619,
"Description": "",
"Timeout": 10,
"MemorySize": 256,
"LastModified": "2026-10-17T01:43:35.000+0000",
"CodeSha256": "cgTW8vA3pzrPZ7VcRsVSuiaXKPmbY1IqoAr5MBWjHG8=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"GREETING": "Hi"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "b9c5af0f-baba-41b3-8fa7-bb902df27f63",
"State": "Active",
"LastUpdateStatus": "InProgress",
"LastUpdateStatusReason": "The function is being created.",
"LastUpdateStatusReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"arm64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:ab559f26fdef82cfdfbb2942d07b46f84b45b52d0d760d3d623001ab0218052e"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/greeter"
}
}
Hi
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
{"message": "Hi, Ada!", "remaining_ms": 9999}
{
"FunctionName": "greeter",
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:greeter",
"Runtime": "python3.14",
"Role": "arn:aws:iam::123456789012:role/learn-lambda-role",
"Handler": "app.handler",
"CodeSize": 641,
"Description": "",
"Timeout": 10,
"MemorySize": 256,
"LastModified": "2026-10-17T01:43:47.000+0000",
"CodeSha256": "OhqqtQSrneSjRDt50w/0TXzs8X0gHT5DMfjK6n62fes=",
"Version": "$LATEST",
"Environment": {
"Variables": {
"GREETING": "Hi"
}
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "7e7a5d68-fe32-415f-a32d-be5fae925fc4",
"State": "Active",
"LastUpdateStatus": "InProgress",
"LastUpdateStatusReason": "The function is being created.",
"LastUpdateStatusReasonCode": "Creating",
"PackageType": "Zip",
"Architectures": [
"arm64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:us-east-1::runtime:ab559f26fdef82cfdfbb2942d07b46f84b45b52d0d760d3d623001ab0218052e"
},
"LoggingConfig": {
"LogFormat": "Text",
"LogGroup": "/aws/lambda/greeter"
}
}
OhqqtQSrneSjRDt50w/0TXzs8X0gHT5DMfjK6n62fes=
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
{"message": "Hi, Ada!", "remaining_ms": 10000, "version": 2}
{
"StatusCode": 204
}
aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the DeleteFunction operation: Function not found: arn:aws:lambda:us-east-1:123456789012:function:greeter
Additional error details:
Type: User
aws: [ERROR]: An error occurred (ResourceNotFoundException) when calling the Invoke operation: Function not found: arn:aws:lambda:us-east-1:123456789012:function:greeter:$LATEST
Additional error details:
Type: Userupdate-function-configuration replaces the whole environment, so the new GREETING takes effect on the next invoke. update-function-code uploads function-v2.zip, the same code with "version": 2 added to the response, and the new CodeSha256 identifies it. Both answers show "LastUpdateStatus": "InProgress"; on your own account, run aws lambda wait function-updated-v2 --function-name greeter before the next call. After delete-function, every call on greeter fails with ResourceNotFoundException again.
Troubleshooting
Error: Running AWS SAM projects locally requires a container runtime. Do you have Docker or Finch installed and running?
sam local invoke could not reach Docker. Start Docker Desktop or your container runtime, wait until it reports running, and run the command again.
Error: Unable to locate credentials
sam deploy found no AWS credentials. Run aws configure, or aws sso login for an SSO profile, confirm with aws sts get-caller-identity, then deploy again.
Handler 'lambda_handler' missing on module 'app' (Runtime.HandlerNotFound)
The Handler value in the template does not match the function name in your code. app.lambda_handler looks for lambda_handler in app.py; rename one side so they match, then run sam build again.
Function 'GreeterFunction' timed out after 10 seconds (locally) or Status: timeout on the REPORT line (CloudWatch)
The handler ran longer than Timeout. Raise the timeout if the work is legitimate, or find the slow call. A function that waits on a network call with no timeout of its own will hang until Lambda kills it.
Your code change does not show up in sam local invoke
sam local invoke runs the copy in .aws-sam/build. Run sam build after every change.
Structuring a Real Function
The sources behind this guide agree on a short list of habits for functions that live longer than a tutorial:
- Split the handler from the logic. Keep the handler thin, parse the event, then call plain functions you can unit test without Lambda, as the Reddit thread recommends.
- Prefer configuration over code. Use event filtering on event source mappings so your function never sees events it would discard, and use native service integrations instead of glue functions (Mao).
- Validate input. Events come from other systems; check the shape before you trust it.
- Test first. One commenter in the thread put it plainly: the most important thing is your tests.
Where Lambda Is Going
Three newer options address the jobs Event Functions handle poorly:
- Lambda Web Functions launched on October 12, 2026. Your code is an ordinary web server, such as Express or Hono, listening on a port. One execution environment serves 64 concurrent requests by default and up to 128 when configured, and the function scales to zero (Web Functions launch post, Web Functions docs). At launch, it supports Node.js 24 on Arm in five Regions.
- Lambda durable functions run multi-step workflows for up to a year with checkpoints and replay (durable functions). They answer the complaint in Qian Li’s post on Lambda’s stateless nature, written to introduce her company’s DBOS product, that stateful tasks such as asynchronous external API calls are hard to run on Lambda.
- Lambda MicroVMs give AI agents, development environments, and CI jobs a full operating system with VM-level isolation (MicroVMs).
Learn AWS Lambda: Beyond the Basics
You’ve covered the essentials. Start with the best practices talk, build two or three functions of your own, then pick a book to go deep.
📹 Video
- Best practices for serverless developers (CNS403), AWS re:Invent 2025.
- From Trigger to Execution: The Journey of Events in AWS Lambda (CNS423), AWS re:Invent 2025, for how Lambda works internally.
- Lambda performance tuning, Serverless Office Hours on the Serverless Land channel (2024).
- AWS Lambda Introduction, Be A Better Dev (2022), a 13-minute primer for complete beginners.
🔊 Audio
- AWS Bites episode 120, “Lambda Best Practices” (2024), covering cold starts, the three invocation types, logging, and security.
📚 Books
- Serverless Development on AWS by Sheen Brisals and Luke Hedger (O’Reilly, 2024), for teams building production serverless systems.
- Serverless Architectures on AWS, Second Edition by Peter Sbarski, Yan Cui, and Ajay Nair (Manning, 2022), for architecture patterns around Lambda.
🌐 Online
- AWS Lambda Developer Guide, the source for every limit in this guide.
- Best practices for working with Lambda functions.
- Serverless Land, with patterns, workshops, and the Office Hours archive.
- Powertools for AWS Lambda.
- AWS Lambda Power Tuning.
- AWS SAM Developer Guide.
Related Content
- Learn Amazon S3 covers the storage service that triggers many Lambda functions.
- Learn Amazon IAM explains the permissions every function’s execution role needs.
- Cloud Providers Guide puts AWS next to Azure and Google Cloud.
- Fundamentals of Monitoring and Observability goes deeper on logs, metrics, and traces.
- Learn Python if the handler code above is new to you.
- Fundamentals of AWS explains Regions, IAM, shared responsibility, and the core AWS services around Lambda.
- Learn Zsh covers zsh, the shell the AWS CLI practice shell runs, including completion and history.
References
- Lambda Event Functions, execution environment lifecycle, Python handler, and Lambda quotas, for the execution model and limits.
- Memory configuration, timeout, and ephemeral storage, for configuration ranges and defaults.
- Retries, asynchronous invocation, event source mappings, and SQS error handling, for invocation types and failure handling.
- Reserved concurrency, provisioned concurrency, and SnapStart, for concurrency controls.
- Container images, Python container images, and supported runtimes, for packaging.
- Lambda pricing and the Graviton2 launch post, for the cost model.
- sam local invoke and the AWS SAM Developer Guide, for the hands-on commands.
- AWS CDK bootstrapping, Bootstrap your environment, cdk deploy, cdk destroy, and the Function construct, for the CDK deployment.
- X-Ray SDK and daemon support timeline, for the move to OpenTelemetry.
- Web Functions launch post, Web Functions docs, durable functions, and MicroVMs, for the newer Lambda options.
- George Mao, Development Best Practices (The Ultimate Guide to AWS Lambda Development, Chapter 1) (2024).
- r/aws, So what does a well-made AWS Lambda in Python actually look like? (2023).
- Luca Mezzalira, post on Lambda performance tuning (2024).
- Darryl Ruggles, Powertools for AWS Lambda: Best Practices By Default (2026).
- Qian Li, post on Lambda’s stateless nature (2024).

Comments #