Skip to content
AWS aws serverless 5 min read

Creating Your First Lambda (Step by Step)

AWS Lambda lets you run code without managing servers — you upload a small piece of code (a “function”), and AWS runs it for you whenever it’s triggered. This page walks you through building your very first function end to end: creating it in the AWS Management Console (the web dashboard you log into), testing it with a sample event, and then doing the same thing from your terminal with the AWS CLI (Command Line Interface, the tool that lets you control AWS by typing commands). By the end you’ll have a working function and you’ll know the one permission mistake that trips up almost everyone on day one.

Before you start: the three things every Lambda needs

Every Lambda function is defined by three core pieces. Understand these and the rest is easy.

PieceWhat it meansExample
RuntimeThe language and version your code runs onpython3.13, nodejs22.x, java21
HandlerThe exact function AWS calls when your Lambda runslambda_function.lambda_handler
Execution roleAn IAM role (a set of AWS permissions) the function uses while it runsarn:aws:iam::111122223333:role/my-lambda-role

The handler format is file.function. So lambda_function.lambda_handler means “in the file lambda_function.py, call the function named lambda_handler.” IAM stands for Identity and Access Management — it’s how AWS decides who can do what.

When to use Lambda (and when not): Use Lambda for short, event-driven work — responding to an API request, processing a file upload, running a scheduled job. Avoid it for tasks that run longer than 15 minutes (Lambda’s hard timeout) or that need to keep state in memory between calls. For those, use a container service like ECS or Fargate instead.

Creating a function in the Console

The Console is the quickest way to get something running and see it work.

  1. Sign in to the AWS Management Console and open the Lambda service.
  2. Click Create function.
  3. Choose Author from scratch.
  4. Enter a Function name, for example hello-world.
  5. Pick a Runtime — choose Python 3.13 for this walkthrough.
  6. Under Permissions, leave Create a new role with basic Lambda permissions selected. This automatically creates an execution role that can write logs (more on that below).
  7. Click Create function.

AWS drops you into the function’s page with a built-in code editor and a starter handler. Replace the code with this:

import json

def lambda_handler(event, context):
    name = event.get("name", "world")
    return {
        "statusCode": 200,
        "body": json.dumps({"message": f"Hello, {name}!"})
    }
  1. Click Deploy to save your changes.

The event is the input data your function receives. The context carries runtime info (like how much time is left). Returning a dictionary is all you need for a simple test.

Testing with a sample event

A “test event” is a fake input you send to your function to see how it behaves — no real trigger needed.

  1. On the function page, click the Test tab.
  2. Under Event JSON, paste a sample event:
{
  "name": "DevCraftly"
}
  1. Give the test event a name like basic-test and click Test.

You’ll see an execution result panel showing the response, duration, and memory used.

Output:

Status: Succeeded
Response:
{
  "statusCode": 200,
  "body": "{\"message\": \"Hello, DevCraftly!\"}"
}

Duration: 1.42 ms   Billed Duration: 2 ms   Memory Size: 128 MB   Max Memory Used: 38 MB

The gotcha: logs and the execution role

Here’s the trap that catches everyone. A Lambda function will happily run even if its execution role cannot write logs. The code executes, but you see nothing in CloudWatch Logs (AWS’s logging service), so when something fails you’re flying blind.

If you let the Console create the role for you (step 6 above), it already attaches the right policy. But when you create a role yourself — or reuse an old one — verify it has AWSLambdaBasicExecutionRole, which grants exactly these three permissions: logs:CreateLogGroup, logs:CreateLogStream, and logs:PutLogEvents.

Check the attached policies from the CLI:

aws iam list-attached-role-policies --role-name my-lambda-role

Output:

{
    "AttachedPolicies": [
        {
            "PolicyName": "AWSLambdaBasicExecutionRole",
            "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        }
    ]
}

If it’s missing, attach it:

aws iam attach-role-policy \
  --role-name my-lambda-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

First thing to check when “it ran but I don’t know why it failed”: look at CloudWatch Logs. If there are no log streams at all, the execution role is almost certainly missing the logs permissions above.

Deploying via the AWS CLI

Once you’re comfortable, the CLI lets you create and update functions repeatably — which is how real teams do it. You package your code into a .zip file and hand it to Lambda.

First, zip your code. The zip must contain the file at its root (not inside a folder):

zip function.zip lambda_function.py

Then create the function. You need the ARN (Amazon Resource Name — a unique ID for an AWS resource) of an execution role:

aws lambda create-function \
  --function-name hello-world \
  --runtime python3.13 \
  --handler lambda_function.lambda_handler \
  --role arn:aws:iam::111122223333:role/my-lambda-role \
  --zip-file fileb://function.zip

Output:

{
    "FunctionName": "hello-world",
    "FunctionArn": "arn:aws:lambda:us-east-1:111122223333:function:hello-world",
    "Runtime": "python3.13",
    "Handler": "lambda_function.lambda_handler",
    "State": "Pending",
    "LastUpdateStatus": "InProgress"
}

Note the fileb:// prefix — it tells the CLI to read the file as raw binary, which is required for zip uploads.

Updating the code

When you change your code, re-zip and push just the code (no need to recreate the function):

zip function.zip lambda_function.py
aws lambda update-function-code \
  --function-name hello-world \
  --zip-file fileb://function.zip

Invoking it from the CLI

aws lambda invoke \
  --function-name hello-world \
  --payload '{"name": "CLI"}' \
  --cli-binary-format raw-in-base64-out \
  response.json

Output:

{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

The actual response body is written to response.json. The --cli-binary-format raw-in-base64-out flag lets you pass plain JSON as the payload in CLI v2.

Cost note: Lambda’s free tier includes 1 million requests and 400,000 GB-seconds of compute per month — enough to run small functions essentially for free. Beyond that, this 128 MB function billed at 2 ms per call costs roughly $0.0000000067 per invocation, so a million calls is well under a cent in compute.

Best Practices

  • Let the Console create the execution role for your first function — it wires up CloudWatch Logs permissions automatically and saves you the most common headache.
  • Always check CloudWatch Logs first when debugging; if there are no log streams, fix the role before anything else.
  • Keep deployment zips small — only include your code and dependencies you actually use, since smaller packages mean faster cold starts.
  • Use update-function-code for code changes and update-function-configuration for settings like memory or timeout — don’t delete and recreate functions.
  • Pin a specific runtime version (for example python3.13) so an upstream change never surprises your function.
  • Test with realistic sample events that match what your real trigger will send, not just empty payloads.
Last updated June 15, 2026
Was this helpful?