In previous posts, have explored getting started with Serverless Framework and how to implement Simple Dependency Injection and then we explored how to implement more advanced dependency injection concepts relating to AWS Lambda.
In each of these posts, we explored different levels of interactivity and how the Serverless Framework helps you develop and deploy your AWS Lambda functions and .net core and C#. In this post, we are going to delve a little deeper again into one specific area of how you can create Environment Variables with the Serverless Framework and share them with the .net core executable.
This is a useful strategy to learn as it helps you to create easily configurable and maintainable infrastructure as code and helps to element those Magic Strings from your code base.
We are going to make use of the serverless.yml
to create our environment variables so if you not all that familiar with it then I recommend reading the high-level serverless.yml reference but I will explain the features we’re going to use as I go through.
Environment Variables in Serverless Framework
The Environment tag in the Serverless Framework YAML file enables you to create service-wide environment variables which you can easily make use of in other programming languages, for instance in nodejs based lambda functions you could simply create an environment variable in your serverless.yml
as follows
Environment:
Variables:
TABLE_NAME: SomeTableName
You can then simply reference and get the value from the environment variable in your code as follows
const tableName = process.env.TABLE_NAME;
This is because of one of the many quirks of the JavaScript that make it both a pleasure and pain to work with in equal amounts and also because the process object is a global that provides information about, and control over, the current Node.js process
In .net core however there is a little bit additional work required but, in my opinion at least, is worth it when it comes to managing variables etc.
We will be building on the code base I introduced in how to implement Simple Dependency Injection the branch we’ll be using is EnvironmentVariables
garywoodfine / HelloConfiguration
AWS Lambda and .net core dependency injection
Hello Configuration
Source Code to support a series of blog posts to detailing how to use Dotnet Core Configuration and Dependency Injection in AWS Dotnet core lambdas
- Simple Dependency Injection In AWS Lambda
- Serverless AWS Lambda Dependency Injection
- Using Serverless Framework Environment Variables .net core Lambda
Donate
Hello Configuration is a FREE tutorial developed and supported by Threenine.co.uk
If you would like to make a donation to keep the developers stocked with coffee and pizza's would be greatly appreciated.
How to use Environment Variables in .net core
In order to extend our AWS Lambda application to make use of Environment Variables we will need to add an additional reference to our application.
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables –version 2.1.0
Once this is added we can now add an environment variable to our serverless.yml. We’ll create an environment variable called SaySomething and provide some arbitary statement.
service: Hello-Configuration
provider:
name: aws
runtime: dotnetcore2.1
environment:
SaySomething: "Eating coconuts is cool"
package:
individually: true
functions:
hello:
handler: HelloConfiguration::Threenine.ConfigTest.Functions.Speak::Greet
package:
artifact: bin/release/netcoreapp2.1/hello.zip
We can then edit our LambdaConfiguration.cs
to the below
using System.IO;
using Microsoft.Extensions.Configuration;
namespace Threenine.ConfigTest
{
public class LambdaConfiguration : ILambdaConfiguration
{
public static IConfigurationRoot Configuration => new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
IConfigurationRoot ILambdaConfiguration.Configuration => Configuration;
}
}
We can now make use of this in our code as follows, we’ll edit our SpeakService to make use of the of the environment variable.
using Microsoft.Extensions.Options;
namespace Threenine.ConfigTest.Services
{
public class SpeakService : ISpeakService
{
private readonly Greeting _greeting;
public SpeakService(IOptions<Greeting> hello)
{
_greeting = hello.Value;
}
public string Greeting => string.Concat( _greeting.Message, " ", System.Environment.GetEnvironmentVariable("SaySomething"));
}
}
If we build, deploy and invoke our lambda this will work fine. However, I’m sure the clean coders amongst you will be thinking that this is dirty and we have polluted the code base a little.
Adding Environment Variables to Dependency Injection
Next we’ll create a class that we will use to store the environment variable, we will make available to the classes in which will make use of it via dependency injection.
In this case it’s a very simple class which is going t have a property we will make use of later.
namespace Threenine.ConfigTest.Config
{
public class SpeakEnvironment
{
public string SaySomething { get; set; }
}
}
The .net core framework enables us to really easily now configure and bind these variables at runtime using the Environment Variables Configuration Provider , all that we now really do is rename our Environment Variable in our serverless.yaml to include the name of our Class and a double underscore
When working with hierarchical keys in environment variables, a colon separator (:) may not work on all platforms (for example, Bash). A double underscore (__) is supported by all platforms and is replaced by a colon.
service: Hello-Configuration
provider:
name: aws
runtime: dotnetcore2.1
environment:
SpeakEnvironment__SaySomething: "Eating coconuts is cool"
package:
individually: true
functions:
hello:
handler: HelloConfiguration::Threenine.ConfigTest.Functions.Speak::Greet
package:
artifact: bin/release/netcoreapp2.1/hello.zip
Modify the StartUp class to configure the environment variables and wire them up for Dependency Injection we do this on line 20, which is similar to what we did previously.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Threenine.ConfigTest.Config;
using Threenine.ConfigTest.Services;
namespace Threenine.ConfigTest
{
public class StartUp
{
public static IServiceCollection Container => ConfigureServices(LambdaConfiguration.Configuration);
private static IServiceCollection ConfigureServices(IConfigurationRoot root)
{
var services = new ServiceCollection();
services.Configure<Greeting>(options =>
root.GetSection("greeting").Bind(options));
services.AddTransient<ISpeakService, SpeakService>();
services.Configure<SpeakEnvironment>(options => root.GetSection("SpeakEnvironment").Bind(options));
return services;
}
}
}
Additional Configuration Options
The above example works great, however you may have still spotted an additional concern. Perhaps you’re thinking this is great but what if you wanted to have different configuration options for your specific environments. For instance what if you wanted data that is created in your staging environment be redirected to different tables or queues than the ones you use in the Production ?
Fortunately, the Serverless Framework provides a super simple option, a configuration for your serverless.yml. By convention, this is typically implemented by creating an additional env.configs.yml
Typically you’ll use this file to create Environment Specific configuration information or sensitive information. For instance, you may want to store your AWS Account ID or even version or feature information.
This file will most likely not be stored in your Source Code Repository and will probably be managed by your DevOps process. However, in my case I included it in the repo to provide a sample.
I’ll create a simple file to provide a general idea:
feature: <feature_name>
version: 1.0.0.0
region: <aws_region>
accountId: <aws_account_id>
something: "Coconuts are cool to eat"
In our case we created a variable which we name something and provided an arbitrary value. We can now edit our serverless.yml to make use of this file
service: Hello-Configuration
provider:
name: aws
runtime: dotnetcore2.1
environment:
SpeakEnvironment__SaySomething: ${file(env.configs.yml):something}
package:
individually: true
functions:
hello:
handler: HelloConfiguration::Threenine.ConfigTest.Functions.Speak::Greet
package:
artifact: bin/release/netcoreapp2.1/hello.zip
Summary
We now have an easy to configure Lambda and the ability to create environment variables within our Serverless.yaml and consume them in our .net core lambda.
Top comments (0)