DEV Community

Boris Barac
Boris Barac

Posted on

A Practical Guide to Local Lambda Debugging: Using SAM CLI with Terraform and VS Code

A practical guide to debugging Lambda functions locally using SAM CLI and VS Code — no more deploying just to test a print() statement.

What You'll Learn

  • How to invoke a Lambda function locally from the command line
  • How to spin up a local API that mirrors your API Gateway
  • How to attach a debugger in VS Code and step through your code
  • How to hit a deployed Lambda from your machine

The Project

Github repo

This repo deploys a simple serverless stack:

Resource Purpose
Lambda Function Python function that writes a timestamp to S3
S3 Bucket Stores timestamp files
API Gateway (HTTP) Exposes the Lambda at GET /timestamp

All infrastructure is defined in Terraform. The Lambda source lives in lambda_function/lambda_function.py:

import time
import boto3
import os


def lambda_handler(event, context):
    """
    AWS Lambda function that stores current timestamp in S3.
    """
    current_timestamp = int(time.time())

    s3 = boto3.client("s3")
    bucket_name = os.environ["S3_BUCKET_NAME"]
    file_name = f"timestamp-{current_timestamp}.txt"

    s3.put_object(Bucket=bucket_name, Key=file_name, Body=str(current_timestamp))

    return {
        "statusCode": 200,
        "body": {
            "message": "Timestamp saved successfully",
            "timestamp": current_timestamp,
            "file": file_name,
        },
    }
Enter fullscreen mode Exit fullscreen mode

Prerequisites

Make sure these are installed and working before you start: Terraform, AWS SAM CLI, Docker, Python, VSCode. Verify everything is in place:

terraform version
sam --version
docker ps
python3 --version
Enter fullscreen mode Exit fullscreen mode

Note: docker ps should run without error. If Docker isn't running, SAM CLI will fail when trying to build or invoke locally.

Project File Map

Here's what matters for debugging:

.
├── main.tf                         # All Terraform infra (Lambda, S3, API Gateway, IAM)
├── lambda_function/
│   ├── lambda_function.py          # Your Lambda handler
│   └── requirements.txt            # Python dependencies
├── ev.json                         # Sample event payload for local invoke
├── .vscode/
│   └── launch.json                 # VS Code debug configurations
└── cli_helper.txt                  # Quick reference CLI commands
Enter fullscreen mode Exit fullscreen mode

Method 1: Debugging with SAM CLI (Command Line)

SAM CLI can read your Terraform project directly using the --hook-name terraform flag. No SAM template needed.

Step 1 — Build

SAM needs to prepare a local build of your function (it resolves dependencies and packages the code):

sam build --hook-name terraform
Enter fullscreen mode Exit fullscreen mode

You'll see output like:

Building codeuri: /Users/you/tf-sam-play-master/lambda_function runtime: python3.13
metadata: {} functions: my_lambda
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource

Build Succeeded

Built Artifacts  : .aws-sam-iacs/build
Built Template   : .aws-sam-iacs/build/template.yaml
Enter fullscreen mode Exit fullscreen mode

Tip: Re-run this every time you change your Python code or dependencies.

Step 2 — Invoke Locally with a Custom Event

The repo includes ev.json — a sample S3 event payload. You can use it to simulate an invocation:

sam local invoke --event ./ev.json --hook-name terraform --debug
Enter fullscreen mode Exit fullscreen mode
  • --event ./ev.json — feeds the event payload to your handler
  • --hook-name terraform — tells SAM to read from Terraform, not a SAM template
  • --debug — prints verbose logs (Docker commands, environment, etc.)

Expected output:

{
  "statusCode": 200,
  "body": {
    "message": "Timestamp saved successfully",
    "timestamp": 1745232000,
    "file": "timestamp-1745232000.txt"
  }
}
Enter fullscreen mode Exit fullscreen mode

Important: The s3 put_object call will fail locally unless you have real AWS credentials that can reach the S3 bucket. For offline-only testing, you'd mock boto3 (covered in the Troubleshooting section).

Step 3 — Run a Local API

If you want to test through the API Gateway path (not just direct invoke):

sam local start-api --hook-name terraform --debug
Enter fullscreen mode Exit fullscreen mode

SAM starts a local HTTP server. You'll see:

Mounting my_lambda at http://127.0.0.1:3000/{path+}
You can now browse to the above endpoints to invoke your functions.
Running on http://127.0.0.1:3000 (Press CTRL+C to quit)
Enter fullscreen mode Exit fullscreen mode

Now hit it from another terminal:

curl http://127.0.0.1:3000/timestamp
Enter fullscreen mode Exit fullscreen mode

This is useful for verifying that the API Gateway integration is wired correctly and that event routing works.

Step 4 — Remote Invoke (Hit the Deployed Lambda)

Once you've deployed with terraform apply, you can invoke the live Lambda from your machine:

sam remote invoke my_lambda --stack-name terraform-2025 --event '{"test": "data"}'
Enter fullscreen mode Exit fullscreen mode

This sends a real event to the deployed function in AWS. Useful for comparing local vs. remote behavior.

Method 2: Debugging in VS Code

Step 1 — Install the AWS Toolkit Extension

  • Open VS Code
  • Go to Extensions (Cmd+Shift+X)
  • Search for AWS Toolkit and install it

This extension understands SAM projects and provides the aws-sam debug type.

Step 2 — Understand the Launch Config

The repo ships with .vscode/launch.json containing three configurations:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "aws-sam",
            "request": "direct-invoke",
            "name": "lambda_function:lambda_function.lambda_handler (python3.13)",
            "invokeTarget": {
                "target": "code",
                "projectRoot": "${workspaceFolder}/lambda_function",
                "lambdaHandler": "lambda_function.lambda_handler"
            },
            "lambda": {
                "runtime": "python3.13",
                "payload": {
                    "json": {
                        "fake": "event"
                    }
                },
                "environmentVariables": {}
            }
        },
        {
            "name": "Attach to SAM Local",
            "type": "debugpy",
            "request": "attach",
            "listen": {
                "host": "127.0.0.1",
                "port": 3000
            },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}/lambda_function/lambda_function",
                    "remoteRoot": "/timestamp "
                }
            ]
        },
        {
            "name": "Python: Current File",
            "type": "debugpy",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal",
            "justMyCode": true
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Here's what each one does:

Config Name When to Use
Direct Invoke (aws-sam) Quick one-click invoke. Builds and runs in Docker.
Attach to SAM Local (debugpy) Start API from CLI, then attach debugger. Full breakpoints.
Python: Current File Run the Python file directly (no Lambda context).

Step 3 — Set a Breakpoint and Debug

Option A — Direct Invoke (quickest)

  1. Open lambda_function/lambda_function.py
  2. Click the gutter to the left of line 11 (current_timestamp = ...) to set a breakpoint (red dot)
  3. Press F5 or go to Run → Start Debugging
  4. Select "lambda_function:lambda_function.lambda_handler (python3.13)"
  5. VS Code builds a Docker container, invokes the handler, and pauses at your breakpoint

You can now inspect event, context, and all local variables in the Variables panel.

Option B — Attach to a Running Local API

This is the most realistic debugging setup because it mirrors the full request flow:

Terminal 1 — Start SAM's local API with the debug port open:

sam local start-api --hook-name terraform --debug-port 3000
Enter fullscreen mode Exit fullscreen mode

VS Code — Attach the debugger:

  1. Set your breakpoints in lambda_function.py
  2. Press F5 and select "Attach to SAM Local"
  3. The debugger connects and waits

Terminal 2 — Trigger a request:

curl http://127.0.0.1:3000/timestamp
Enter fullscreen mode Exit fullscreen mode

VS Code pauses execution at your breakpoints. Step through the handler line by line.

Step 4 — Customize the Event Payload

In the Direct Invoke config, change the payload to match a real event:

"payload": {
    "json": {
        "queryStringParameters": {
            "key": "value"
        },
        "pathParameters": {},
        "body": null
    }
}
Enter fullscreen mode Exit fullscreen mode

Or point it to a file:

"payload": {
    "jsonPath": "${workspaceFolder}/ev.json"
}
Enter fullscreen mode Exit fullscreen mode

Working with Events

Generate Sample Events with SAM

Don't hand-write event payloads. SAM can generate them for you:

# Generate an S3 Put event
sam local generate-event s3 put --bucket my-bucket --key my-key

# Generate an API Gateway HTTP API event (proxy integration)
sam local generate-event apigateway http-api-proxy --method GET --path timestamp

# Save to a file
sam local generate-event s3 put > my-event.json
Enter fullscreen mode Exit fullscreen mode

Then use it:

sam local invoke --event my-event.json --hook-name terraform --debug
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
boris9027 profile image
Boris Barac