Write elegant and testable AWS Lambdas using .NET Core and Microsoft's Dependency Injection + Configuration extensions. No longer do you need to write your own boilerplate to achieve this - just write your Lambda code and service configuration!
Lambdajection aims to:
- Facilitate rapid and secure lambda development using C#.
- Increase Lambda testability by enabling use of Microsoft's dependency injection, configuration and logging extensions.
- Achieve faster startup times (and lower costs) by using compile-time code generation rather than reflection wherever possible.
- Optimize for container reuse by reusing service collections and services between invocations.
- Be highly extensible and configurable.
Community contribution/pull requests are welcome and encouraged! See the contributing guide for instructions. Report issues on JIRA - you can report anonymously or include github username/contact info in the ticket summary.
- 1. Installation
- 2. Packages
- 3. Templates
- 4. Usage
- 5. Examples
- 6. Acknowledgments
- 7. Donations
- 8. Contributing
- 9. Security
- 10. License
See the packages section for a list of available packages.
dotnet add package Lambdajection
ℹ Templates will be available starting in v0.5.0-beta1
dotnet new -i Lambdajection.Templates
Development builds are generated for PRs and uploaded to GitHub Packages. To use them, update the user config file for nuget (varies by OS - see this article) and add this to the packageSourceCredentials section of that file:
<github>
<add key="Username" value="USERNAME" />
<add key="ClearTextPassword" value="TOKEN" />
</github>Replace USERNAME with your username, and TOKEN with a personal access token from GitHub that has permissions to read packages. It is important that this goes in the user config file rather than the project one, so that you do not accidentally leak your personal access token to the world.
Then, in your project's nuget.config file, add the following to your packageSources section:
<add key="github" value="https://nuget.pkg.github.com/cythral/index.json" />Finally, you may use development builds by adding the package and version to your .csproj, for instance:
<PackageReference Include="Lambdajection" Version="0.3.0-gc2ca768d3f" />| Lambdajection | |
| Lambdajection.Attributes | |
| Lambdajection.Core | |
| Lambdajection.Generator | |
| Lambdajection.Encryption | |
| Lambdajection.Templates |
ℹ Templates will be available starting in v0.5.0-beta1
Lambdajection .NET templates are available for your convenience. Run dotnet new [template-name] --help to see a list of available options for each template.
dotnet new lambdajection
Creates a new C# project with Lambdajection installed, plus boilerplate for a Lambda Handler and Startup class.
dotnet new lambdajection-options
Creates a new Options class to be injected into your Lambda as an IOption<>.
Writing the lambda is simple: Just define a public, partial class that contains a Handle method and annotate the class with the Lambda attribute. The Lambda attribute requires that you specify a startup class - more on this in the next step. You are not limited to an request/input parameter of type object - this can be any serializable value or reference type. Same goes for the return value, however the return value must be enclosed in a Task.
using System.Threading.Tasks;
using Amazon.Lambda.Core;
using Lambdajection.Attributes;
namespace Your.Namespace
{
[Lambda(typeof(Startup))]
public partial class YourLambda
{
private IRegisteredService yourService;
public YourLambda(IRegisteredService yourService)
{
this.yourService = yourService;
}
public Task<object> Handle(object request, ILambdaContext context)
{
return new {
foo = request
};
}
}
}If your Lambda targets the netcoreapp3.1 framework, then by default, the serializer is set to DefaultLambdaJsonSerializer from the Amazon.Lambda.Serialization.SystemTextJson package. With any TFM, you may specify the serializer you want to use by setting the Lambda attribute's Serializer argument:
[Lambda(typeof(Startup), Serializer = typeof(Serializer))]
public partial class Lambda
{
...See a full example of serializer customization.
The startup class configures services that are injected into the Lambda's IoC container / service collection.
- Use the ConfigureServices method to add services to your lambda.
- Use
IServiceCollection.UseAwsService<IAmazonService>to inject AWS Clients and Client Factories into the lambda. See the example here.
- Use
- Optionally use the ConfigureLogging method to configure additional log settings.
using System;
using Lambdajection.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Your.Namespace
{
public class Startup : ILambdaStartup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
this.Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// configure injected services here
services.AddScoped<IRegisteredService, DefaultRegisteredService>();
// Add AWS Services by their interface name - the default
services.UseAwsService<IAmazonService>();
}
public void ConfigureLogging(ILoggingBuilder logging)
{
// this method is optional
// logging comes preconfigured to log to the console
}
}
}By default, configuration is environment variables-based. If you would like to use a file-based or other configuration scheme, you may supply a custom configuration factory to the Lambda attribute:
[Lambda(typeof(Startup), ConfigFactory = typeof(ConfigFactory))]
public partial class Lambda
{
...A custom config factory might look like the following:
using System.IO;
using Lambdajection.Core;
using Microsoft.Extensions.Configuration;
namespace Your.Namespace
{
public class ConfigFactory : ILambdaConfigFactory
{
public IConfigurationRoot Create()
{
return new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.Build();
}
}
}See the full example here.
You can add an options section by defining a class for that section, and annotating it with the LambdaOptions attribute. If any options are in encrypted form, add the Encrypted attribute to that property. When the options are requested, the IDecryptionService singleton in the container will be used to decrypt those properties. The default decryption service uses KMS to decrypt values.
- The Encrypted attribute, IDecryptionService and DefaultDecryptionService are all provided by the
Lambdajection.Encryptionpackage. - Option classes must be in the same assembly as your lambda.
- You can replace the default decryption service with your own
IDecryptionServiceby injecting it as a singleton in your Startup class'ConfigureServicesmethod. - See the example for using Encrypted properties.
using Lambdajection.Encryption;
namespace Your.Namespace
{
[LambdaOptions(typeof(LambdaHandler), "SectionName")]
public class ExampleOptions
{
public string ExampleValue { get; set; }
[Encrypted]
public string ExampleEncryptedValue { get; set; }
}
}Initialization services can be used to initialize data or perform some task before the lambda is run. Initialization services should implement ILambdaInitializationService and be injected into the container as singletons at startup.
ℹ Disposers will be fully supported starting in v0.5.0-beta1
Disposers can be used to run object finalization and mark for garbage collection. Lambdajection supports Lambdas that implement either IDisposable, IAsyncDisposable or both. If you implement both, DisposeAsync will be preferred.
When configuring your lambda on AWS, the method name you'll want to use will be Run (NOT Handle). For context, Run is a static method generated on your class during compilation. It takes care of setting up the IoC container, if it hasn't been setup already.
So, going off the example above, the handler scheme would look like this:
Your.Assembly.Name::Your.Namespace.YourLambda::Run
- Injecting and using AWS Services + Factories
- Automatic decryption of encrypted options
- Using a custom serializer
- Using a custom config factory
- CodeGeneration.Roslyn - Used for compile-time code generation using attributes.
- Simple Lambda Dependency Injection in AWS Lambda .NET Core by Gary Woodfine - primary inspiration for this project.
If you use this project and find it useful, please consider donating. Donations are accepted via Github Sponsors and PayPal.
Issues and feature requests may be reported anonymously on JIRA Cloud. Pull requests are always welcome! See the contributing guide for more information.
Security issues can be reported on our JIRA. See our security policy for more information.
This project is licensed under the MIT License.