Using Serverless Framework Environment Variables .net core Lambda

gary_woodfine

Gary Woodfine

Posted on April 27, 2019

Using Serverless Framework Environment Variables .net core Lambda

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

Enter fullscreen mode Exit fullscreen mode

You can then simply reference and get the value from the environment variable in your code as follows

const tableName = process.env.TABLE_NAME;

Enter fullscreen mode Exit fullscreen mode

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

GitHub logo 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

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.

Donate via PayPal

threenine logo






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

Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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;
    }
}

Enter fullscreen mode Exit fullscreen mode

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"));
    }
}


Enter fullscreen mode Exit fullscreen mode

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; }
    }
}

Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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;

                }
    }
}



Enter fullscreen mode Exit fullscreen mode

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"

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
gary_woodfine
Gary Woodfine

Posted on April 27, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related