Demonstrates how to add .NET Core dependency injection and configuration services to an AWS Lambda Function project for .NET Core 2.1.
The idea behind this approach is to leverage the built-in configuration system of .NET Core, which can accept mulitple inputs that can override one another. This allows for use of an appsettings.json file with settings entries that can be overriden by environment variables applied when the Lambda Function is deployed. This allows for values that will be available when debugging the function locally, as well as values that can be set as part of a CICD pipeline.
- Visual Studio 2017 or greater.
- AWS Toolkit for Visual Studio 2017.
- .NET Core 2.1 SDK
-
Open VS 2017 and create a new project.
- Select AWS Lambda Project with Tests.
-
Set project SDK to "Microsoft.NET.Sdk.Web".
-
Add package reference to "Microsoft.AspNetCore.App"
- Version="2.1.5"
-
Add an appsettings.json file to the project.
- Add the following content:
{ "env1": "val1", "env2": "val2", "env3": "val3" }
-
Add an appsettings.Development.json file to the project.
- Add the following content:
{ "env1": "dev-val1", "env2": "dev-val2", "env3": "dev-val3" }
-
Select both JSON files, open the Propeties window of Visual Studio, then set the
BuildAction
property to Content and theCopy to Output Directory
property to Copy always. -
Open the aws-lambda-tools-defaults.json file and add the following:
"environment-variables" : "\"ASPNETCORE_ENVIRONMENT\"=\"Development\";\"env1\"=\"val1\";\"env2\"=\"val2\"",
-
Add an
environmentVariables
property to the launchSettings.json file."environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }
-
Add a
Constants
class to the project.public static class Constants { public static class EnvironmentVariables { public const string AspnetCoreEnvironment = "ASPNETCORE_ENVIRONMENT"; } public static class Environments { public const string Production = "Production"; } }
-
Add
DependencyResolver
class.public class DependencyResolver { public IServiceProvider ServiceProvider { get; } public DependencyResolver(Action<IServiceCollection> registerServices) { // Set up Dependency Injection var services = new ServiceCollection(); registerServices?.Invoke(services); ServiceProvider = services.BuildServiceProvider(); } }
-
Add
ServiceCollectionExtensions
class.public static class ServiceCollectionExtensions { public static IServiceCollection AddConfiguration( this IServiceCollection services, Func<IConfigurationBuilder, IConfiguration> builderFunc) { if (builderFunc == null) throw new ArgumentNullException(nameof(builderFunc)); return services.AddTransient(provider => builderFunc(new ConfigurationBuilder())); } }
-
Add a
ConfigureServices
method to theFunction
class in order to register services with the DI system.private void ConfigureServices(IServiceCollection services) { // Add Configuration to DI system services.AddConfiguration(builder => { // Get ASPNETCORE_ENVIRONMENT var environment = Environment.GetEnvironmentVariable( Constants.EnvironmentVariables.AspnetCoreEnvironment) ?? Constants.Environments.Production; // Build config return builder .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{environment}.json", optional: true) .AddEnvironmentVariables() .Build(); }); }
-
Add a property
IConfiguration
property and two constructors to theFunction
class.// Configuration private IConfiguration Configuration { get; } public Function() { // Set up dependency resolution var resolver = new DependencyResolver(ConfigureServices); // Get Configuration from DI system Configuration = resolver.ServiceProvider.GetService<IConfiguration>(); } // Use this ctor from unit tests that can mock IConfiguration public Function(IConfiguration configuration) { Configuration = configuration; }
-
Flesh out the
FunctionHandler
method to use theIConfiguration
.- Use the input parameter for the key used to retrieve a config setting.
public string FunctionHandler(string input, ILambdaContext context) { // Get config setting using input as a key return Configuration[input] ?? "None"; }
-
Press F5 to launch the Mock Lambda Test Tool and start debugging.
-
Enter
"env1"
into Function Input, and click Execute Function.- A value of
"dev-val1"
should be returned base on the appsettings.Development.json file.
- A value of
-
Add the NuGet package for Moq to the Test project.
-
Add a reference to the NetCoreLambda project.
-
Mock
IConfiguration
(from Microsoft.Extensions.Configuration) to return the expected value.- Invoke the lambda function and confirm config value is returned.
[Fact] public void Function_Should_Return_Config_Variable() { // Mock IConfiguration var expected = "val1"; var mockConfig = new Mock<IConfiguration>(); mockConfig.Setup(p => p[It.IsAny<string>()]).Returns(expected); // Invoke the lambda function and confirm config value is returned var function = new Function(mockConfig.Object); var result = function.FunctionHandler("env1", new TestLambdaContext()); Assert.Equal(expected, result); }
-
Right-click on the project in the Solutions Explorer and select Publish to AWS Lambda.
-
Under Configuration you can change the values for the environment variables, and these will override the values from the appsettings.json file published with the Lambda Function.
Once you have edited your template and code you can deploy your application using the Amazon.Lambda.Tools Global Tool from the command line.
Install Amazon.Lambda.Tools Global Tools if not already installed.
dotnet tool install -g Amazon.Lambda.Tools
If already installed check if new version is available.
dotnet tool update -g Amazon.Lambda.Tools
Execute unit tests
cd "NetCoreLambda/test/NetCoreLambda.Tests"
dotnet test
Deploy function to AWS Lambda
cd "NetCoreLambda/src/NetCoreLambda"
dotnet lambda deploy-function