In part 1 we installed dependencies, created the project from a template and generally got to poking around things. Now let’s get on to the less introductory stuff, building it, running it locally and invoking actual lambdas.
Building the project
If you used the project template, you’ll have several files that start with build. It’s a pre-configured copy of Cake Build and it works well (for C# projects at least). If you have the AWS Toolkit for Visual Studio installed you have a second option as well.
- .\build.ps1 –target=package
Run in powershell (or put powershell before the period) with the root of the template as the current directory, this will build both the test & lambda projects, run the tests and if that passes, package everything up in a zip file for easy upload to AWS. - dotnet-lambda package
Does the same thing, minus running the tests and the dependency on powershell. This requires you to have
Running the lambda locally
Now that we’ve got a project compiled and the template.yaml definition we can finally launch a local copy of the lambda. Word of caution though, if you are expecting this to be faster than uploading it to AWS and running it from there then you may be in for a disappointment. With my simplest lambdas, running it locally only had a small performance hit. When I launched my lambda that called other lambdas into it, I saw performance of the lambda jump from 30-40 seconds at worst to 1.5-2.5 minutes. It did help me troubleshoot some errors though as well as do some testing without eating up too much of my limit for the AWS free tier. Ok disclaimer aside let’s get to the actual commands, both of these are run from the directory with the template.yaml.
- Pure lambda:
sam local start-lambda - API Gateway Lambda:
sam local start-api
After a few seconds if it read your template correctly, it should tell you that you can invoke your lambdas and give you a URL. The default has both on local host with the pure lambda running on port 3001 and the api gateway on 3000.
Pointing your project at the local lambda
That last command was underwhelming after the build up, I admit I had a moment once I got there where I said “ok it didn’t fail, but is it actually working?” So now we need to figure out how to repoint a lambda call at the local instance. Let’s start with an basic example of how to create the lambda client object in C# because that’s where this change occurs:
var lambdaConfig = new AmazonLambdaConfig {
RegionEndpoint = RegionEndpoint.USEast1
}
var lambdaClient = new AmazonLambdaClient("access-key","secret",lambdaConfig)
Now I should mention that the AmazonLambdaConfig properties are all read only, hence the syntax to initialize the region. Now let’s change that to point at the local lambda, say a pure lambda:
var lambdaConfig = new AmazonLambdaConfig {
RegionEndpoint = RegionEndpoint.USEast1,
ServiceUrl = "http://127.0.0.1:3001" // That url from the previous command
}
var lambdaClient = new AmazonLambdaClient("access-key","secret",lambdaConfig)
That’s it. That’s the only change to point your lambda at the local instance. If you were using api calls, you’d switch the port to 3000, but still, nothing else required. I personally use compiler directives to turn that line on and off based on project profiles to keep things simple.
Calling a lambda
I suppose we can’t make it this far without showing you how to call a lambda. I’m going to take a moment and share a wrapper function I use in C#, it is for calling pure lambdas, not ones behind an api gateway (though with some reworking the basics will still hold). I do have one additional NuGet package installed for Json serialization (Newtonsoft.Json)
public async Task<T> RunLambdaWithResult<T>(string functionName, object arguments)
{
var response = await _lambdaClient.InvokeAsync(new InvokeRequest
{
FunctionName = functionName,
Payload = JsonConvert.SerializeObject(arguments)
});
using (var streamReader = new StreamReader(response.Payload))
{
return JsonConvert.DeserializeObject<T>(streamReader.ReadToEnd());
}
}
And one of my actual calls to this function looks like:
await _lambdaWrapper.RunLambdaWithResult<BoardStateResponse>("checkBoard",
new BoardStateArguments {Board = ToList()});
The RunLambdaWithResult function flows pretty clearly for the most part, build an InvokeRequest with the function name & argument (as json string), invoke it and wait for the task to finish. The response though uses a MemoryStream as the returning payload, so we have to create a StreamReader to turn the MemoryStream back into something that the Json library can deserialize. From there it returns that to the calling function and it continues on, happily unaware of how it got from the argument to the result.
That about covers the major portions of my work with lambdas, skipping over the false starts, misunderstood instructions and general time wasting black holes I fell into along the way. It is by no means comprehensive, because I stuck with a single language, and avoided a lot of the additional frills beyond a passing mention.
One thought on “Invoking AWS Lambdas”