Accessing AWS Secrets Manager from .NET Lambda Functions, Part 1 - The Simple Way

Download full source code.

This is the first in a short series on retrieving secrets from AWS Secrets Manager inside Lambda functions.

In this post, you will create a secret, and deploy a Lambda function that will retrieve the secret. That’s all.

In the second post, you will do the same as in the first post, but using a fully async Lambda function.

In the third post, you will retrieve the secret in a Lambda function, but this time the function is connected to your VPC. This requires a VPC endpoint to be created.

In the last post, you will see how to store an AWS RDS SQL Server login in Secrets Manager, retrieve it in a Lambda function that is connected to your VPC, create a connection string to use the database, and make a query to the database.

First things, first, the simplest case - creating a secret, and then retrieving it in a function (I use Lambda function and function interchangeably).

1. Get the tools

See this post for how to get all the tools you need.

2. Create the secret

Create the secret using the following command -

aws secretsmanager create-secret --name my-credentials --secret-string '{\"username\":\"bryan\",\"password\":\"A-COMPLEX-PASSWORD123!\"}'

You will see output that looks like this -

{
    "ARN": "arn:aws:secretsmanager:us-east-1:xxxxxxxx:secret:my-credentials-dwiP2a",
    "Name": "my-credentials",
    "VersionId": "1c26a403-e3e7-4170-9e62-312c3e5b1880"
}

The response includes the ARN, you will use that later. Note that I am using us-east-1, you may be using a different region.

If you are using the Windows command prompt, you need to enclose the secret string with double quotes, instead of single quotes.

3. Create the Lambda function

The AWS dotnet tooling makes it easy to create Lambda functions, run this -

dotnet new lambda.EmptyFunction --name LambdaSecretsManagerSimple

This creates a simple Lambda function based on the templates you installed.

Change to the LambdaSecretsManagerSimple/src/LambdaSecretsManagerSimple directory.

Add the AWSSDK.SecretsManager NuGet package.

dotnet add package AWSSDK.SecretsManager

The below example shows synchronous code, in another post I will show how do this with async code.

Edit Function.cs, adding three using statements -

using Amazon;
using Amazon.SecretsManager;
using Amazon.SecretsManager.Model;

Replace the body of the Function class with the following code -

public class Function
{
    public string FunctionHandler(ILambdaContext context)
    {
        string secretName = "my-credentials";
        string region = "us-east-1"; // You may be using a different region
        return GetSecretValue(secretName, region);
    }

    private string GetSecretValue(string secretName, string region)
    {
        IAmazonSecretsManager client = new AmazonSecretsManagerClient(RegionEndpoint.GetBySystemName(region));

        GetSecretValueRequest request = new GetSecretValueRequest(){
            SecretId = secretName,
            VersionStage = "AWSCURRENT"
        };

        GetSecretValueResponse response = client.GetSecretValueAsync(request).GetAwaiter().GetResult(); // there is an async way to do this, I will write another short post on that
        return response.SecretString;
    }
}

This Lambda function does not take any parameters and returns the secret as a string.

4. Deploy the function

To deploy your application to the Lambda service, run -

dotnet lambda deploy-function LambdaSecretsManagerSimple

You will be asked to select an IAM role, or create a new one, at the bottom of the list will be *** Create new IAM Role ***, type in the associated number.

You will be asked for a role name, enter LambdaSecretsManagerSimpleRole.

After this you will be prompted to select the IAM Policy to attach to the role, choose AWSLambdaBasicExecutionRole, it is number 6 on my list.

After a few seconds, the function will be deployed.

5. Invoking and not working!

To invoke the Lambda function, run -

dotnet lambda invoke-function LambdaSecretsManagerSimple

You will get a long error, but near the top it says -

User: arn:aws:sts::xxxxxxxxxx:assumed-role/LambdaSecretsManagerSimpleRole/LambdaSecretsManagerSimple is not authorized to perform: secretsmanager:GetSecretValue on resource: my-credentials because no identity-based policy allows the secretsmanager:GetSecretValue action)

This tells you that the Lambda function does not have the GetSecretValue permission needed to access the secret.

6. Add permissions to the role

You can add the relevant permissions to the role via the AWS Console, or the command line.

Command line

In the attached zip file there is a policies directory, open the ReadCredentialsFromSecretsManager.json and change the ARN to the one returned when you created the secret in step 2.

The following command will create an inline policy, granting LambdaSecretsManagerSimpleRole access to the secret you created.

aws iam put-role-policy --role-name LambdaSecretsManagerSimpleRole --policy-name ReadCredentialsFromSecretsManager --policy-document file://policies/ReadCredentialsFromSecretsManager.json

Now the Lambda function will be able to access the secret.

For completeness, here is the content of ReadCredentialsFromSecretsManager.json -

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "arn:aws:secretsmanager:us-east-1:xxxxxxxxxx:secret:my-credentials-xxxxxx"
        }
    ]
}

AWS Console (UI)

If you want to do it via the AWS Console, go to the Lambda function, click Configuration, Permissions, then LambdaSecretsManagerSimpleRole.

Accessing the function execution role
Accessing the function execution role

On the next screen, click Add permissions, and then Create inline policy.

Creating the policy via the GUI
Creating the policy via the GUI

For the service select Secrets Manager.

From Actions, expand Read and select GetSecretValue.

In Resources, select Specific, and click Add ARN.

Enter the ARN you got from step 2 and save changes.

At the bottom of the page, click the Review Policy button.

On the next screen, enter a name for the policy.

Click Create Policy.

7. Invoke the function again

Changes to permissions policies can take a few seconds to take effect.

Try invoking the function again -

dotnet lambda invoke-function LambdaSecretsManagerSimple 

The request will succeed and you will see -

Amazon Lambda Tools for .NET Core applications (5.3.0)
Project Home: https://github.com/aws/aws-extensions-for-dotnet-cli, https://github.com/aws/aws-lambda-dotnet

Payload:
"'{\"username\":\"bryan\",\"password\":\"A-COMPLEX-PASSWORD123!\"}'"

There you have it, the simplest example of accessing a secret from AWS Secrets Manager in a Lambda function.

Download full source code.

comments powered by Disqus

Related