This sample shows how to debug .NET Lambda functions using Visual Studio Code.
This section will showcase the usage of this sample, and explain the steps necessary to make .NET Lambda debugging work.
This guide assumes that you have Visual Studio Code installed, with the Docker and C# Dev Kit extensions.
We also expect the following tools being installed:
We need to install the visual studio .NET debugger vsdbg
into a local directory to allow it to be mounted into our Lambda containers.
The VS Code C# extension wiki page uses an installation script for this (see here), and so will we.
Using the following command, we will install the latest version in the linux-x64
version into the ./vsdbg
directory of this folder:
Note
In general, we do not recommend piping the output of curl
directly into a shell process.
For illustration purposes, we will do this in this guide, please download the script manually and check it before running it on your machine.
curl -sSL https://aka.ms/getvsdbgsh | /bin/sh /dev/stdin -v latest -r linux-x64 -l ./vsdbg
Alternatively, you can execute the download-vsdbg.sh
script, performing the exact same action.
This will also be done using the make install
command, in addition to downloading other dependencies.
The docker-compose.yml
file in this repository is already configured to mount the ./vsdbg
directory into all spawned Lambda containers.
Please set your LOCALSTACK_AUTH_TOKEN
and run docker compose up
to start LocalStack.
In our test-stack directory, we prepared a simple CDK stack to deploy an .NET lambda (its code can be found in the lambda-code directory).
This stack contains an API Gatway REST API with a Lambda proxy integration to our .NET lambda.
To deploy it, please go to the test-stack directory.
cdklocal bootstrap
cdklocal deploy --require-approval "never"
This will build the .NET Lambda function and deploy it - and the rest of the stack - on LocalStack.
As seen in the stack definition, we need to build the lambda using the Debug
configuration.
Please add --configuration Debug
to the dotnet lambda
command packaging the Lambda code archive, when trying to enable debugging on existing projects.
This repository contains the a VS Code launch.json
and tasks.json
configuration in the lambda-code/.vscode directory.
This configurations are already set up to attach to running processes, and you can use them by starting VS Code in the lambda-code/ directory.
In the rest of this section we will explain the configuration.
Let us take another look into the important part of the launch.json
file, this time annotated with more context:
"configurations": [
{
"name": "Attach to .NET Lambda",
"type": "coreclr",
"request": "attach",
"sourceFileMap": {
// This mapping is necessary, as the deployment package is built in this folder inside the CDK build container.
"/asset-input": "${workspaceFolder}/src"
},
// This part allows VS Code to communicate with the debugger running in docker
"pipeTransport": {
"pipeCwd": "${workspaceFolder}",
"pipeProgram": "docker",
"quoteArgs": false,
"pipeArgs": [
"exec",
"-i",
"-u",
// We need to execute the debugger as the same user as the Lambda is running - per default this is `sbx_user1051`.
"sbx_user1051",
// Select the target container to debug
"${command:vscode-docker.containers.select}",
],
// Path to the debugger inside the Lambda container, we mount it using the `LAMBDA_DOCKER_FLAGS` in our docker compose file
"debuggerPath": "/vsdbg/vsdbg"
},
// We need to install some dependencies inside the container before we can start the debugging process
"preLaunchTask": "init-container"
}
]
The preLaunchTask
refers to the following task inside our tasks.json
:
{
"label": "init-container",
"command": "docker",
"type": "process",
"args": [
"exec",
// select the target container
"${command:vscode-docker.containers.select}",
"dnf",
"install",
"-y",
// install `xargs` and `ps`
"findutils",
"procps"
]
}
The init-container
task installs two packages into the Lambda container, to allow usage of the xargs
and ps
tools necessary by the process selection of the VS Code .NET debugger.
To allow attaching our debugger to the running .NET process, we need to start the Lambda once, as we can only attach to running processes.
We do this by invoking the API Gateway endpoint (please replace <api_id>
with the APIGateway ID returned by the cdklocal deploy
step):
curl https://<api_id>.execute-api.localhost.localstack.cloud:4566/prod/
Before continuing to debug, let's set a breakpoint in VS Code, so we can actually observe the outcome.
For demonstration purposes, let's choose the Console.WriteLine
statement here:
Once the container is running, we can select the Run and Debug
menu in VS Code, and select the "Attach to .NET Lambda" configuration.
VS Code will now prompt us to select the container twice, we need to choose the same container twice. LocalStack Lambda container names include the function name, which we can use to choose the right container.
After a short installation step, we now select the "dotnet" process, usually marked as such, to attach the debugger.
Another invocation of the API Gateway endpoint should trigger a halt at the selected breakpoint correctly.
There are some limitations with this approach to debugging .NET Lambda functions.
We currently have to select the Lambda function twice, once for the preLaunchTask
, and then again to connect to the container for debugging.
Sadly, VS Code currently does not allow passing inputs to a preLaunchTask
, so we could reuse the selection.
However, you can build your own Lambda images containing those packages, and push them to an internal docker registry.
To enable this, please take a look into the LAMBDA_RUNTIME_IMAGE_MAPPING
configuration, as described in our documentation.
You need to install the findutils
and procps
packages using dnf install -y findutils procps
to enable proper functioning of the debugger.
Per design, we can only attach to running processes with the .NET debugger. This leads to the requirement to execute the Lambda function at least once to attach a debugger.
A future solution could involve something similar to provisioned concurrency, but the node process is currently not started in this case using LocalStack.