Quick Start: Create, invoke, and delete a Python function with the AWS CLI, or run the same commands in the practice shell without an account. Hands-on work takes about 20 minutes; the rest covers what determines Lambda’s production behavior.

Lambda functions seem easy to ship: one function, no servers, millisecond billing. Then a queue delivers a message twice, an unset timeout kills a request after three seconds, and the next call takes a second longer. These surprises stem from a few core concepts this guide covers.

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 core concepts do you need to be productive: handlers, execution environment, invocation types, concurrency, packaging, observability, and cost?
  • How do you create, invoke, update, and delete a function with the AWS CLI?
  • Where can you go to learn more about Lambda?

The Basics

AWS Lambda runs your code in response to events, such as HTTP requests, files arriving in Amazon S3, or queue messages. You pay per request and execution time. AWS manages the servers, operating system, scaling, and patching.

AWS calls the classic Lambda function covered here an Event Function, though API operations, SDKs, the CLI, and AWS::Lambda::Function still use “function” (Lambda Event Functions). All concepts below apply to Event Functions.

Two newer options sit alongside it. Lambda Web Functions run standard 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 covers the 20% of Lambda used in 80% of real work: handlers, execution environments and cold starts, memory sizing, invocation types, concurrency, packaging, logging, and costs. Resources cover the rest, including VPC networking, extensions, custom runtimes, and Lambda@Edge.

Primary Use Cases

  • APIs and webhooks behind Amazon API Gateway or a function URL.
  • Process S3 uploads, such as resizing images or parsing files.
  • 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, 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

Use Lambda for bursty workloads that finish in minutes or seconds and don’t need servers. Avoid it for steady, heavy traffic, jobs over 15 minutes, or code requiring a long-lived, stateful process.

The 20% That Does 80%

The handler, the event, and the context

Lambda calls your handler with two inputs (Python handler): an event JSON document shaped by the invoker and a context object with details like the request ID and time remaining before 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).

graph TB A[Init: start the runtime and run code outside the handler] --> B[Invoke: run the handler for one event] B --> C{Another event arrives soon?} C -->|Yes: warm start| B C -->|No| D[Shutdown]

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). The same rule has a flip side: everything at the top runs on every cold start, and you pay for that Init time. Load anything only some code paths need, such as a secret one route reads or a heavy library one event type uses, the first time that path runs, and keep it in a module-level variable so warm invocations reuse it.

The /tmp directory survives between warm invocations too. Its content stays while the environment is frozen, and Lambda does not clear it before the next invocation (execution environment lifecycle). That makes /tmp a useful cache, but files one request writes are still there for the next, so give each request its own directory, such as one named after context.aws_request_id, and delete it when the request ends.

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, don’t guess. AWS recommends the open-source AWS Lambda Power Tuning, which tests several memory settings and identifies the cheapest, fastest, or best-balanced option.

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 Invoke call (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 can run an event twice. Make handlers harmless on reruns: for example, check a processed-ID table before charging a card. AWS advises: “Write idempotent code” (Lambda best practices).

For SQS batches, enable ReportBatchItemFailures and return only failed message IDs to prevent one bad message from resending the entire batch (SQS error handling).

Concurrency: reserved, provisioned, and SnapStart

On Lambda’s default compute, each environment handles one request at a time, so 50 simultaneous requests require 50 environments. The default regional quota is 1,000 concurrent executions, though new accounts start lower and AWS raises it automatically as usage grows (Lambda quotas). Three settings shape this:

  • 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 for cold starts. It supports Java 11+, Python 3.12+, and .NET 8+, but not Node.js. Java is free; Python and .NET incur snapshot caching and restoration charges (SnapStart).

Packaging: .zip, container image, and layers

Ship code as a .zip archive (250 MB unzipped, including layers) or container image (up to 10 GB) (Lambda quotas). Use .zip unless dependencies exceed its limit or your team already builds containers. You can’t change an existing function’s package type (container images).

Package every dependency your function uses with the function, and keep the package to what runs: leave test frameworks, build tools, and unused libraries out (Python code best practices). A layer lets several functions share one dependency, but each function pins an exact layer version, the layer counts toward the same 250 MB limit, and container images can’t use layers at all (layers). Choose a runtime from the supported runtimes page and move off one before it reaches deprecation.

Observability: logs, the REPORT line, and Powertools

Function output goes to CloudWatch Logs. Each invocation’s REPORT line lists duration, billed duration, memory size, and Max Memory Used, the key metric for memory sizing (Lambda best practices).

Powertools for AWS Lambda provides structured JSON logging, tracing, custom metrics, idempotency, and batch processing for Python, TypeScript, Java, and .NET. Its logger leaves the incoming event out of the logs by default, because events often carry personal or secret data; turn event logging on only where you know what the event holds (Powertools logger). For tracing, plan to use OpenTelemetry: AWS X-Ray SDKs and daemon entered maintenance mode on February 25, 2026, receiving only security fixes (X-Ray SDK and daemon support timeline).

The limits worth memorizing

Time 15 minutes maximum per invocation. The default timeout is 3 seconds, so set it on purpose (timeout).
Memory and disk 128 MB to 10,240 MB of memory, and 512 MB to 10,240 MB of /tmp (ephemeral storage).
Payload 6 MB for a synchronous request or response, 1 MB for an asynchronous event (Lambda quotas).
Concurrency 1,000 concurrent executions per Region by default (new accounts start lower), raised by request (Lambda quotas).

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: Create, Invoke, and Delete a Function with the AWS CLI

Use Infrastructure as Code for functions you keep. Creating a function by hand with the CLI is a way to learn, not a way to run production. Define the function, its execution role, and its settings in an Infrastructure as Code (IaC) tool such as AWS CloudFormation, AWS SAM, the AWS CDK, Terraform, or Pulumi, and deploy from that definition (Lambda with IaC). The definition lives in version control, gets reviewed like any other code, and builds the same function in every account and Region. A function built by hand drifts the first time someone changes a setting, and nothing records what changed.

Every one of those tools wraps 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 a greeter function by hand, invokes it, and explains what each command answers. To see the answers without an account, run the same commands in the practice shell below, which replays what AWS CLI 2.38.0 printed against a real account.

Before you start on your own account:

  • The AWS CLI v2 installed and signed in to an account (install the AWS CLI).
  • Permission to create IAM roles and Lambda functions. An administrator user in a personal account has both.
  • No function named greeter in the Region, because Lambda rejects a second function with the same name.

Check which account the CLI uses

Runs on your machine, not in the practice shell

aws sts get-caller-identity

The answer names your account ID, your user or role, and its ARN. Read it before you create anything: the next commands create a role and a function in that account.

Write the function

Make a project folder with a src folder inside it:

Runs on your machine, not in the practice shell

mkdir -p ~/greeter/src && cd ~/greeter

Save 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(),
    }

Configuration loads once at the top; the handler does only per-request work and logs one JSON line per call.

Save a test event as event.json:

{"name": "Ada"}

Create the execution role

Every function runs as an IAM role, its execution role (Lambda execution role). Package the code with app.py at the top of the zip, where --handler app.handler looks for it, and write the trust policy that lets Lambda assume the role:

Runs on your machine, not in the practice shell

zip -j function.zip src/app.py
echo '{"Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}' > trust.json

Then 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-role

Save 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.

Create and invoke the 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
  • create-function uploads the zip and sets the runtime, handler, architecture, memory, timeout, and environment in one call (create-function). AWS answers with the function’s full configuration, which reports the function as Pending while Lambda prepares it.
  • get-function with --query reads one setting, here the function’s state. It prints Active once the function is ready.
  • invoke calls the function with the test event and prints only the call’s status code. The function’s own response goes into response.json (invoke).
  • cat response.json; echo prints that response, a JSON greeting for Ada, then adds the newline the response lacks.

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 greeter

The 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/greeter

Practice 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 starts in the greeter folder from Create the execution role, which holds function.zip, function-v2.zip, and event.json, so its prompt reads reader@practice ~/greeter %, 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:

  • aws is 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), --, an invoke outfile that is -, starts with -, is . or .., or contains /, a missing ~ file, any AWS_* environment variable, and any unrecorded value
  • No credentials are read or needed, so aws configure and 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, and remaining_ms
  • Asynchronous transitions complete at once: a new function shows State: Pending and an update shows LastUpdateStatus: InProgress, as recorded, and the next command already sees the finished state
  • get-function’s presigned Code.Location is the placeholder https://presigned-url.invalid/removed
  • aws --version prints 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_ARN and ~/.aws/config
  • No network: the kernel has no IPv4 (INET), so wget, ping, nc, and nslookup fail
  • The practice config turns off the CLI’s pager (cli_pager =); your machine pages long output with less unless 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=value and --flag value, any option or global order, the same file bytes under another name or a ~ path, another bare outfile name, --version added to a listed command, and --output json or --no-cli-pager spelled 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, or PYTHON_COLORS set, or another TERM), 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 ~/greeter % aws lambda list-functions
{
    "Functions": []
}
reader@practice ~/greeter %

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:

  1. 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.2023
  2. List the functions in the Region: aws lambda list-functions. The list is empty: nothing exists yet.

    {
        "Functions": []
    }
  3. 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_ARN is 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"
        }
    }
  4. Invoke it with the test event: aws lambda invoke --function-name greeter --payload fileb://event.json response.json. invoke prints the call’s status. The function’s own response lands in response.json.

    {
        "StatusCode": 200,
        "ExecutedVersion": "$LATEST"
    }
  5. Read the function’s response: cat response.json; echo. echo adds the newline the response lacks.

    {"message": "Hello, Ada!", "remaining_ms": 9999}
  6. 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"
        }
    }
  7. Upload the second version of the code: aws lambda update-function-code --function-name greeter --zip-file fileb://function-v2.zip. CodeSize and CodeSha256 change, and the greeting stays Hi.

    {
        "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"
        }
    }
  8. Invoke it again: aws lambda invoke --function-name greeter --payload fileb://event.json response.json. The status is the same as in step 4.

    {
        "StatusCode": 200,
        "ExecutedVersion": "$LATEST"
    }
  9. Read the new response: cat response.json; echo. The greeting is now Hi, and the second version of the code adds "version": 2.

    {"message": "Hi, Ada!", "remaining_ms": 10000, "version": 2}
  10. 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
    }
  11. 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. Run any block in the practice shell to see what real AWS answered.

Each block starts from an empty Region, which is why most of them create the function first, 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:

Version and list

aws --version
aws lambda list-functions
aws lambda list-functions --query 'Functions[].FunctionName' --output text

aws --version names the CLI release. list-functions lists every function in the Region. --query filters the answer on your machine with a JMESPath expression, and --output text prints bare values instead of JSON (filter output), so 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

Every command that names a function that does not exist fails with ResourceNotFoundException, 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

A second create-function with the same name fails with ResourceConflictException, because names are unique per Region. get-function with --query reads one setting, such as the state, runtime, or greeting; asking for several prints them on one line, separated by tabs. list-functions with --query prints just the names, or names and runtimes.

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

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

update-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: Unable to locate credentials

The CLI found no AWS credentials. Run aws configure, or aws sso login for an SSO profile, confirm with aws sts get-caller-identity, then run the command again.

Handler 'lambda_handler' missing on module 'app' (Runtime.HandlerNotFound)

The --handler value does not match the function name in your code. app.lambda_handler looks for lambda_handler in app.py; fix it with aws lambda update-function-configuration --function-name greeter --handler app.handler, or rename the function in the code and upload it again with update-function-code.

Status: timeout on the REPORT line in CloudWatch Logs

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

Lambda runs the code you last uploaded, not the file on your disk. Zip it again and run aws lambda update-function-code --function-name greeter --zip-file fileb://function.zip.

Structuring a Real Function

AWS’s own guidance and the failure modes above add up to 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 (Python code best practices).
  • Prefer configuration over code. Add event filtering to an event source mapping so the function only receives the events it acts on (event filtering). Don’t have one function call another and wait; chain the steps with AWS Step Functions or durable functions instead (event-driven anti-patterns).
  • Validate input. Events come from other systems; check the shape before you trust it.
  • Test the logic first. A handler split from its logic lets most tests run on your machine in milliseconds, without deploying anything.

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 fill the gap stateless Event Functions leave for work that waits on slow external calls or spans many steps, without a separate orchestrator.
  • 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

🔊 Audio

📚 Books

🌐 Online

References