Using AWS Secrets Manager with dotnet Core

Jon Vines
3 min readMay 18, 2018

We’re going to take a quick look at storing secrets accessed by our serverless Lambda functions in AWS using the recently (April ’18) announced AWS Secrets Manager.

The Secrets Manager main aim is to help you store, distribute and rotate credentials securely. Secrets come at $0.40 per month and $0.05 per 10,000 subsequent requests to access.

Motivation

We have a number of Lambda functions which utilise a connection to a MongoDB Atlas instance via VPC peering for both read and write use cases. This means that we must somehow pass a connection string into the function to establish the connection to the database. Prior to Secrets Manager, our options were few. Amongst others, we could:

  • Hard-code the connection string in the function code
  • Pass the connection string in via environment variables specified in our Serverless script
  • Host and maintain a HashiCorp Vault instance to store the secret

Each option had some drawbacks for us. The introduction of Secrets Manager has allowed us to store our connection strings securely with no server management. This fits really well with our best efforts to be serverless first.

How about it

Storing secrets in Secrets Manager has been covered in quite a bit of detail in both the announcement and via the docs. I’ll be covering how we accessed these secrets via a C# Lambda function and serverless script.

First things first, you will need to get the Secrets Manager NuGet package. Then we create a class to query Secrets Manager:

Here, we pass in a secret name which is the ID we gave our secret in AWS Secrets Manager. Then we build up the configuration for the client and the request we’ll be placing into Secrets Manager. As the call to Secrets Manager is asynchronous, we wrap this in a Task.Run and synchronously wait for the Result. This result will be our secret, which we can access via the SecretString .

We then make a call to the SecretManager class we’ve created in the constructor of our Lambda function. This will prevent us from having to query the Secrets Manager on every invocation of our function.

Our secret takes the format:

{"ConnectionString":"mongodb+srv://user:<password>@cluster.mongodb.net/test?retryWrites=true"}

Finally, we can see we’re still specificying environment variables in the GetSecretFunction.cs script Environment.GetVariable("SECRET_NAME") . Here we’re still utilising environment variables defined in our deployment process. This means we get the connection string specific to the environment we’re deploying to. We define the environment variable in our serverless.yml script like so:

SECRET_NAME: ${env:${self:provider.stage}secret}

This subsequently pulls an environment variable defined in our BitBucket Pipelines repository.

Final Thoughts

AWS Secrets Manager has been a great addition for us. It has allowed us to centrally store our secrets and pass to all functions that may require it in a very simple way. We’re impressed that we don’t have to worry about servers and that the cost is relatively small when weighed against the alternatives.

Looking forward, we’ll be thinking about how we can incorporate the ability to rotate our keys to provide a further layer of security in our environment.

--

--

Jon Vines

Software Engineer and Team Lead at AO.com. Aspiring DevOps practitioner. Thoughts are my own.